| 07-mobile-test-harness |
04 |
test-harness |
| playwright |
| e2e |
| calendar |
| lists |
| populated-state |
| error-state |
| empty-state |
| auth-bypass |
| service-worker |
|
| requires |
provides |
affects |
| apps/pwa/playwright.config.ts (07-01) — iphone/pixel project matrix, serviceWorkers: 'block', globalSetup path |
| apps/pwa/e2e/global-setup.ts (07-02) — /health readiness gate + DB seed (calendar_id=10 'Seeded Test Event', 'E2E Grocery List' for user_id=1) |
| apps/pwa/e2e/layout.spec.ts (07-03) — locator patterns and conventions mirrored |
|
| apps/pwa/e2e/calendar.spec.ts — TEST-01 (populated + error) + TEST-02 (auth-bypass + SW precondition) assertions for /calendar |
| apps/pwa/e2e/lists.spec.ts — TEST-01 (populated + network-simulated empty) assertions for /lists |
| 20 tests total (8 calendar + 12 lists, per profile) — all passing on iphone/WebKit and pixel/Chromium |
|
| Phase 08 CI (both specs run as regression gates) |
|
|
| added |
patterns |
|
|
| page.route('/api/*', fulfill 500) registered BEFORE page.goto — error-state simulation (Pattern 5) |
| page.unroute() immediately after assertion — route mocks scoped to single test (T-07-11) |
| page.locator('.sx-react-calendar-wrapper') — CSS class fallback for widget wrapper with no semantic role |
| getByRole('button', { name: 'Open list: E2E Grocery List' }) — aria-label stable anchor on ListCard |
| page.route('/api/lists', fulfill 200 []) — network-simulated empty state without DB mutation (D-06) |
| page.evaluate(() => navigator.serviceWorker.controller) — runtime SW controller assertion |
|
|
| created |
modified |
| apps/pwa/e2e/calendar.spec.ts |
| apps/pwa/e2e/lists.spec.ts |
|
|
|
| D-04-SCHEDULE-X-LOCATOR: Asserted .sx-react-calendar-wrapper via CSS class (page.locator) since Schedule-X's React adapter emits no semantic role on the outer wrapper div — documented in index.css. No data-testid added to source; the CSS class is stable within @schedule-x/react. |
| D-04-POPULATED-NO-EVENT-CHIP: Populated calendar test asserts grid visible + empty-absent (NOT event chip text) — chip visibility depends on Schedule-X's default view and the seed event date relative to today; date-dependent assertions are exactly the drift the phase avoids (UI-SPEC Rule 6 rationale). |
| D-04-EMPTY-STATE-NETWORK-SIM: Lists empty state simulated via page.route to 200 [] rather than DB mutation — preserves seeded populated state for concurrent test workers and satisfies D-06 deterministic seed / T-07-11 mock isolation. |
| D-04-LISTCARD-ARIA-LABEL: Lists populated test locates card by getByRole('button', { name: 'Open list: E2E Grocery List' }) — ListCard.tsx renders a <button> (not <a>) with that exact aria-label; no link role collision. |
|
| Error-state simulation: register page.route BEFORE page.goto, assert heading+button, then page.unroute |
| Empty-state simulation (no DB mutation): page.route to 200+empty-body BEFORE goto, assert empty UI, then page.unroute |
| SW-block assertion: page.evaluate(() => navigator.serviceWorker?.controller) — null confirms no controlling SW |
| Auth reach: getByRole('navigation', { name: 'Main navigation' }) visible + URL hostname check against external auth host |
|
|
22min |
2026-06-11 |