"The model selection in teach it back is kinda broken at least on desktop": the desktop panels were position:absolute children of whatever container held their chip. In chat that is the composer at the bottom of the column, which works, but Teach it back keeps its chips inside a scrolling form card — so the panel opened upward out of the card and its top rows (including the selected model) were cut off. The probe measured top: -66px and no way to reach the first rows by scrolling the list. - New components/chat/PickerPopover.tsx: the desktop surface for both pickers, portalled to <body> and position:fixed, anchored to the opening chip ([data-picker-trigger][aria-expanded="true"]), opening on the side with room and capped to the viewport so the panel scrolls as a whole with its heading and note attached. It closes on an outside press, Escape, resize or a page scroll — but not on scrolling its own list. - ContextPicker and ModelPicker use it on desktop; the phone sheets and their inner list caps are unchanged (the list max-height became a parameter: the sheet caps it, the desktop panel scrolls instead). - teach-test pins it: the panel's rect is fully inside the viewport, elementFromPoint over it lands on the panel, it is not a descendant of the scrolling card, and picking a model updates the chip + freezmo.chat.model.v1 (46 checks). Chat and the phone sheets re-verified unchanged (110 + 128). Version 0.8.3, SW v105. |
||
|---|---|---|
| .. | ||
| activities-test.mjs | ||
| aero-music-test.mjs | ||
| bubble-perf.mjs | ||
| bubbles-test.mjs | ||
| chat-test.mjs | ||
| contrast.mjs | ||
| freeze-loading-test.mjs | ||
| heat-probe.mjs | ||
| i18n-test.mjs | ||
| lib.mjs | ||
| mobile-test.mjs | ||
| model-compare.mjs | ||
| native-test.mjs | ||
| phase1-test.mjs | ||
| phase2-test.mjs | ||
| phase3-test.mjs | ||
| podcast-test.mjs | ||
| providers-test.mjs | ||
| README.md | ||
| regression.mjs | ||
| review-test.mjs | ||
| run-all.mjs | ||
| security-test.mjs | ||
| teach-test.mjs | ||
| wizard-test.mjs | ||
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/(Linux) or~/Library/Caches/ms-playwright/(macOS); override withCHROME=/path/to/chrome. npm install(dev deps:playwright-core,axe-core).
Running
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. |
aero-music-test.mjs |
Frutiger Aero's soundtrack player (the SoundCloud widget API is stubbed): nothing loads in light/dark, a random playlist track starts in Aero, the circle expands into the credit bar, pause/collapse/close wiring, the off choice persisting and Aero re-selecting re-arming it, phone geometry over the tab bar and the chat composer, and the control stepping aside under an immersive overlay. |
bubbles-test.mjs |
Ice-scene bubbles: stacking/layout contract, the open band (a plain clip, no mask) and a 41-phase sweep that no visible orb or bubble ever touches its edges, keyframes only transform+opacity, rise/sway/scale/fade phases, per-breakpoint counts (5/3/2), phone pacing, reduced motion, parking (hidden tab, unfocused window, dialog scrim), and the cost contract (static blobs, no blend-mode grain, no blur on static glass, no hidden blur under Aero's bake). |
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. |
podcast-test.mjs |
Podcast mode against a stub Ollama and a stubbed speech engine: the third wizard segment, notes → a saved episode, the word-wrapped transcript, boundary-driven highlighting (and the timer fallback's contract), pause/resume-from-word, chapter jumps, speed changes that keep playing, the position saved on page-hide and resumed on reopen, the library tab, and the API's 400/stream guards. |
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. |
chat-test.mjs |
Chat: streaming API (validation, allowlist, context travel, history window, thinking separation, usage-limit mapping), attachment extraction, entry points, streaming/stop, markdown + KaTeX, model/scope pickers, edit/regenerate, offline, reduced motion, plus history (IDB persistence, reload, rename/search/delete/undo, scope+model memory), assistant actions, the export payload and a two-device sync round-trip. |
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).
Diagnostics (not part of the run)
node tests/bubble-perf.mjs [label] measures the ice-scene frame cost with a
CDP performance trace (rAF frame timing, long tasks, main-thread busy time,
style recalcs) on a 4× throttled phone profile and a 2× desktop profile. It
always exits 0; the numbers are for tuning the bubble count/speed.
node tests/heat-probe.mjs [webkit|chromium] (macOS only) measures what an
idle screen actually costs the machine: headed Playwright WebKit and Chromium
on the real display, the browser's CPU, the GPU time of its processes and of
WindowServer, per screen and theme, against an about:blank baseline and with
the scene parked. Install the engines once with
npx playwright-core install webkit chromium and keep the window in front.