Browse docs
Screens and states
Describe each step once, then give every meaningful condition a reachable frame.
On this page
A screen is a step with evidence#
A screen describes one product surface: its stable identity, route pattern, design purpose, source files, shared components, relevant API routes, and states. Prefer a page.mockspace.mjs beside the page it describes so the metadata moves and is deleted with the page.
export const meta = {
id: "inventory",
name: "Inventory",
route: "/trips/[id]/inventory",
purpose: "Compare available options before choosing one.",
sources: ["src/app/trips/[id]/inventory/page.tsx"],
components: ["ResultCard"],
api: ["GET /api/trips/:id/inventory"],
};
export const states = [
{ id: "default", url: "/trips/trip-searched/inventory" },
{
id: "no-results",
url: "/trips/trip-searched-none/inventory",
purpose: "A completed empty search must not look like a failure.",
},
];
The default state supplies the screen URL and always appears first. Source paths are relative to the application repository. mockspace check fails when a declared source path does not exist.
A state is a condition worth comparing#
States stack vertically beneath their step. Derive them from behavior in the actual page: lifecycle enums, independent statuses, decision records, error branches, optional fields, and list sizes that change the layout.
A useful state has a reason to exist. “No results” checks the difference between an empty success and a failed search. “Dialog open” checks an overlay against the screen behind it. A second label for the same pixels is not coverage.
A frame is reachable behavior#
Every frame ultimately has a URL in the application. Prefer a real identifier in the path or another routing mechanism the app already uses. Mockspace opens that URL; it does not understand how the app selects its data.
Some UI state is not URL-addressable. For a dialog, menu, loading moment, or expanded panel, a state can declare an act sequence after loading its URL.
{
id: "edit-search-dialog",
url: "/trips/trip-searched/inventory",
act: [{ click: 'button:has-text("Edit search")' }],
expect: "[role=dialog]",
purpose: "Check the dialog against a full result list.",
}
Each action step uses exactly one of click, hover, fill, press, or wait. A fill also needs a string value. Prefer selectors based on accessible names. Use expect when opening an overlay so a click that did nothing cannot silently capture the unopened page.
Link a control to its captured state#
A static frame contains no application JavaScript. If a captured state is normally reached by a control, reachedBy can identify that control. The capture marks it so selecting the control moves the canvas to the corresponding frame instead of navigating the inert snapshot.
States captured with a click action already identify their trigger. Use reachedBy for a URL-addressable state reached by a tab, mode switch, or list row.
Validate what differs#
After capture, compare the visible result rather than only checking for HTTP 200. Client-rendered applications can return a loading shell for every state. Two states with identical rendered text or pixels usually mean the fixture selector is ignored, a fallback erased the difference, or the state should not exist.
Read Prepare states for the app-owned fixture workflow.