Type to search pages and rules.

Browse pages

Frontend Testing

Frontend tests prove behavior at the cheapest boundary that represents the risk.

Markdown13 provisions

Intent#

Frontend tests prove behavior at the cheapest boundary that represents the risk. Each kind in the table below runs against a frontend, and each one answers one question.

KindWhere it runsWhat it proves
LogicUnit runner beside the moduleA mapping, a computed window, a state decision
StructureUnit runner beside the suiteThe module tree mirrors the backend
Route contractBrowser suite against a running stackEvery route renders one floorplan and passes the browser checks
EvidenceBrowser suite against a running stackA use case's Cases, with each declared refusal provoked by name

A browser suite needs a deployment. Nothing in it starts one, so the same suite runs against a developer's stack and against a review deployment.

Related rules live on other pages. litespec/core/release#run-frontend-release-checks owns the gates a changed frontend runs. litespec/frontend/structure#place-tests-at-their-ownership-boundary owns test layout. litespec/frontend/ui#prove-ui-behavior-and-appearance owns the evidence a UI change runs.

Standards#

Match test level to risk#

ruleguidance

Requirement A frontend MUST use a logic test for pure logic, a browser specification for navigation and integration, and the route contract for every address.

Rationale Matching level to risk keeps the fast tests fast and reserves browser runs for behavior only a browser proves.

litespec/frontend/testing#match-test-level-to-risk

Prove a case by calling its use case#

ruleguidance

Requirement A test MUST prove a Case by calling its use case and asserting its Result, submitting a money-moving use case rather than only reading it.

Rationale Steps that call the use case and assert the Result let the model builder compute proof from the test body. A title tag claims proof without exercising anything. A money-moving use case that a test only reads never shows it can complete.

Example An evidence specification for placing an order submits the order and asserts the confirmation the success Result promises.

litespec/frontend/testing#prove-a-case-by-calling-its-use-case

Assert each declared refusal by its code#

ruleguidance

Requirement An evidence specification for a use case declaring a refusal MUST reach that refusal and assert it by its Result's code.

Rationale A refusal that only appears in a log never renders where a reader can see it. Addressing it by code also proves the code the Result declares is the code the page shows. The Case records the situation that reaches it.

Example The refusal alert carries the code, so the assertion names it.

litespec/frontend/testing#assert-each-declared-refusal-by-its-code

Test observable states#

ruleguidance

Requirement A frontend test MUST assert observable behavior in each state applicable to what the test covers: loading, empty, error, forbidden, not-found, pending, validation, and success.

Rationale An assertion on internal state passes while the rendered page stays broken. The Case states which of these its situation reaches, so a logic test asserts no route state it cannot reach. The project records each state as a Term with labels per language.

litespec/frontend/testing#test-observable-states

Keep mocks at owned boundaries#

ruleguidance

Requirement A frontend test MUST NOT mock the generated contract types or an implementation-private function, nor a library its stack names.

Rationale A test may still replace the generated operation boundary, because that is an owned contract. Everything else it replaces becomes a claim about code the test no longer exercises.

litespec/frontend/testing#keep-mocks-at-owned-boundaries

Use network mocks for component tests#

ruleguidance

Requirement A component or logic test that needs HTTP MUST serve its answers through network mocks generated from the published contract.

Rationale A mocked client proves the mock rather than the component. A network mock answers the same request the browser would send, so the generated client stays in the path it takes in production.

Example The workspace keeps one mock-generation command that regenerates them.

litespec/frontend/testing#use-network-mocks-for-component-tests

Isolate browser tests#

ruleguidance

Requirement A browser test MUST create or identify its own data, its own authentication context, and its own expected state.

Rationale A test depending on execution order or on leftover data fails for reasons unrelated to the change under review. A test spends no seeded record; it creates the record it needs.

litespec/frontend/testing#isolate-browser-tests

Prove the module mirror in a structure test#

ruleguidance

Requirement A frontend calling the API MUST hold a test asserting that its module tree resolves to the backend use-case folders and names no refused folder.

Rationale The mirror is the claim that makes a specification findable from a folder path. A check that runs on every build keeps it true when the backend adds or renames a use case.

Example The structure check resolves each folder name against the backend's Application tree.

litespec/frontend/testing#prove-the-module-mirror-in-a-structure-test

Reach every public use case from a journey#

ruleguidance

Requirement Every public use case MUST sit in a Journey or name its background caller.

Rationale A public use case no Journey reaches and no background caller names has no stated path to it. The model builder reports each one, so the gap stays visible until a Journey or a caller claims it.

litespec/frontend/testing#reach-every-public-use-case-from-a-journey

Run browser checks through the command line#

ruleguidance

Requirement A browser suite MUST run through the command line, addressable per route and per file.

Rationale The command line is the interface every runner shares: the developer, the drill, and the integration job. A check that runs only inside an editor plugin or a proxy runner cannot be repeated by the next investigator.

litespec/frontend/testing#run-browser-checks-through-the-command-line

Conventions#

Query by accessible behavior#

conventionguidance

Default Query elements by role, label, name, or visible text before reaching for a test identifier.

Replacement A project departs from this default through an Exception record.

Rationale A query by accessible name fails when the accessible name breaks, which is the behavior worth protecting. A structural floorplan marker is not an identifier, so a query may use it to scope to one floorplan. Slot content is addressed by its accessible role and name.

litespec/frontend/testing#query-by-accessible-behavior

Keep test support narrow#

conventionguidance

Default Limit test support to render helpers for required providers, to one API helper that every specification shares, and to the locator helpers.

Replacement A project departs from this default through an Exception record.

Rationale Support code that grows into a second application framework becomes its own untested surface. One API helper means a change to the actor header or the idempotency key reaches the fixtures and not every specification.

litespec/frontend/testing#keep-test-support-narrow

Read the deployment for its identifiers#

conventionguidance

Default Read a record identifier a browser test needs from the running deployment, and fall back to an absent identifier when the deployment holds none.

Replacement A project departs from this default through an Exception record.

Rationale A hard-coded identifier passes against one deployment and fails against the next. A fallback keeps the route visited as a not-found page, which is the state a reader can reach anyway.

litespec/frontend/testing#read-the-deployment-for-its-identifiers

Reference example#

This informative example demonstrates litespec/frontend/testing#match-test-level-to-risk, litespec/frontend/testing#assert-each-declared-refusal-by-its-code, and litespec/frontend/testing#isolate-browser-tests.

A withdrawal window is a pure function, so it takes a logic test beside it. A reader reaches a redelivery dialog only through a record, so it takes an evidence specification. That specification creates the order it needs and asserts the refusal by its code.

Verification#

IDMarkEvidence
litespec/frontend/testing#match-test-level-to-riskguidanceDoes each new test sit at the level its risk requires?
litespec/frontend/testing#prove-a-case-by-calling-its-use-caseguidanceDo the test's steps call the use case and assert the Result, submitting where money moves?
litespec/frontend/testing#assert-each-declared-refusal-by-its-codeguidanceDoes the specification reach each declared refusal and assert it by its Result's code?
litespec/frontend/testing#test-observable-statesguidanceDoes each test assert observable output in the states its situation reaches?
litespec/frontend/testing#keep-mocks-at-owned-boundariesguidanceIs every mock at an owned boundary, replacing no library internal or private function?
litespec/frontend/testing#use-network-mocks-for-component-testsguidanceDoes each component test needing HTTP run through contract-generated network mocks?
litespec/frontend/testing#isolate-browser-testsguidanceDoes each browser test create its own data, context, and expected state?
litespec/frontend/testing#prove-the-module-mirror-in-a-structure-testguidanceDoes the structure test resolve every module folder to a backend use-case folder?
litespec/frontend/testing#reach-every-public-use-case-from-a-journeyguidanceDoes every public use case sit in a Journey or name its background caller?
litespec/frontend/testing#run-browser-checks-through-the-command-lineguidanceDoes each browser check run through the command line per route and per file?
litespec/frontend/testing#query-by-accessible-behaviorguidanceDoes each identifier query lack a stable user-facing alternative?
litespec/frontend/testing#keep-test-support-narrowguidanceDo the helpers stay limited to providers, one API helper, and locators?
litespec/frontend/testing#read-the-deployment-for-its-identifiersguidanceIs each record identifier read from the deployment rather than written in a specification?