> ## Documentation Index
> Fetch the complete documentation index at: https://docs.encoreos.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing Setup and Run Guide

> Last Updated: 2026-05-13

**Last Updated:** 2026-05-13

This document is the single reference for how to run each test type, required environment variables, E2E authentication, and debugging.

**See also:** [SPEC\_TEST\_COVERAGE.md](SPEC_TEST_COVERAGE.md) (spec–test matrix), [TESTING\_IMPROVEMENTS.md](TESTING_IMPROVEMENTS.md) (historical review findings and follow-up backlog), [TEST\_ORGANIZATION\_STANDARDS.md](TEST_ORGANIZATION_STANDARDS.md) (naming and structure), [tests/README.md](../../tests/README.md) (suite overview), [EDGE\_AND\_API\_TESTING.md](EDGE_AND_API_TESTING.md) (Edge Functions and API local testing, debugging, troubleshooting). To test and verify Sentry configuration, see [Sentry verification runbook](../development/SENTRY_VERIFICATION_RUNBOOK.md).

**Canonical skill patterns** (Cursor IDE): When writing or migrating tests, follow these skill SOPs — they encode the patterns that this guide formalizes:

* [`module-testing-strategy`](../../.cursor/skills/module-testing-strategy/SKILL.md) — module-level testing strategy and coverage requirements across unit, integration, RLS, and E2E.
* [`playwright-testing`](../../.cursor/skills/playwright-testing/SKILL.md) — E2E test organization, auth fixtures, flake reduction.
* [`tdd-workflow`](../../.cursor/skills/tdd-workflow/SKILL.md) — TDD red/green/refactor loop and when to apply it.

***

## 1. How to run each test type

| Test type                      | Command                           | Notes                                                                                                                                                                             |
| ------------------------------ | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **All (Vitest)**               | `npm test`                        | All Vitest suites except E2E. When `SKIP_SUPABASE_TESTS=true`, excludes Supabase-dependent RLS/integration suites and DB-backed FM unit tests via `vitest.config.ts`.             |
| **Unit**                       | `npm run test:unit`               | `tests/unit/`                                                                                                                                                                     |
| **Unit (watch)**               | `npm run test:unit:watch`         | Re-runs on file changes                                                                                                                                                           |
| **Unit (Vitest UI)**           | `npm run test:ui`                 | Browser UI for running/debugging Vitest tests                                                                                                                                     |
| **Integration**                | `npm run test:integration`        | `tests/integration/`                                                                                                                                                              |
| **RLS**                        | `npm run test:rls`                | Full `tests/rls/` baseline (legacy clusters included); Supabase-aware wrapper auto-sets `SKIP_SUPABASE_TESTS=true` if env/REST checks fail                                        |
| **RLS (full suite)**           | `npm run test:rls`                | Full RLS suite / 100% coverage gate                                                                                                                                               |
| **RLS (smoke)**                | `npm run test:rls:smoke`          | Fast DB-connected smoke over `pf_profiles` + `pf_organizations` RLS suites                                                                                                        |
| **Baseline confidence gate**   | `npm run test:baseline`           | Runs unit + integration + RLS smoke + E2E smoke + audit completeness/enforcement                                                                                                  |
| **Baseline smoke gate**        | `npm run test:baseline:smoke`     | Fast confidence gate: RLS smoke + E2E smoke + audit completeness/enforcement                                                                                                      |
| **E2E (all)**                  | `npm run test:e2e`                | Full Playwright suite (all projects)                                                                                                                                              |
| **E2E (UI)**                   | `npm run test:e2e:ui`             | Playwright UI mode                                                                                                                                                                |
| **E2E (auth folder)**          | `npm run test:e2e:auth`           | `tests/e2e/auth/` only                                                                                                                                                            |
| **E2E (HR folder)**            | `npm run test:e2e:hr`             | `tests/e2e/hr/` only                                                                                                                                                              |
| **E2E (FA folder)**            | `npm run test:e2e:fa`             | `tests/e2e/fa/` only (Playwright `.spec.ts` only)                                                                                                                                 |
| **E2E (CE folder)**            | `npm run test:e2e:ce`             | `tests/e2e/ce/` only                                                                                                                                                              |
| **E2E (FW folder)**            | `npm run test:e2e:fw`             | `tests/e2e/fw/` only                                                                                                                                                              |
| **E2E (GR folder)**            | `npm run test:e2e:gr`             | `tests/e2e/gr/` only (root-level GR specs)                                                                                                                                        |
| **E2E (platform)**             | `npm run test:e2e:platform`       | `tests/e2e/platform/` only                                                                                                                                                        |
| **E2E (smoke)**                | `npm run test:e2e:smoke`          | Deterministic auth/security route smoke (`tests/e2e/smoke/auth-security-smoke.spec.ts`, Chromium only, list reporter)                                                             |
| **RLS coverage**               | `npm run check-rls-coverage`      | Report or enforce RLS test coverage; use `--missing-only`, `--min-coverage N`, `--json`                                                                                           |
| **Test completeness audit**    | `npm run test:audit:completeness` | Generates `reports/audits/TEST_COMPLETENESS_AUDIT.{md,json}` with spec/matrix gaps, RLS coverage rollup, placeholder/skip counts, Supabase env consistency, and E2E auth adoption |
| **Quick (changed)**            | `npm run test:quick`              | Unit tests for changed files only                                                                                                                                                 |
| **Coverage**                   | `npm run test:coverage`           | Vitest coverage report                                                                                                                                                            |
| **Edge function (Deno) tests** | `npm run test:functions`          | Deno tests for Edge Functions. Requires local Supabase + functions serve for HTTP tests; see [EDGE\_AND\_API\_TESTING.md](EDGE_AND_API_TESTING.md).                               |

**Single file or pattern:**

```bash theme={null}
npm test -- tests/unit/hr/useEmployees.test.ts
npm test -- tests/rls/hr-employees.rls.test.ts
npx playwright test tests/e2e/hr/compensation-management.spec.ts
```

***

## 2. Environment variables

| Test type                  | Local                                                                                                                                                                                                                                                                                                                                                                                                                              | CI                                                                                                                |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| **Unit**                   | None                                                                                                                                                                                                                                                                                                                                                                                                                               | `SKIP_SUPABASE_TESTS=true` (unit job)                                                                             |
| **Integration**            | Optional: `VITE_SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`                                                                                                                                                                                                                                                                                                                                                                         | `SKIP_SUPABASE_TESTS=true` (integration job runs mocked)                                                          |
| **RLS**                    | **Required:** `VITE_SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`, `VITE_SUPABASE_PUBLISHABLE_KEY`                                                                                                                                                                                                                                                                                                                                    | Same (secrets); RLS job runs only when not a fork                                                                 |
| **E2E**                    | For authenticated E2E, apply seed data first (see [Run all tests locally](#7-run-all-tests-locally)); fixture defaults match [supabase/seeds/base/02\_users.sql](../../supabase/seeds/base/02_users.sql) ([admin@test-org-alpha.example](mailto:admin@test-org-alpha.example) / password / test-org-alpha). Override with `TEST_USER_ADMIN_EMAIL`, `TEST_USER_ADMIN_PASSWORD`, `TEST_ORG_SLUG` (and role-specific vars if needed). | E2E workflows do not set test user secrets by default; set in GitHub secrets if you need authenticated E2E in CI. |
| **Edge/API local testing** | For integration tests or Deno tests that invoke Edge Functions locally: `VITE_SUPABASE_URL=http://localhost:54321`, anon key from `npx supabase status`. Run `npm run supabase:functions:serve` in a separate terminal.                                                                                                                                                                                                            | —                                                                                                                 |

**Local `.env.local` (git-ignored) example for RLS and E2E:**

```env theme={null}
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key

# Optional: E2E test users (override fixture fallbacks)
TEST_USER_ADMIN_EMAIL=admin@your-test-org.com
TEST_USER_ADMIN_PASSWORD=YourTestPassword123!
TEST_ORG_SLUG=your-org-slug
```

***

## 2a. E2E conventions (Playwright vs Vitest)

* **Playwright** runs only `*.spec.ts` (and `.spec.tsx`) in `tests/e2e/` (see [playwright.config.ts](../../playwright.config.ts) `testMatch`). Use `npm run test:e2e` or `npm run test:e2e:{core}` to run these.
* **Vitest** can run `*.test.ts` under `tests/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 with `npm test -- tests/e2e/`.
* Some spec files are in Playwright’s `testIgnore` (e.g. `fa/budget-scenarios.spec.ts`, `ce/activities.spec.ts`) and run as Vitest or are documented in the [spec–test matrix](SPEC_TEST_COVERAGE.md#e2e-conventions).
* **Critical flows** (auth, multi-tenant, financial, workflow approvals, leave accruals) are listed in [SPEC\_TEST\_COVERAGE.md § Critical flows](SPEC_TEST_COVERAGE.md#critical-flows-agentsmd).

***

## 3. E2E authentication

### 3.1 Shared auth fixture and setup project

* **Fixture:** [tests/e2e/fixtures/auth.fixture.ts](../../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](../../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-auth` runs [tests/e2e/setup-auth.spec.ts](../../tests/e2e/setup-auth.spec.ts), which logs in as admin and saves browser state to `tests/e2e/.auth/admin.json`. The `chromium-authenticated` project depends on this, so auth runs after the server is reachable.
* **Authenticated project:** Playwright config defines a project `chromium-authenticated` that uses `storageState: 'tests/e2e/.auth/admin.json'`. Specs that use `test.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. No `beforeEach` login.
* **Need a different role (e.g. staff/employee) in one test:** Import `loginAsTestUser` from the fixture and call `await 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 optionally `TEST_USER_STAFF_*`, `TEST_USER_MANAGER_*`, `TEST_USER_VIEWER_*`.
* **Default (no env):** The fixture uses seed users ([admin@test-org-alpha.example](mailto:admin@test-org-alpha.example), password `password`, org slug `test-org-alpha`). For this to work, seed data must be applied after `npx supabase db reset` (see [Run all tests locally](#7-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 `.gitignore` so auth state is not committed.

***

## 4. Debugging

### 4.1 Vitest (unit, integration, RLS)

* **UI:** Run `npm run test:ui` to 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` (uses `playwright-report/` by default).
* **Trace:** On failure or first retry, a trace is recorded (config: `trace: 'on-first-retry'`). Open it via the report or with `npx playwright show-trace path/to/trace.zip`.
* **UI:** Run `npm run test:e2e:ui` for Playwright’s UI mode to step through and debug tests.
* **CI:** In GitHub Actions, the workflow uploads the `playwright-report/` (and optionally `test-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` → Configure `127.0.0.1:8083` → Open dedicated DevTools for Node. Trigger a function to pause and step. See [EDGE\_AND\_API\_TESTING.md](EDGE_AND_API_TESTING.md).
* **Troubleshooting:** 401/403, import errors, file writes, hanging requests — see [EDGE\_AND\_API\_TESTING.md](EDGE_AND_API_TESTING.md#troubleshooting-checklist).

***

## 5. CI summary

* **Build workflow (PR + push):** [.github/workflows/build.yml](../../.github/workflows/build.yml) runs format check, typecheck, lint, `npm test` with `SKIP_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](../../.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`

  Required secrets: `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_*`. Uploads `playwright-report/` and `test-results/` artifacts on completion / failure.
* **`SKIP_SUPABASE_TESTS=true` behavior:** 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:rls` runs 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 sets `TEST_USER_ADMIN_EMAIL`, `TEST_USER_ADMIN_PASSWORD`, and Supabase env vars (from GitHub secrets), then runs `npm 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:

| Gate variable                                       | CI value | Meaning                                                                                             |
| --------------------------------------------------- | -------: | --------------------------------------------------------------------------------------------------- |
| `MAX_COMPLETED_SPECS_MISSING_FROM_MATRIX`           |      `0` | Completed specs must all be present in matrix                                                       |
| `MAX_COMPLETED_SPECS_WITH_UNIT_OR_INTEGRATION_GAPS` |      `0` | No completed spec can have Unit/Integration = Partial/No                                            |
| `MAX_PLACEHOLDER_ASSERTIONS`                        |      `0` | No `expect(true).toBe(true)` placeholders                                                           |
| `MAX_EXPLICIT_SKIP_COUNT`                           |      `0` | No `.skip` in active test suites                                                                    |
| `MAX_E2E_WITHOUT_SHARED_AUTH`                       |      `0` | All non-exempt Playwright specs adopt shared auth pattern                                           |
| `MAX_POLICY_DRIFT_SIGNALS`                          |      `0` | No detected RLS policy drift signals                                                                |
| `MAX_LIKELY_MISSING_CHECKLIST_ITEMS`                |      `0` | No likely-missing checklist backlog for completed specs                                             |
| `MAX_DEFAULT_EXCLUDED_RLS_CLUSTERS`                 |      `0` | Default `test:rls` cannot exclude legacy clusters                                                   |
| `MAX_E2E_SMOKE_SKIP_DIRECTIVES`                     |      `0` | Smoke spec files cannot contain `skip`/`fixme` directives                                           |
| `MIN_E2E_SMOKE_SPEC_FILES`                          |      `1` | At least one executable smoke spec must exist                                                       |
| `MIN_RLS_COVERAGE`                                  |    `100` | Audit-reported RLS table coverage minimum                                                           |
| `ENFORCE_RUNTIME_SUPABASE_HEALTH`                   |  `false` | Enable strict runtime Supabase gates (URL/key alignment + REST reachability floor)                  |
| `MIN_RUNTIME_REST_REACHABLE`                        |      `0` | Runtime Supabase REST reachability floor (applied only when `ENFORCE_RUNTIME_SUPABASE_HEALTH=true`) |

***

## 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 any `vi.mock(...)` / `await import(...)` calls) to the new location.

* **`Failed to resolve import "npm:@sentry/deno"` (or other `npm:` specifiers)** — A unit test is reaching into `supabase/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.ts` that maps `npm:@sentry/deno*` → `tests/mocks/deno-sentry-shim.ts`. (The shim re-exports the surface as no-ops.)

* **`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 with `TestProviders` from `@/tests/utils/providers` (which already provides `MemoryRouter`, `QueryClientProvider`, `OrganizationProvider`):

  ```tsx theme={null}
  import { render } from '@testing-library/react';
  import { TestProviders } from '@/tests/utils/providers';

  render(
    <TestProviders route="/hr">
      <DesktopHeader user={user} onSignOut={vi.fn()} />
    </TestProviders>,
  );
  ```

  If your test wraps its own router (e.g. `MemoryRouter` with `<Routes>`), pass `noRouter` to `TestProviders` to avoid React Router's "cannot render a Router inside another Router" invariant.

* **`Cannot find module '@/...'` from a `require()` call** — Tests run as ESM under Vitest 4; replace `require(...)` with a static `import` and a `vi.mock(...)` declaration at the top of the file.

* **Supabase chainable mock returns `undefined`** — When mocking `supabase.from(...).select(...).eq(...).range(...)`, build the chain so it is **both chainable and thenable** (the real `PostgrestFilterBuilder` is). See `tests/unit/platform/audit/useAuditLogsList.test.ts` for 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's `undici` fetch implementation calls `clone()` 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_STATUSES` length, `/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 supabase` from project root)
* Docker (for local Supabase)

### One-time local setup

1. **Start local Supabase**
   ```bash theme={null}
   npx supabase start
   ```
   (Or `supabase start` if the CLI is on PATH.)

2. **Apply migrations and base seeds**
   ```bash theme={null}
   npx supabase db reset
   ```
   This applies all migrations and runs base seeds (organizations, sites, departments, users). Test users (e.g. [admin@test-org-alpha.example](mailto:admin@test-org-alpha.example), password `password`) are created and used by E2E. See [supabase/seeds/README.md](../../supabase/seeds/README.md) and [SUPABASE\_CLI\_LOCAL\_WORKFLOW.md](../development/supabase/SUPABASE_CLI_LOCAL_WORKFLOW.md).

3. **Start Edge Functions (optional, for integration tests that invoke functions)**\
   In a separate terminal, run `npm run supabase:functions:serve`. See [EDGE\_AND\_API\_TESTING.md](EDGE_AND_API_TESTING.md).

4. **Set Supabase env for RLS and E2E**
   Copy `.env.example` to `.env.local` (or set env in your shell) and set:

   * `VITE_SUPABASE_URL` — from `npx supabase status` (API URL) when using local Supabase
   * `VITE_SUPABASE_PUBLISHABLE_KEY` — anon key from `npx supabase status`
   * `SUPABASE_SERVICE_ROLE_KEY` — service\_role key from `npx supabase status`

   For a linked remote (e.g. staging), use that project’s URL and keys instead.

### 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](EDGE_AND_API_TESTING.md)).
* **E2E:** `npm run build && npm run test:e2e`\
  Or `npm run test:e2e:smoke` for a shorter run (deterministic auth/security route smoke).

### Troubleshooting

* **E2E auth fails:** Confirm base seed was applied (`npx supabase db reset` runs base seeds). Fixture defaults use [admin@test-org-alpha.example](mailto: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`, and `VITE_SUPABASE_PUBLISHABLE_KEY` are 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:

  ```bash theme={null}
  set -a && source ".env" && set +a
  ```

  Then verify refs are aligned with `npm 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

1. **Install Playwright browsers** (browsers are not pre-installed on cloud VMs):
   ```bash theme={null}
   npx playwright install chromium       # add --with-deps if root, otherwise system deps may already be present
   ```

2. **Provision Supabase env vars.** Provide the project URL + anon key in `.env` or `.env.local`:
   ```bash theme={null}
   VITE_SUPABASE_URL=https://<ref>.supabase.co
   VITE_SUPABASE_PUBLISHABLE_KEY=<anon key>
   ```
   The build (`npm run build`) requires both vars; `@julr/vite-plugin-validate-env` enforces this.

3. **Provision E2E test user credentials** (a seeded admin who can log in via password):
   ```bash theme={null}
   TEST_USER_ADMIN_EMAIL=<admin email seeded in target project>
   TEST_USER_ADMIN_PASSWORD=<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.

4. **Verify Supabase reachability + admin login** (cheap pre-flight; mirrors `setup-auth.spec.ts`):
   ```bash theme={null}
   set -a && source .env && set +a
   curl -sS -o /dev/null -w "REST: %{http_code}\n" \
     "$VITE_SUPABASE_URL/rest/v1/" -H "apikey: $VITE_SUPABASE_PUBLISHABLE_KEY"
   curl -sS -X POST "$VITE_SUPABASE_URL/auth/v1/token?grant_type=password" \
     -H "apikey: $VITE_SUPABASE_PUBLISHABLE_KEY" \
     -H "Content-Type: application/json" \
     -d "{\"email\":\"$TEST_USER_ADMIN_EMAIL\",\"password\":\"$TEST_USER_ADMIN_PASSWORD\"}"
   ```
   REST should return 200/401; the token endpoint should return a JSON body with `access_token`.

5. **Build the app** (Playwright's `webServer` will run `npm run preview` on `:4173`, which requires `dist/`):
   ```bash theme={null}
   npm run build
   ```

### 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` / `:platform` etc.
* **Bulk triage helper:** `bash scripts/test-analysis/run-e2e-triage.sh` runs every per-folder set in sequence and writes results to `reports/e2e-triage/{folder}.txt` for analysis.

### Notes

* **Docker is NOT required** for these flows; only `npx supabase start` / `db reset` need Docker. Schema changes still need to be tested through the full local Supabase workflow.
* **`tests/e2e/.auth/admin.json`** is git-ignored and re-created by the `e2e-setup-auth` Playwright 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`.
