18 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 14-desktop-e2e-coverage | 01 | execute | 1 |
|
true |
|
|
The real work is the spec-compat pass, not CI plumbing. Phase 8's harness job already runs
pnpm --filter @familysync/pwa test:e2e, which executes every configured project; the new
desktop project is picked up automatically. Desktop Chrome uses the Chromium engine
already installed in CI (playwright install --with-deps webkit chromium), so no browser
install or CI plumbing change is required.
Purpose: Earn a desktop-layout regression gate that is real (blocking, deterministic) without
duplicating the shared cross-cutting assertions (D-01) or weakening the mobile assertions (D-02).
Output: an extended playwright.config.ts, desktop-skip-guarded layout.spec.ts, header-comment
updates across the spec files, and an updated e2e/README.md.
<execution_context> @$HOME/.claude/gsd-core/workflows/execute-plan.md @$HOME/.claude/gsd-core/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md@.planning/phases/14-desktop-e2e-coverage/14-CONTEXT.md @.planning/phases/14-desktop-e2e-coverage/14-PATTERNS.md
@apps/pwa/playwright.config.ts @apps/pwa/e2e/layout.spec.ts @apps/pwa/e2e/calendar.spec.ts @apps/pwa/e2e/lists.spec.ts @apps/pwa/e2e/README.md @apps/pwa/e2e/global-setup.ts
Task 1: Add the `desktop` project to playwright.config.ts (SC-1, D-06) apps/pwa/playwright.config.ts - apps/pwa/playwright.config.ts — the file being modified; mirror the existing `iphone` (lines 36-46) and `pixel` (lines 47-54) project entries exactly. - .planning/phases/14-desktop-e2e-coverage/14-PATTERNS.md §"apps/pwa/playwright.config.ts — add `desktop` project entry" (the exact new-entry shape and the four key invariants). - .planning/phases/14-desktop-e2e-coverage/14-CONTEXT.md D-06 (single Desktop Chrome project, no Desktop WebKit, keep the default 1280×720 viewport). Append a third project object to the `projects` array (after the `pixel` entry, before the closing `]`): `name: 'desktop'` with `use: { ...devices['Desktop Chrome'], serviceWorkers: 'block' }`. Mirror the iphone/pixel shape exactly. Do NOT add a `baseURL` override (inherited from the top-level `use` block, line 29). Do NOT touch the shared `webServer` block (lines 57-64) or `globalSetup`. Keep `serviceWorkers: 'block'` — mandatory on every project per D-02/Pitfall 15. Do NOT add Desktop WebKit (D-06). Keep the `devices['Desktop Chrome']` default viewport (1280×720, no `hasTouch`); no custom width override. Update the file header jsdoc (lines 4-12) so the device-matrix line names all three profiles (iphone / pixel / desktop) and the example run-commands block reflects a desktop example. cd apps/pwa && grep -q "name: 'desktop'" playwright.config.ts && grep -q "devices\['Desktop Chrome'\]" playwright.config.ts && grep -q "serviceWorkers: 'block'" playwright.config.ts && npx playwright test --list --project=desktop >/dev/null 2>&1 && echo OK - `apps/pwa/playwright.config.ts` contains the literal `name: 'desktop'`. - The desktop entry spreads `devices['Desktop Chrome']` and sets `serviceWorkers: 'block'`. - No `baseURL` key appears inside the desktop project's `use` block (it is inherited). - `npx playwright test --list --project=desktop` exits 0 (the project resolves and tests enumerate) — run from apps/pwa. - The file header jsdoc lists three profiles, not two. The `desktop` project exists, resolves via `--project=desktop`, and mirrors the mobile entries with no baseURL/webServer duplication. Task 2: Desktop-skip the two mobile-only layout assertions + add D-04 parity (SC-2, D-01, D-02, D-04, D-05) apps/pwa/e2e/layout.spec.ts - apps/pwa/e2e/layout.spec.ts — the file being modified; note the exact tests at line 62 ("BottomTabBar is fully in-viewport (Rule 3 — safe-area-inset)"), line 91 ("New Event FAB meets 56×56px touch-target minimum (Rule 1)"), and line 130 ("BottomTabBar is fully in-viewport on /lists (Rule 3)"). - apps/pwa/e2e/calendar.spec.ts lines 56-62 — the established inline `test.skip(condition, reason)` first-statement pattern (the live skip reference in this suite). - .planning/phases/14-desktop-e2e-coverage/14-PATTERNS.md §"apps/pwa/e2e/layout.spec.ts — spec-compat pass" (which tests skip, which pass unchanged, the D-04 parity block, and the testInfo signature) + §"Shared Patterns — Project-name conditional skip". - .planning/phases/14-desktop-e2e-coverage/14-CONTEXT.md D-01 (one spec set, no desktop.spec.ts), D-02 (scope/skip, never weaken or delete mobile assertions), D-04 (parity-adapted: desktop toolbar button ≥44px), D-05 (do NOT add "mobile chrome absent on desktop" negative assertions — `New Event` is a shared accessible name). Add a desktop-only skip guard to exactly the two mobile-only tests, plus the FAB test, using the first-statement form `test.skip(testInfo.project.name === 'desktop', '')` with the `testInfo` fixture added as the second body argument (`async ({ page }, testInfo) => {`): (a) the line-62 "BottomTabBar is fully in-viewport (Rule 3 — safe-area-inset)" test — reason: on desktop the `Main navigation` landmark resolves to the DesktopNav sidebar, for which the safe-area-inset bottom-edge assertion is semantically wrong; (b) the line-130 "BottomTabBar is fully in-viewport on /lists (Rule 3)" test — same reason; (c) the line-91 "New Event FAB meets 56×56px" test — reason: on desktop `New Event` resolves to the toolbar button (CalendarShell.tsx:436-457), not the 56×56 FAB. For D-04 parity, add a desktop toolbar-button ≥44px height assertion using `page.getByRole('button', { name: 'New Event' })` and `boundingBox()`; place it so it runs ONLY on desktop (e.g. a sibling test guarded by `test.skip(testInfo.project.name !== 'desktop', ...)`, or a conditional desktop branch) — keep it from running on mobile where that name is the FAB. Do NOT add any negative "mobile chrome absent" assertion (D-05). Do NOT modify the nav-landmark-visible, 44×44 tab-target, PhoneNav-header, settings-button, Rule 2 overflow, or harness self-validation tests — per PATTERNS they pass unchanged on desktop (the landmark resolves to the sidebar, sidebar links have minHeight:44px, the DesktopNav title matches `getByText('FamilySync', { exact: true })`). Do NOT weaken or delete any existing mobile assertion (D-02). Update the file header jsdoc (lines 10-12) to list all three profiles. cd apps/pwa && grep -c "testInfo.project.name === 'desktop'" e2e/layout.spec.ts | grep -qx 3 && grep -q "test.skip(\s*testInfo.project.name !== 'desktop'" e2e/layout.spec.ts && echo OK - `e2e/layout.spec.ts` contains exactly three `test.skip(testInfo.project.name === 'desktop', ...)` guards (the two safe-area tests + the FAB test). - A desktop parity assertion exists that checks `New Event` toolbar button height ≥ 44px and runs only on desktop (guarded by `testInfo.project.name !== 'desktop'` skip or equivalent desktop-only branch). - No existing mobile assertion is removed or weakened: the file still contains the `>= 56` FAB-width/height checks and the `>= 44` tab-target checks (grep for `toBeGreaterThanOrEqual(56)` and `toBeGreaterThanOrEqual(44)` both still present). - No `desktop.spec.ts` file is created (D-01). - No negative "absent on desktop" assertion is added (D-05) — no new `toHaveCount(0)` / `not.toBeVisible()` keyed on the `New Event` name. layout.spec.ts skips exactly the three mobile-geometry tests on desktop, adds a desktop ≥44px parity assertion, preserves every mobile assertion, and stays a single spec set. Task 3: Update spec headers + README, then prove the full suite is green on all three projects (SC-2, SC-3, D-03) apps/pwa/e2e/calendar.spec.ts, apps/pwa/e2e/lists.spec.ts, apps/pwa/e2e/README.md - apps/pwa/e2e/calendar.spec.ts header (lines 13-15) and apps/pwa/e2e/lists.spec.ts header (lines 16-19) — the "Runs on both device profiles" comment blocks to update; per PATTERNS no structural test changes are needed in these files. - apps/pwa/e2e/README.md — preamble line 3 ("mobile-emulated (iPhone 14/WebKit + Pixel 7/Chromium)") and the run-commands block lines 37-47; mirror PATTERNS §"apps/pwa/e2e/README.md — docs update only". - .gitea/workflows/ci.yml lines 241-296 — confirm the harness job runs `pnpm --filter @familysync/pwa test:e2e` (all projects) and installs the `chromium` engine that Desktop Chrome uses; assert no CI plumbing change is required beyond a cosmetic comment. - .planning/phases/14-desktop-e2e-coverage/14-CONTEXT.md D-03 (desktop gate is blocking; fix flaky specs, never downgrade the gate) and 14-PATTERNS.md §calendar.spec.ts / §lists.spec.ts (all tests pass unchanged on desktop). Update the header jsdoc comment block in `calendar.spec.ts` and `lists.spec.ts` to list all three profiles (iphone / pixel / desktop) — comment-only, no test-body changes (PATTERNS confirms both files pass unchanged on desktop). In `e2e/README.md`: update the preamble (line 3) to add "Desktop Chrome (1280×720)" alongside the iPhone/Pixel emulation note, change the full-suite command comment (line 37) to name all three profiles, and add a `--project=desktop` single-profile example. Confirm — by reading `.gitea/workflows/ci.yml` — that the harness job already runs every project via `test:e2e` and installs the `chromium` engine Desktop Chrome uses; the desktop gate is therefore blocking automatically (CI fails the PR if any desktop test fails) with NO continue-on-error carve-out, satisfying D-03. Do NOT add `continue-on-error`. If a desktop test flakes, fix the spec (proper waits/locators), never weaken the assertion or downgrade the gate. Then run the FULL suite locally against all three projects and confirm exit 0; the dev stack must be up with `DEV_AUTH_BYPASS=true` per e2e/README.md before running. set -a; source .env 2>/dev/null; set +a; export DEV_AUTH_BYPASS=true DB_HOST=127.0.0.1 DB_PORT=3306; pnpm --filter @familysync/pwa test:e2e - `pnpm --filter @familysync/pwa test:e2e` exits 0 with all three projects (iphone, pixel, desktop) reported — no failures, no unexpected skips beyond the three desktop-guarded geometry tests + the SW-availability skip. - `apps/pwa/e2e/README.md` line 3 mentions Desktop Chrome, and the run-commands block contains a `--project=desktop` example. - `calendar.spec.ts` and `lists.spec.ts` header blocks each list the `desktop` profile. - `.gitea/workflows/ci.yml` is unchanged except (optionally) the cosmetic step-name comment on line 255 — no `continue-on-error` added, no new browser install needed (chromium already installed for Desktop Chrome). The full e2e suite is green on iphone + pixel + desktop, docs name the desktop profile, and the CI gate blocks on desktop with zero plumbing change.<threat_model>
Trust Boundaries
This phase adds a test-only Playwright desktop project and adjusts existing e2e specs/docs.
It introduces NO new runtime code, NO new request handlers, NO new data flows, and NO new
attack surface in the shipped application. No new trust boundary is created.
| Boundary | Description |
|---|---|
| (none new) | All changes are confined to the e2e test harness (apps/pwa/e2e/*, playwright.config.ts) and docs; nothing in this phase is reachable in the production app. |
STRIDE Threat Register
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|---|---|---|---|---|
| T-14-01 | Elevation of Privilege | desktop Playwright project (serviceWorkers, DEV_AUTH_BYPASS) |
mitigate | The desktop project MUST set serviceWorkers: 'block' (mirrors mobile profiles) and rely ONLY on the test-only DEV_AUTH_BYPASS path that is a no-op when NODE_ENV === 'production' (devBypass.ts) and absent from docker-compose.yml. This phase adds no real-auth bypass and no production-reachable code. |
| T-14-02 | Tampering | npm/pip/cargo installs | accept | No new packages installed. Desktop Chrome uses the already-installed Chromium engine (playwright install --with-deps webkit chromium); no dependency or lockfile change. Package Legitimacy Gate N/A. |
| T-14-SC | Tampering | supply chain | accept | No package-manager install tasks in this plan; no [ASSUMED]/[SUS] packages to gate. |
Honest scope statement: there is no genuine new application attack surface here. The only
test-infra concern worth recording is keeping serviceWorkers: 'block' on the desktop project
and never letting DEV_AUTH_BYPASS leak into a production-reachable path — both already enforced
by existing guards (T-14-01).
</threat_model>
<success_criteria>
- SC-1: A
desktopproject (Desktop Chrome, 1280×720, nohasTouch) exists inplaywright.config.ts. - SC-2: The existing e2e specs pass (or are explicitly, justifiably skipped) on the desktop profile — the two mobile-only geometry assertions are desktop-skipped, everything else runs;
pnpm test:e2eis green on all three projects. - SC-3: The desktop gate is blocking in CI (no
continue-on-error), runs automatically via the unchangedtest:e2einvocation, and the specs are deterministic (D-03). - D-01..D-06 all satisfied: one spec set (D-01), no weakened/deleted mobile assertions (D-02), blocking gate (D-03), parity ≥44px desktop assertion (D-04), no negative "absent on desktop" assertion (D-05), single Desktop Chrome project no WebKit (D-06). </success_criteria>
<artifacts_produced>
Artifacts this phase produces
New symbols / identifiers created by this phase (none are application runtime code — all test-harness):
desktopPlaywright project — new entry inapps/pwa/playwright.config.tsprojectsarray;name: 'desktop',use: { ...devices['Desktop Chrome'], serviceWorkers: 'block' }. Referenceable via--project=desktop.- Desktop skip guards in
apps/pwa/e2e/layout.spec.ts— threetest.skip(testInfo.project.name === 'desktop', '<reason>')first-statement guards on: "BottomTabBar is fully in-viewport (Rule 3 — safe-area-inset)" (line ~62), "BottomTabBar is fully in-viewport on /lists (Rule 3)" (line ~130), "New Event FAB meets 56×56px touch-target minimum (Rule 1)" (line ~91). - Desktop parity assertion (D-04) — a new desktop-only check (test or branch) asserting the
New Eventtoolbar button height ≥ 44px, guarded so it runs only on thedesktopproject. - Header-comment updates —
layout.spec.ts,calendar.spec.ts,lists.spec.ts, andplaywright.config.tsjsdoc blocks updated to list all three profiles. - README update —
apps/pwa/e2e/README.mdpreamble + run-commands block document thedesktopprofile and a--project=desktopexample.
No new helper module (e.g. no isDesktop helper) is introduced — the inline test.skip form is used per PATTERNS.md, consistent with the existing skip pattern. No desktop.spec.ts file is created (D-01).
</artifacts_produced>