module-lifecyclePhase 7 testing-strategy reference — module-level testing strategy and coverage requirements across unit, integration, RLS, and E2E.playwright-testing— E2E test organization, auth fixtures, flake reduction.
1. How to run each test type
Single file or pattern:
2. Environment variables
Local
.env.local (git-ignored) example for RLS and E2E:
2a. E2E conventions (Playwright vs Vitest)
- Playwright runs only
*.spec.ts(and.spec.tsx) intests/e2e/(see playwright.config.tstestMatch). Usenpm run test:e2eornpm run test:e2e:{core}to run these. - Vitest can run
*.test.tsundertests/e2e/(e.g.financial-transactions.test.ts,leave-accruals.test.ts); these are integration-style E2E with Supabase and are not run by Playwright. Run them withnpm test -- tests/e2e/. - Some spec files are in Playwright’s
testIgnore(e.g.fa/budget-scenarios.test.ts,ce/activities.test.ts) and run as Vitest or are documented in the spec–test matrix. - Critical flows (auth, multi-tenant, financial, workflow approvals, leave accruals) are listed in SPEC_TEST_COVERAGE.md § Critical flows.
3. E2E authentication
3.1 Shared auth fixture and setup project
- Fixture: tests/e2e/fixtures/auth.fixture.ts provides
loginAsTestUser(page, role),TEST_USERS, and helpers. Use for per-test or per-spec login when you need a specific role (e.g. staff, manager). - Global setup: tests/e2e/global-setup.auth.ts only creates
tests/e2e/.auth/. It does not log in (so it does not require the app to be up). - Setup project: After the web server is ready, the Playwright project
e2e-setup-authruns tests/e2e/setup-auth.spec.ts, which logs in as admin and saves browser state totests/e2e/.auth/admin.json. Thechromium-authenticatedproject depends on this, so auth runs after the server is reachable. - Authenticated project: Playwright config defines a project
chromium-authenticatedthat usesstorageState: 'tests/e2e/.auth/admin.json'. Specs that usetest.use({ project: 'chromium-authenticated' })start already logged in as admin and do not need to call login in each test (unless switching to another role).
3.2 When to use which
- Need admin (or default) for whole spec: Use
test.use({ project: 'chromium-authenticated' })in the describe. NobeforeEachlogin. - Need a different role (e.g. staff/employee) in one test: Import
loginAsTestUserfrom the fixture and callawait loginAsTestUser(page, 'staff')at the start of that test. - Public/unauthenticated flows (login page, registration, public form): Do not use the authenticated project; run in the default (no storageState) project.
3.3 Env vars for E2E auth
- Same as in the fixture:
TEST_USER_ADMIN_EMAIL,TEST_USER_ADMIN_PASSWORD,TEST_ORG_SLUG, and optionallyTEST_USER_STAFF_*,TEST_USER_MANAGER_*,TEST_USER_VIEWER_*. - Default (no env): The fixture uses seed users (admin@test-org-alpha.example, password
password, org slugtest-org-alpha). For this to work, seed data must be applied afternpx supabase db reset(see Run all tests locally). - For CI, set GitHub Actions secrets if you want authenticated E2E to pass.
3.4 Auth state file
- Path:
tests/e2e/.auth/admin.json(created by global setup). - Git: Add
tests/e2e/.auth/to.gitignoreso auth state is not committed.
4. Debugging
4.1 Vitest (unit, integration, RLS)
- UI: Run
npm run test:uito open Vitest’s browser UI. Use it to run and debug tests by file or by test. - VS Code / Cursor: Use the Vitest extension to run and debug from the editor (Run/Debug on the test or describe block).
4.2 Playwright (E2E)
- Report: After a run, open the HTML report:
npx playwright show-report(usesplaywright-report/by default). - Trace: On failure or first retry, a trace is recorded (config:
trace: 'on-first-retry'). Open it via the report or withnpx playwright show-trace path/to/trace.zip. - UI: Run
npm run test:e2e:uifor Playwright’s UI mode to step through and debug tests. - CI: In GitHub Actions, the workflow uploads the
playwright-report/(and optionallytest-results/) as artifacts. Download the artifact and open the report or trace locally.
4.3 Edge Functions (debugging and troubleshooting)
- Breakpoints: Run
npm run supabase:functions:serve:inspect, then open Chrome →chrome://inspect→ Configure127.0.0.1:8083→ Open dedicated DevTools for Node. Trigger a function to pause and step. See EDGE_AND_API_TESTING.md. - Troubleshooting: 401/403, import errors, file writes, hanging requests — see EDGE_AND_API_TESTING.md.
5. CI summary
-
Build workflow (PR + push): .github/workflows/build.yml runs format check, typecheck, lint,
npm testwithSKIP_SUPABASE_TESTS=true(non-Supabase Vitest suites), RLS coverage check (npm run check-rls-coverage --min-coverage 100), build (npm run build), installs Playwright Chromium, runs baseline smoke gate (npm run test:baseline:smoke— Playwright smoke + RLS smoke + audit completeness/enforce), and uploads audit artifacts. -
E2E (full) workflow (opt-in): .github/workflows/e2e-full.yml runs the full Playwright suite (
npx playwright test --project=chromium) on:workflow_dispatch(manual:gh workflow run e2e-full.yml)- Nightly schedule (07:00 UTC)
- Pull requests labeled
e2e-full
E2E_VITE_SUPABASE_URL,E2E_VITE_SUPABASE_PUBLISHABLE_KEY,TEST_USER_ADMIN_EMAIL,TEST_USER_ADMIN_PASSWORD,TEST_ORG_SLUG. Optional role-specific secrets:TEST_USER_STAFF_*,TEST_USER_MANAGER_*,TEST_USER_VIEWER_*. Uploadsplaywright-report/andtest-results/artifacts on completion / failure. -
SKIP_SUPABASE_TESTS=truebehavior: CI test job excludes Supabase-dependent suites (tests/rls/**,tests/integration/**) and DB-backed FM unit tests (fleet-maintenance-triggers,fleet-mpg-calculation) via Vitest config. This keeps the job deterministic without Supabase credentials while baseline smoke/audit gates still run. -
RLS: Default command
npm run test:rlsruns the full RLS baseline (legacy clusters enabled) via the Supabase-aware full runner. RLS coverage is enforced in build workflow at 100%. -
E2E: Default build workflow runs deterministic Chromium smoke only (
npm run test:e2e:smoke). For broader E2E coverage in CI, add a dedicated job that setsTEST_USER_ADMIN_EMAIL,TEST_USER_ADMIN_PASSWORD, and Supabase env vars (from GitHub secrets), then runsnpm run build && npm run test:e2e.
6. Quick reference
- Run unit tests for a path:
npm run test:unit -- tests/unit/hr/ - Run RLS tests (full baseline):
npm run test:rls(ensure Supabase env is set, or wrapper will auto-skip DB-dependent specs). - Full RLS run:
npm run test:rls - Run RLS smoke:
npm run test:rls:smoke - Run baseline confidence gate:
npm run test:baseline - Run baseline smoke gate:
npm run test:baseline:smoke - Run E2E smoke locally:
npm run test:e2e:smoke - Open Vitest UI:
npm run test:ui - Open Playwright report after E2E:
npx playwright show-report
6.1 Test audit gate defaults (CI)
npm run test:audit:enforce reads thresholds from environment variables. In CI build workflow we enforce:
6.2 Common test failures and fixes
Patterns we hit during the 2026-04 dev/test stability sweep — keep these in mind when adding or migrating tests:-
Failed to resolve import "@/cores/{core}/hooks/{useFoo}"— HR hooks were reorganized into subfolders (ats/,oversight/,employees/,payroll/,scheduling/,credentialing/,onboarding/,benefits/). Update the import path in the test (and anyvi.mock(...)/await import(...)calls) to the new location. -
Failed to resolve import "npm:@sentry/deno"(or othernpm:specifiers) — A unit test is reaching intosupabase/functions/_shared/*.ts. Don’t do that. Either:- Extract the pure logic into
src/shared/lib/*and have the Edge Function import it from there, OR - Rely on the Vitest alias safety-net in
vitest.config.tsthat mapsnpm:@sentry/deno*→tests/mocks/deno-sentry-shim.ts. (The shim re-exports the surface as no-ops.)
- Extract the pure logic into
-
useNavigation must be used within NavigationProvider/useLocation() may be used only in the context of a <Router>/No QueryClient set, use QueryClientProvider— Component test renders a real component without provider context. Wrap withTestProvidersfrom@/tests/utils/providers(which already providesMemoryRouter,QueryClientProvider,OrganizationProvider):If your test wraps its own router (e.g.MemoryRouterwith<Routes>), passnoRoutertoTestProvidersto avoid React Router’s “cannot render a Router inside another Router” invariant. -
Cannot find module '@/...'from arequire()call — Tests run as ESM under Vitest 4; replacerequire(...)with a staticimportand avi.mock(...)declaration at the top of the file. -
Supabase chainable mock returns
undefined— When mockingsupabase.from(...).select(...).eq(...).range(...), build the chain so it is both chainable and thenable (the realPostgrestFilterBuilderis). Seetests/unit/platform/audit/useAuditLogsList.test.tsfor the canonical pattern (createChainableMock). -
Fetch / Response stubbing in MCP / edge-shared tests — Use
new Response(JSON.stringify({...}), { status })instead of POJO{ ok, status, text: () => Promise.resolve('') }. Node’sundicifetch implementation callsclone()and other Response methods that POJO stubs don’t have. -
Route or constant drift — Don’t reach for
it.skip(). Read the source, decide whether the source or the test is authoritative (source usually wins), and align. Many recent failures were just constants that moved (e.g.DEFAULT_SLA_THRESHOLD_MINUTES: 240 → 120,INTAKE_STATUSESlength,/hr/benefits/enrollment → /hr/my-benefits/enroll).
7. Run all tests locally
Use this checklist to run every test type (unit, integration, RLS, E2E) on your machine.Prerequisites
- Node 20
- Supabase CLI (or
npx supabasefrom project root) - Docker (for local Supabase)
One-time local setup
-
Start local Supabase
(Or
supabase startif the CLI is on PATH.) -
Apply migrations and base seeds
This applies all migrations and runs base seeds (organizations, sites, departments, users). Test users (e.g. admin@test-org-alpha.example, password
password) are created and used by E2E. See supabase/seeds/README.md and SUPABASE_CLI_LOCAL_WORKFLOW.md. -
Start Edge Functions (optional, for integration tests that invoke functions)
In a separate terminal, runnpm run supabase:functions:serve. See EDGE_AND_API_TESTING.md. -
Set Supabase env for RLS and E2E
Copy
.env.exampleto.env.local(or set env in your shell) and set:VITE_SUPABASE_URL— fromnpx supabase status(API URL) when using local SupabaseVITE_SUPABASE_PUBLISHABLE_KEY— anon key fromnpx supabase statusSUPABASE_SERVICE_ROLE_KEY— service_role key fromnpx supabase status
Optional E2E overrides
If you use custom test users instead of seed users, set:TEST_USER_ADMIN_EMAIL,TEST_USER_ADMIN_PASSWORD,TEST_ORG_SLUG- And, if needed:
TEST_USER_STAFF_*,TEST_USER_MANAGER_*,TEST_USER_VIEWER_*
Commands
- Vitest (unit + integration + RLS):
npm test - RLS baseline (legacy clusters included by default):
npm run test:rls - Edge function (Deno) tests:
npm run test:functions(see EDGE_AND_API_TESTING.md). - E2E:
npm run build && npm run test:e2e
Ornpm run test:e2e:smokefor a shorter run (deterministic auth/security route smoke).
Troubleshooting
-
E2E auth fails: Confirm base seed was applied (
npx supabase db resetruns base seeds). Fixture defaults use admin@test-org-alpha.example / password / test-org-alpha. If using custom users, set the env vars and ensure those users exist in the DB. -
RLS tests fail: Confirm
VITE_SUPABASE_URL,SUPABASE_SERVICE_ROLE_KEY, andVITE_SUPABASE_PUBLISHABLE_KEYare set and that local Supabase is running (or the linked project is reachable). -
Runtime env mismatch in cloud/dev shells: If injected runtime vars differ from repo
.env, load project-local values before DB-integrated tests:Then verify refs are aligned withnpm run test:audit:completeness(Environment Consistency section).
8. Run E2E in Cursor Cloud (or any Docker-less environment)
When running E2E in environments without Docker (e.g. Cursor Cloud VMs), local Supabase is unavailable. Use a remote Supabase project (dev/staging) instead:One-time setup
-
Install Playwright browsers (browsers are not pre-installed on cloud VMs):
-
Provision Supabase env vars. Provide the project URL + anon key in
.envor.env.local:The build (npm run build) requires both vars;@julr/vite-plugin-validate-envenforces this. -
Provision E2E test user credentials (a seeded admin who can log in via password):
Default fixture values target the local seed user (
admin@test-org-alpha.example/password/test-org-alpha); override with the env vars above when targeting a remote project. -
Verify Supabase reachability + admin login (cheap pre-flight; mirrors
setup-auth.spec.ts):REST should return 200/401; the token endpoint should return a JSON body withaccess_token. -
Build the app (Playwright’s
webServerwill runnpm run previewon:4173, which requiresdist/):
Running
- Smoke (~10 s):
npm run test:e2e:smoke - Full (chromium-only):
npx playwright test --project=chromium - Per folder:
npm run test:e2e:hr/:fa/:ce/:fw/:gr/:platformetc. - Bulk triage helper:
bash scripts/test-analysis/run-e2e-triage.shruns every per-folder set in sequence and writes results toreports/e2e-triage/{folder}.txtfor analysis.
Notes
- Docker is NOT required for these flows; only
npx supabase start/db resetneed Docker. Schema changes still need to be tested through the full local Supabase workflow. tests/e2e/.auth/admin.jsonis git-ignored and re-created by thee2e-setup-authPlaywright project on each run.- Specs that require seeded data (e.g. CE-UX-05 lead-conversion-wizard expects a “Test LeadE2E” lead) skip themselves when the data is absent. Track these via the Skipped column in
reports/e2e-triage/SUMMARY.md.
9. AI / interactive browser testing
When an AI agent (or a human) drives the app in a real browser — exploratory verification, debugging, or confirming a flow before claiming “E2E done” — follow the conventions below. Playwright*.spec.ts files remain the source of truth for regression; the interactive browser session is an additional check, not a replacement. For the automated auto-heal verdict loop, see BROWSER_AUTOHEAL.md.
9.1 Credential policy
Do not store plaintext credentials in docs or prompts. Use environment variables:TEST_USER_ADMIN_EMAIL,TEST_USER_ADMIN_PASSWORDTEST_USER_STAFF_EMAIL,TEST_USER_STAFF_PASSWORDTEST_USER_MANAGER_EMAIL,TEST_USER_MANAGER_PASSWORDTEST_USER_VIEWER_EMAIL,TEST_USER_VIEWER_PASSWORD
.env.local example and §3.3 for the fixture defaults).
Security notes: use test accounts only; never commit or document raw credentials; never include PHI/PII in prompts, screenshots, or logs; keep tenant isolation and RLS behavior validated during tests.
9.2 Standard browser testing workflow
http://localhost:8080). If already authenticated, continue; if at /auth, sign in using TEST_USER_ADMIN_EMAIL / TEST_USER_ADMIN_PASSWORD, verify the redirect to an authenticated page, then proceed to the target workflow.
9.3 Recommended scenarios
- Form submission and validation
- CRUD lifecycle
- Navigation and breadcrumbs
- Permission-protected routes
- Mobile/responsive layout
- Cross-module journey transitions
9.4 Troubleshooting
- Login fails: confirm env vars are present in the running environment; verify the test users exist in the target Supabase project; check the browser console and network logs for auth errors.
- No organization access: verify role assignments in
pf_user_role_assignments; confirm org/site records exist; verify permissions and RLS policies.
9.5 HR workflows (browser verification)
HR has two complementary modes:
Playwright specs are the source of truth. AI may additionally use the browser MCP to verify a flow manually.
HR workflow routes:
- Self-service:
/hr/me,/hr/me/profile,/hr/me/onboarding,/hr/me/pay,/hr/my-performance - Admin:
/hr/employees,/hr/employees/new,/hr/leave,/hr/leave/new,/hr/ats/candidates,/hr/performance(and sub-routes),/hr/employee-relations(incidents, disciplinary, grievances, investigations, my-cases),/hr/benefits/plans,/hr/benefits/enrollments,/hr/analytics
hr-e2e-browser-testing skill. Run the Playwright HR suite with run-e2e-core --core hr; for interactive verification use verify-core-workflow-browser --core hr. New HR E2E specs follow specs/_templates/tests/E2E_HR_WORKFLOW_TEMPLATE.ts.