Files
2026-06-18 22:21:38 -04:00

18 KiB
Raw Permalink Blame History

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
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
true
SC-1
SC-2
SC-3
truths artifacts key_links
A `desktop` Playwright project (Desktop Chrome, 1280×720, no hasTouch) exists in playwright.config.ts (SC-1, D-06)
`pnpm --filter @familysync/pwa test:e2e` exits 0 on all three projects: iphone, pixel, desktop (SC-2)
The two mobile-only assertions (BottomTabBar safe-area in-viewport, 56×56 FAB geometry) are skipped on desktop only, not deleted, and still run on iphone/pixel (SC-2, D-01, D-02)
Cross-cutting tests (no-overflow, nav-landmark visible, tap targets ≥44px, populated/empty/error states) run unchanged on desktop (SC-2, D-04)
The desktop gate is blocking — same severity as mobile, no continue-on-error carve-out (SC-3, D-03)
path provides contains
apps/pwa/playwright.config.ts desktop project entry mirroring iphone/pixel name: 'desktop'
path provides contains
apps/pwa/e2e/layout.spec.ts desktop-skip guards on the two mobile-only tests + optional D-04 parity assertion testInfo.project.name === 'desktop'
path provides contains
apps/pwa/e2e/README.md harness docs listing the desktop profile desktop
from to via pattern
apps/pwa/playwright.config.ts devices['Desktop Chrome'] spread into the desktop project use block devices['Desktop Chrome']
from to via pattern
apps/pwa/e2e/layout.spec.ts testInfo.project.name test.skip first-statement guard keyed on the desktop project name test.skip(\s*testInfo.project.name === 'desktop'
Add a third `desktop` Playwright project (Desktop Chrome, 1280×720, no `hasTouch`) to `apps/pwa/playwright.config.ts` and make the existing Phase 7 mobile-authored e2e specs green on that no-touch desktop viewport — so the Phase 8 CI regression gate validates the desktop layout/flows in addition to the `iphone`/`pixel` mobile profiles.

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>

- `npx playwright test --list --project=desktop` resolves the new project (run from apps/pwa). - `pnpm --filter @familysync/pwa test:e2e` exits 0 across iphone + pixel + desktop. - `grep -c "testInfo.project.name === 'desktop'" apps/pwa/e2e/layout.spec.ts` == 3. - Mobile assertions intact: `grep "toBeGreaterThanOrEqual(56)" apps/pwa/e2e/layout.spec.ts` still matches the FAB checks. - No `desktop.spec.ts` exists under `apps/pwa/e2e/`. - `.gitea/workflows/ci.yml` has no `continue-on-error` on the harness job (desktop gate is blocking, D-03). - `apps/pwa/e2e/README.md` documents the desktop profile.

<success_criteria>

  • SC-1: A desktop project (Desktop Chrome, 1280×720, no hasTouch) exists in playwright.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:e2e is green on all three projects.
  • SC-3: The desktop gate is blocking in CI (no continue-on-error), runs automatically via the unchanged test:e2e invocation, 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):

  • desktop Playwright project — new entry in apps/pwa/playwright.config.ts projects array; name: 'desktop', use: { ...devices['Desktop Chrome'], serviceWorkers: 'block' }. Referenceable via --project=desktop.
  • Desktop skip guards in apps/pwa/e2e/layout.spec.ts — three test.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 Event toolbar button height ≥ 44px, guarded so it runs only on the desktop project.
  • Header-comment updateslayout.spec.ts, calendar.spec.ts, lists.spec.ts, and playwright.config.ts jsdoc blocks updated to list all three profiles.
  • README updateapps/pwa/e2e/README.md preamble + run-commands block document the desktop profile and a --project=desktop example.

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>

Create `.planning/phases/14-desktop-e2e-coverage/14-01-SUMMARY.md` when done.