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

# Testing

> Where tests live, what a green run proves, fakes instead of secrets, and the end-to-end matrix.

Every test file sits directly in a `__tests__/` folder beside the code it proves, anywhere in the repository ([ADR 0022](/adr/0022-tests-live-in-tests-folders)). There is no root `tests/` folder and no shared tests workspace: each member owns its tests.

## Where tests live

| Kind | Where | Runner |
| - | - | - |
| Unit / component | `src/**/__tests__/<name>.test.ts(x)` | vitest (`pnpm test`) |
| App end-to-end | `apps/<app>/e2e/__tests__/`, never a shared workspace | Playwright (web), added with the first flow |
| Service tests | `src/**/__tests__/` in each service | vitest (and `encore test` once Encore lands) |
| Repository scripts | `tools/scripts/__tests__/*.test.mjs` | `node --test` (part of `pnpm validate`) |
| Fixtures, doubles | `__fixtures__/` beside the tests, or the `testing` entry of the package that owns the contract | n/a |

* A test file's name is its subject's name plus `.test` (never `.spec`); its header says what the suite proves, and each `it()` title is a full sentence stating behaviour.
* The vitest presets set `passWithNoTests: false`, so a member with an empty suite fails. Every layer or entry of a package ships at least one test.
* Packages meet one coverage floor from `@stackra/vitest-config/coverage`: lines and functions 80, branches 70. A threshold moves only upward; a package that cannot meet it says why in its own `vitest.config.ts`. Apps are not gated.
* Run one member with `pnpm turbo run test --filter=<package-name>`; coverage with `pnpm test:coverage`.

## Tests need zero secrets

No credential appears in a test, a fixture or a flow. A test that needs a real credential is a design finding, not a missing variable ([Secrets](/constitution/secrets)).

* Replace a provider (payments, mail, SMS, AI) with a recording **fake** at the seam where the code takes it as an argument or a port. The fake records calls so the test can assert on them (`assertSent`, `assertNothingSentTo`) and can fail on demand (`failNext("card_declined")`).
* A fake is the same shape as the real provider's port and lives in the `testing` entry of the package that owns the port, so every consumer reuses it.
* Where a test must type a payment card, it uses the provider's published test numbers only.
* Authenticated tests do not sign in: they build the same principal object the auth handler would produce and pass it to the endpoint.

## Table-driven tests

When a surface publishes many similar cases (commands, routes, validation rules, locale rows), define the cases once as a typed table and drive one `it.each` over it. The table is also the checklist: a new row without a case is visible in review. The same applies to the scaffold, which renders every template kind from one list in `tools/scripts/scaffold.test.mjs`.

## What a green run proves

`pnpm verify` runs, in order: `turbo run format:check lint typecheck test build`, then `pnpm validate` (script tests plus the repository-shape validator), `pnpm boundaries` and `pnpm knip`. A green run proves formatting, lint and type-correctness for every member, that each member's suite passes, that every build emits, and that the workspace contract holds (required files, tier tags, catalog versions, no unused code).

It does **not** prove three things:

> **It does not run a service or an app on a device**
>
> Nothing in `verify` starts a service, opens a browser, or launches a simulator. A wiring
> mistake, a startup failure or a screen that only breaks on iOS is invisible to it. Say
> "typechecks and unit-tests pass", never "the service works".

> **It does not exercise the CI runner or reach the vault**
>
> The local gate runs on your machine, not in the runner image, and never touches the secret
> vault. A job that needs a vault credential is reported as unexercised, not as passing; a stubbed
> credential would prove nothing and risks a real value in a log. See [CI](/runbooks/ci).

> **It does not prove layout, direction or theme correctness**
>
> Unit tests do not see pixels. Right-to-left layout, dark theme, dense text and screen-reader
> behaviour are proved by the end-to-end matrix below and by the accessibility review ([UI and
> accessibility](/standards/ui-and-accessibility)).

Never report a stage complete on the strength of a gate that could not have observed it. Report a run as exactly what it checked.

## End-to-end matrix

An app's e2e suite is a set of journeys run across axes, not a single configuration. Each project in the Playwright config is one combination:

| Axis | Values |
| - | - |
| Viewport | phone, tablet, desktop |
| Theme | light, dark |
| Direction | LTR, RTL |
| Text size | default, large (native) |

* The default run is a small, named subset (for example tablet light LTR, tablet dark LTR, tablet light RTL, phone light LTR). The full grid runs on a schedule or on demand.
* Journeys do not hard-code demo values or market literals: read them from the same source the app uses, and assert amounts by shape.
* A step that changes the screen may screenshot, named `<journey>-<step>-<viewport>-<locale>-<theme>`; baselines are reviewed images, compared per combination.
* Mobile journeys run against a simulator or device (Maestro or an equivalent); VoiceOver and split-screen checks stay manual and are listed in a checklist, not claimed as covered.
* Failures found by the suite in app code are reported to the app's owner, with the failing step; a spec does not paper over them with workarounds without saying so.

## What enforces this

| Rule | Check | Status |
| - | - | - |
| Empty suite fails | vitest presets (`passWithNoTests: false`) | Yes |
| Coverage floor on packages | `withCoverage` in `pnpm test:coverage` | Yes |
| Script tests and the scaffold render every template kind | `node --test tools/scripts/__tests__/*.test.mjs` in `pnpm validate` | Yes |
| Every test file sits directly in `__tests__/`; no `.spec` | `checkTestPlacement` in `tools/scripts/validate-repository.mjs` ([ADR 0022](/adr/0022-tests-live-in-tests-folders)) | Yes |
| Template output is validated, not only rendered | planned: scaffold test also runs the validator on each kind | No |
| E2E matrix and screenshot baselines | planned: per-app Playwright config when the first flow lands | No |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.