Move the e2e suites into the repo (tests/), out of /tmp

The browser harness lived in /tmp/opencode and an overnight reboot
wiped it. It now lives in tests/ and survives reboots, is versioned
and runs with one command.

- tests/lib.mjs: shared helpers (Chromium auto-detect, seeding,
  IndexedDB fixtures, stub/extra instance spawn with process-group
  cleanup, check/finish).
- tests/run-all.mjs + npm run test:e2e [filter]: runs the ten suites
  in order with a summary; suites that need a stub Ollama or VAPID
  keys start their own next start instance on 3208/3209, verify the
  port is free and kill the group on exit. Test scratch in
  tests/.tmp/ (gitignored).
- Suites rebuilt and green against the current build: regression 38,
  mobile 94 (tab matrix, ... sheet, settings sheet, axe), wizard 83,
  native 31 (PWA/offline/share/back/undo/haptics/draft), i18n 13,
  freeze-loading 6, review 15, phase1 36 (backup/sync/usage limits),
  phase2 42 (learning engine/reminders), phase3 21 (homework check).
  379 checks, 10/10 suites in one full run.
- playwright-core + axe-core added as devDependencies; CHROME env or
  the newest ~/.cache/ms-playwright chromium is used.
- README: new decision 45 documenting the harness; decision 44's
  verification note updated.

The older suites' exact sources were lost with the wipe, so the
rebuilt versions reconstruct the same feature areas rather than
being byte-identical.
This commit is contained in:
Martin Dvořák 2026-10-11 15:11:06 +00:00
commit dbd5dfbc58
17 changed files with 2280 additions and 8 deletions

46
tests/README.md Normal file
View file

@ -0,0 +1,46 @@
# Browser suites
End-to-end checks for Freezmo, driven by Playwright against a **running
production build**. Everything lives in this repo (nothing in `/tmp`), so a
reboot cannot wipe the harness.
## Requirements
- The app is running on `http://127.0.0.1:3207` (`systemctl --user start freezmo`).
- A Chromium build. The suites auto-detect the newest Playwright Chromium in
`~/.cache/ms-playwright/`; override with `CHROME=/path/to/chrome`.
- `npm install` (dev deps: `playwright-core`, `axe-core`).
## Running
```bash
npm run build && systemctl --user restart freezmo # fresh build first
npm run test:e2e # every suite, in order
npm run test:e2e -- phase1 # only suites whose name contains "phase1"
```
Each suite prints `PASS`/`FAIL` lines and exits non-zero on failure; the runner
prints a per-suite summary at the end.
## Suites
| Suite | Covers |
| --- | --- |
| `regression.mjs` | Library, deck card + menu, study feed to results, homework entry, desktop shell + settings/health panel. |
| `mobile-test.mjs` | Five-tab bar at 360/390 × EN/CS × dark/light × reduced motion (labels, targets, no hamburger/drawer), the library ⋯ sheet (Import JSON / New folder), the settings sheet (touch targets, key flow, drag-to-close, backdrop), axe contrast audits. |
| `wizard-test.mjs` | Deck 3-step flow and homework flow, flat action bar, in-page mode switch, the loading lock, abort-on-leave. |
| `native-test.mjs` | Manifest/PWA head, offline banner + disabled generate, share target GET + POST, Back-gesture layers, undo toast, tab re-tap, haptics, draft restore. |
| `i18n-test.mjs` | EN/CS dictionary parity, locale switch, Czech desktop shell, Czech phone tab labels and plurals. |
| `freeze-loading-test.mjs` | The generation loading screen appears immediately, hides the action bar, Cancel returns with the notice. |
| `review-test.mjs` | Smart Review loading feedback under a stalled request, the session end-to-end, mistakes, and the health-probe cache. |
| `phase1-test.mjs` | Backup export/import, persistence row, nudge, encrypted sync (enable/join/push/delete + API validation), usage-limit handling (client + a stub Ollama). |
| `phase2-test.mjs` | Answer history + SRS from a real study run, review/mistakes, stats, exam mode, push reminders hidden without VAPID and validated with it. |
| `phase3-test.mjs` | Check my work (API + phone/desktop UI), follow-ups, guide step explain, turn-into-a-study-deck. |
## Extra instances
`phase1`, `phase2` and `phase3` start their own `next start` instance with a
stub Ollama (or VAPID keys) on a spare port (3208/3209). Each suite checks the
port is free first and kills the whole process group on exit, so ports do not
leak between suites. Scratch data for the VAPID test lives in `tests/.tmp/`
(gitignored).