Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
8.4 KiB
Phase 7: Mobile Test Harness - Context
Gathered: 2026-06-10 Status: Ready for planning
## Phase BoundaryDeliver an automated, mobile-emulated, authenticated Playwright harness that drives the FamilySync PWA against the host-side dev stack, so mobile-only layout / tap-target / flow defects are caught automatically rather than only by the operator on real devices. The same specs are the artifact Phase 8 (Gitea CI) runs as its PR UI-regression step.
In scope: mobile-emulated browser driving (@playwright/test, new dev dep in apps/pwa), authenticated via DEV_AUTH_BYPASS, structured to run headlessly in CI against a stack the runner brings up.
Out of scope (stays a human/device gate): real production-service-worker behavior, iOS-Safari standalone-PWA behavior (Home-Screen install, standalone OIDC redirect, iOS push), live event-create against Fastmail (dev-bypass user 1 has no CalDAV credential / calendars). No backend changes.
## Implementation DecisionsAuth & Service Worker (locked by ROADMAP / PITFALLS — not re-discussed)
- D-01: Auth via
DEV_AUTH_BYPASS=trueon the host-side dev stack — never a checked-instorage-state.jsonwith an expiring session cookie (Pitfall 14). No Authelia/OIDC mocking. Dev-bypass resolves to Dev User id 1. - D-02: Playwright context uses
serviceWorkers: 'block'so the PWA'sinjectManifestSW (sw.js,registerType: 'autoUpdate') cannot intercept requests / return stale cached responses (Pitfall 15). Verify the trace shows no SW-sourced responses.
Device Emulation
- D-03: Run a two-profile matrix: iPhone + Pixel — covers both household ecosystems (Apple + Android/Fastmail). The iPhone profile satisfies the hard non-technical-Apple-member UX constraint; Pixel covers Chrome-viewport defects.
- D-04: Use faithful browser engines per profile: iPhone → WebKit, Pixel → Chromium. Adds a WebKit browser to the harness/CI image. (Note: this exceeds the existing global
playwright-cliChromium tooling — the harness brings its own@playwright/testbrowsers.) SW-block + dev-bypass apply to both profiles.
Test Data
- D-05: Hybrid — seed deterministic DB fixtures for populated views and keep explicit empty-state assertions. Dev-bypass user 1 natively has no calendars (calendar/list views render empty, live create 422s), so populated coverage requires seeding.
- D-06: Seeding is deterministic and reset per run (truncate/reset → insert, not insert-if-absent) to guarantee repeatable day-over-day results with no stale state (SC #3). Seed onto the shared calendar (id 10, per prior project memory) + list items so user 1's views render populated.
- D-07: Seeding runs in global-setup against the dev MariaDB (already port-bound on 3306 via
docker-compose.dev.yml); teardown/reset keeps runs idempotent.
Stack Lifecycle / Connection
- D-08: Harness targets a configurable
baseURL(env-driven: operator's vite dev server locally, CI service host in Phase 8) with a readiness gate in global-setup (wait on/healthbefore any spec; mirrors the PITFALLS CI-readiness guidance to avoid flaky ECONNREFUSED). - D-09: Stack bring-up is the caller's responsibility — operator's already-running dev stack locally, compose orchestration in Phase 8 CI. The harness never depends on a pre-running stack; it waits for one. Satisfies SC #4.
- D-10: Optionally use Playwright
webServerfor vite only withreuseExistingServer: !process.env.CI(reuse the operator'spnpm devlocally, start vite fresh in CI). The API + MariaDB + Redis always stay compose-managed —webServercannot own a multi-container stack.
Claude's Discretion
- Assertion approach (D-08-area) — deferred to research. User wants a robust, low-maintenance, host↔CI-portable pattern and expects this is well-documented prior art. Steer: lead with structural / role-based locator assertions + explicit tap-target measurements (computed box ≥ 44px, no horizontal overflow, visibility/position) which are stable across environments. Add
toHaveScreenshotvisual snapshots only if research finds a well-established way to keep them non-flaky across host↔CI rendering (CI-generated baselines + tolerance config); otherwise omit screenshots. The Schedule-X calendar widget makes naive pixel snapshots especially drift-prone — weigh that heavily. - Stack lifecycle (D-08–D-10): user said "you decide" — decisions above are Claude's recommendation; planner may refine the exact env-var name and webServer wiring.
- Spec file location/structure, trace/artifact capture on failure, and npm-script + Makefile wiring were not discussed — planner's discretion (follow existing conventions:
apps/pwa, pnpm filters, Makefile-first per global instructions).
<canonical_refs>
Canonical References
Downstream agents MUST read these before planning or implementing.
Phase scope & requirements
.planning/ROADMAP.md§ "Phase 7: Mobile Test Harness" — goal, success criteria (4), phase-owned pitfalls, dependency notes..planning/REQUIREMENTS.md— TEST-01 (mobile-emulated viewport), TEST-02 (DEV_AUTH_BYPASS auth, dev-build scope, consumed by Phase 8 CI).
Pitfalls this phase owns (MUST read — they lock D-01/D-02)
.planning/research/PITFALLS.md§ "Pitfall 14: Playwright Authed-Mobile Harness Reusing a Stale storage-state" (≈L354) — whyDEV_AUTH_BYPASS, not storage-state..planning/research/PITFALLS.md§ "Pitfall 15: Production Service Worker Intercepting Playwright Requests" (≈L375) —serviceWorkers: 'block', verify trace has no SW-sourced responses..planning/research/PITFALLS.mdquick-reference rows (≈L424–425, L491) and the CI-readiness-wait row (≈L409) — readiness gate before specs.
Codebase conventions
.planning/codebase/TESTING.md— current Vitest setup, test locations, the "E2E not implemented; playwright-cli skill used for smoke tests" gap this phase fills.apps/pwa/vite.config.ts— vite dev-server proxy (/api,/health,/callback→ :3000),injectManifestSW config (the SW that D-02 blocks).docker-compose.dev.yml— dev override exposing MariaDB :3306 / Redis :6379, APIdevtarget. The stack the harness targets.- Project
CLAUDE.md§ "Browser-based verification" — playwright-cli is global Chromium;@playwright/testis NOT yet a repo dep (this phase adds it toapps/pwa).
</canonical_refs>
<code_context>
Existing Code Insights
Reusable Assets
DEV_AUTH_BYPASSalready wired across the API (apps/api/src/auth/devBypass.ts,apps/api/src/index.ts, route handlers) and used in API tests — the harness rides the existing bypass, no new auth code.- Shared calendar id 10 + dev MariaDB on :3306 (per prior project memory
dev-data-user1-no-calendars/dev-stack-bringup) — the seed target. - Existing
apps/pwaVitest config + test conventions to mirror for harness file layout/naming (note: Playwright specs are typically*.spec.ts, distinct from Vitest*.test.tsglobs — keep them separate so runners don't collide).
Established Patterns
- Vite dev server proxies
/api,/health,/callbackto the API on :3000 —baseURLpoints at the vite origin; readiness gate hits proxied/health. - Dev API
devtarget needs its own build (dist can be stale, per prior memory) — relevant when CI brings up the stack.
Integration Points
- Phase 8 (Gitea CI) consumes these specs as its PR UI-regression step against a CI-brought-up dev stack — keep the harness stack-agnostic via
baseURL+ readiness gate (D-08/D-09).
</code_context>
## Specific Ideas- User explicitly wants the assertion strategy to be robust and low-maintenance, grounded in established/documented prior art rather than a bespoke approach — flagged as the primary research question.
- Faithful WebKit-for-iPhone fidelity was a deliberate choice over Chromium-only emulation, accepting the heavier browser image.
Reviewed Todos (not folded)
- "Gitea CI — full regression on PR to main + build/publish Docker image" (
.planning/todos/2026-06-10-gitea-ci-regression-and-docker-publish.md, match score 0.6) — belongs to Phase 8 (Gitea CI), which consumes this harness. Not folded; Phase 7 only produces CI-runnable specs, it does not own the CI pipeline.
Phase: 7-Mobile Test Harness Context gathered: 2026-06-10