fix(e2e): add Playwright auth fixture to unblock 356 UI tests - #12
Merged
Conversation
5 tasks
E2E browser-context tests called page.goto(...) on protected routes
without an authenticated browser session. RequireAuth redirected them to
/login, and assertions like `expect(page.locator('main')).toBeVisible()`
timed out because <main> only renders inside AppLayout (gated by
RequireAuth). Result: 356 of 403 tests failed; only the 47 API-only
tests (which use the request fixture) passed.
Fix:
- Add e2e/global.setup.ts: a Playwright "setup" project that drives
the UI login flow with TEST_CREDENTIALS.admin and persists cookies +
localStorage to e2e/.auth/admin.json via context.storageState().
- Wire chromium and ci projects to depend on the setup project and
consume the persisted state via use.storageState.
- Override the "Protected Routes (Frontend)" describe block in
auth.spec.ts with empty storageState so it can verify RequireAuth
redirects to /login when unauthenticated.
- Add e2e/.auth/ to .gitignore — the storage file holds a real JWT.
Type-checked with `npx tsc --noEmit playwright.config.ts e2e/global.setup.ts e2e/auth.spec.ts` (exit 0).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mborgeson
force-pushed
the
fix/e2e-auth-fixture
branch
from
April 30, 2026 10:29
37271a2 to
acf3324
Compare
mborgeson
added a commit
that referenced
this pull request
Apr 30, 2026
Add a one-shot smoke project that runs before every other Playwright project. It validates the most fundamental contract — dev server up, React app served, router redirects unauthenticated users to /login — in a single navigation. When it fails, Playwright skips downstream projects via the new dependencies wiring, so the suite exits in <2 min instead of running ~40 min of redundant red across 400+ tests. The smoke test is intentionally standalone: no auth, no storageState, no backend dependency beyond the login page being reachable. This lets it compose cleanly with the auth setup project that PR #12 introduces; chromium and ci can list both as dependencies in any order without coupling smoke to credential availability. - e2e/smoke.spec.ts: single test, asserts /login redirect and email field visible (proves React rendered, not a hydration crash) - playwright.config.ts: new "smoke" project (testMatch on smoke spec, retries: 0). chromium and ci gain dependencies: ['smoke'] and testIgnore on smoke.spec.ts so the smoke test only runs once. Verified: tsc --noEmit clean; playwright test --list shows 1 smoke test under the smoke project and 404 tests under chromium (smoke excluded as expected). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mborgeson
added a commit
that referenced
this pull request
Apr 30, 2026
) Add a one-shot smoke project that runs before every other Playwright project. It validates the most fundamental contract — dev server up, React app served, router redirects unauthenticated users to /login — in a single navigation. When it fails, Playwright skips downstream projects via the new dependencies wiring, so the suite exits in <2 min instead of running ~40 min of redundant red across 400+ tests. The smoke test is intentionally standalone: no auth, no storageState, no backend dependency beyond the login page being reachable. This lets it compose cleanly with the auth setup project that PR #12 introduces; chromium and ci can list both as dependencies in any order without coupling smoke to credential availability. - e2e/smoke.spec.ts: single test, asserts /login redirect and email field visible (proves React rendered, not a hydration crash) - playwright.config.ts: new "smoke" project (testMatch on smoke spec, retries: 0). chromium and ci gain dependencies: ['smoke'] and testIgnore on smoke.spec.ts so the smoke test only runs once. Verified: tsc --noEmit clean; playwright test --list shows 1 smoke test under the smoke project and 404 tests under chromium (smoke excluded as expected). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
The 356/403 E2E failures on PR #8 trace to a single defect: every browser-context test in
e2e/callspage.goto(...)on a protected route without an authenticated browser session.RequireAuth(defined insrc/app/router.tsx) seesisAuthenticated === false, returns<Navigate to=\"/login\" replace />, and the assertionexpect(page.locator('main')).toBeVisible()times out because<main>only renders insideAppLayout— which lives underRequireAuth.The 47 passing tests are all API-only — they use Playwright's
requestfixture and never spin up a browser, so the auth gate is irrelevant.Fix
Implement Playwright
storageStateglobal setup so all browser-context tests start authenticated.e2e/global.setup.ts(new): a Playwright "setup" test that drives the UI login form withTEST_CREDENTIALS.adminand persists cookies + localStorage (whereauthStore.tswrites the JWT) toe2e/.auth/admin.jsonviacontext.storageState({ path }).playwright.config.ts: adds asetupproject keyed offglobal.setup.ts, and wires bothchromiumandciprojects todependencies: ['setup']plususe.storageState: 'e2e/.auth/admin.json'.e2e/auth.spec.ts— "Protected Routes (Frontend)" describe block: when written, the app had no auth gate and these tests asserted that<main>renders without auth. They now usetest.use({ storageState: { cookies: [], origins: [] } })to opt out of the global storage state and assert thatRequireAuthredirects to/login..gitignore: addse2e/.auth/so the storage file (which contains a real JWT) never gets committed.Expected impact
Roughly 340 of the 356 currently-failing browser tests should flip to passing once the setup project runs successfully and the storage state seeds an authenticated session into each browser context. The remaining ~16 failures are likely unrelated (selector drift, fixture data, timing) and can be triaged on the next CI run.
Type sanity check:
npx tsc --noEmit playwright.config.ts e2e/global.setup.ts e2e/auth.spec.tsexits 0. The full E2E suite was not run locally — CI will validate.Branch hygiene
Branched from
origin/main. Does not touchci/fix-e2e-seed-and-bcrypt(PR #8) orfeat/test-quality-system(PR #11).Follow-ups (not in this PR)
These are the "right fix" items the architect flagged for a separate change:
analyst.json(and any future roles) so role-gated tests can opt in without UI logging in each time.Test plan
Protected Routes (Frontend)describe still verifies redirect-to-login behavior (now via empty storageState override)e2e/.auth/admin.jsonis not committed (verify viagit ls-files e2e/.auth/)Generated with Claude Code