# Test Strategy The frontend test suite is intentionally layered so failures point at the right level of the application. ## Commands - `pnpm test`: run all Vitest suites. - `pnpm run test:unit`: run utility, store, and infrastructure unit tests. - `pnpm run test:api`: run API wrapper contract tests. - `pnpm run test:component`: run Vue component contract tests. - `pnpm run test:views`: run route-level view tests (mounted views with mocked APIs). - `pnpm run test:guard`: run meta tests that keep the test suite itself safe. - `pnpm run test:impact`: print the recommended checks for the current change. - `pnpm run test:ci`: run Vitest with coverage thresholds. - `pnpm run test:coverage`: run Vitest with coverage. - `pnpm run test:e2e`: run Playwright Test specs. - `pnpm run test:e2e:responsive`: run the three-terminal gate against the self-contained mock build. - `pnpm run test:e2e:headed`: run Playwright Test specs in a visible browser. - `pnpm run test:e2e:sweep`: run the deep browser sweep. - `pnpm run test:e2e:sweep:headed`: run the deep browser sweep in a visible browser. ## Verification Run the checks yourself instead of trusting recorded results, which go stale as the suite changes. - `pnpm check`, `pnpm lint:check`, `pnpm build`, and `pnpm test:ci` cover types, lint, the build, and coverage-gated Vitest (thresholds stay enforced by `vitest.config.ts`). - `pnpm run test:e2e:responsive` runs the three-terminal gate against the self-contained mock build, so it needs no backend. - `pnpm run test:e2e` runs the full Playwright suite; it needs a reachable gateway unless you set `E2E_SERVER_MODE=mock`. The automated suites do not replace release gating: full real-backend business E2E, Lighthouse budgets, full `axe-core` scans, and a full-route manual visual review must still run against a disposable backend dataset before release. ## Layers - `tests/unit`: deterministic unit tests for utils, stores, and shared infrastructure such as Axios interceptors. - `tests/api`: frontend API wrapper contracts. These tests mock the transport layer and snapshot the method, URL, body, and config used by every exported wrapper. - `tests/component`: Vue component contracts using `@vue/test-utils`. Reuse the shared Element Plus stubs in `tests/setup/stubs/element-plus.ts` instead of redefining them per file. - `tests/views`: route-level view contracts. Same toolchain as `tests/component` but the subject is a `src/views/**` page mounted with mocked APIs. Use this layer when the unit-of-behaviour is a routed page rather than a reusable building block. - `tests/e2e`: Playwright browser tests. The specs cover authentication, route availability, page health, and safe UI interactions. - `tests/e2e/browser-sweep.mjs`: entrypoint for the deep browser sweep. The actual runner lives under `tests/e2e/browser-sweep`; keep new browser scenarios in Playwright specs unless they specifically need the sweep runner. - `tests/guardrails`: meta tests for AI-assisted development. These tests block focused or disabled tests, fixed E2E business IDs, missing guard scripts, and missing testing policy documentation. ## Adding a new test 1. Pick the right layer (`tests/{unit,component,views,api,e2e}`). 2. Copy the matching template from `tests/_templates/`: - `store.test.template.ts` for Pinia stores - `composable.test.template.ts` for `src/composables/*` - `component.test.template.ts` for `src/components/*` (and views) - `api.test.template.ts` for new API wrappers (which actually means extending `tests/api/api-contracts.test.ts`) 3. Reuse fixtures from `tests/fixtures/` when the data shape already exists there — don't inline a duplicate `sampleMenuTree` / credentials block. 4. Reuse stubs from `tests/setup/stubs/element-plus.ts` for any Element Plus components. 5. Run `pnpm test` locally before committing. If it fails with a Vue warn, see [test-debugging](https://docs.dc3.site/en/frontend/test-debugging). ## Conventions These rules are mechanically enforced by `tests/guardrails/ai-guardrails.test.ts`. Style points without a guardrail are listed for reviewer reference. ### File naming (enforced) - `tests/{unit,component,views}/.test.ts`. The base name should mirror the source module under test (`auth.ts` → `auth-store.test.ts`, `usePagedList.ts` → `use-paged-list.test.ts`, `validationUtil.ts` → `validation-util.test.ts`). - Avoid suffixes that describe the test itself (`-coverage`, `-bonus`); name for the _subject_, not the goal. ### Mocking (enforced for multi-mock files) - More than one `vi.mock` in a file → bundle the spies in one `vi.hoisted(() => ({…}))` block at the top, conventionally named `xxxMocks` (`tokenMocks`, `apiMocks`). Keeps top-level state minimal and avoids "Cannot access X before initialization" hoisting errors. - A single `vi.mock` may inline its factory. ### Element Plus stubs (enforced) - Reuse `tests/setup/stubs/element-plus.ts`. Do not redefine `ElButton`, `ElForm`, `ElPagination`, `ElInput`, `ElSelect`, etc. inline. - Add new layout/decorator stubs to `layoutStubs` in that file when a new Element Plus component is needed. ### Type assertions (enforced) - No `as unknown as T` double assertions. Use a type-correct fixture builder or `// @ts-expect-error` for the one line that intentionally violates the contract. - No `as never`. Build a properly-typed fixture instead. ### `wrapper.vm` is forbidden in component/view tests (enforced) - Drive the component through props, slots, emits, and DOM events — the public contract. Calling `wrapper.vm.someInternalMethod()` couples tests to the component's internal API, which churns whenever `