Write a spec by hand
The recording-based and scenario-based paths handle most situations. But sometimes you want to write the spec yourself — to model a complex multi-step flow precisely, to document an existing manual test, or simply to understand the format deeply before trusting the AI to fill it in.
This guide walks you through writing a spec file from scratch, validating it, and then handing it to the Generator to produce the runnable TypeScript.
The spec format
A spec is a plain Markdown file in tests/specs/<feature>/. It has two parts:
a YAML frontmatter block and a body with three named sections.
For the full format reference, see Specs: the English description of a test.
Frontmatter
---
id: FEATURE-AREA-SHORT-DESCRIPTION-001
feature: project
title: 'Case: Short human-readable title'
priority: high
tags: [happy-path, feature-tag]
fixture: featurePageFixture
data:
- data.key_reference
---
| Field | What to put here |
|---|---|
id | Unique ID in SCREAMING-KEBAB form. Use the feature area as a prefix. |
feature | The PACE feature area (project, estimates, deliverables, …). |
title | A short label shown in test reports. |
priority | high, medium, or low. High-priority tests run in the smoke suite. |
fixture | The Playwright fixture that logs in and navigates to the right page. Check tests/e2e/fixtures/ for available fixtures. |
data | Dotted keys into tests/data/*.jsonc. Run pnpm run test:validate-data to confirm the keys exist. |
Body sections
After the frontmatter, add three sections exactly as shown:
## Background
One or two sentences explaining what this test is proving and any assumptions about
the starting state (e.g. "the project already exists", "the user is logged in as a
project manager").
## Steps
1. Navigate to the Deliverables tab on project 5192
- intent: "Start on the correct tab so subsequent actions land on the right grid."
- method: `projectPage.openDeliverables(data.project_number)`
2. Click the '+New Deliverable' button
- intent: "Open the create-deliverable form."
- method: `deliverableGrid.clickNew()`
3. Fill in the deliverable number and name
- intent: "Provide the minimum required fields to enable save."
- method: `deliverableForm.fill({ number: data.deliverable.number, name: data.deliverable.name })`
4. Click 'Create and go to deliverables'
- intent: "Submit the form and return to the grid."
- method: `deliverableForm.submit()`
5. Assert the new row appears with status 'Draft'
- intent: "Confirm the server accepted the record and the UI reflects the initial status."
- method: `deliverableGrid.expectRowWithStatus(data.deliverable.name, 'Draft')`
## Expected
The deliverables grid shows the newly created row. The Status column reads 'Draft'.
Key rules for Steps:
- One logical action or assertion per numbered item.
- Every step must have an
intent:(the why) and amethod:(the page-object calls to execute). - If the page-object method does not exist yet, annotate it:
method: [NEW: DeliverableGrid.expectRowWithStatus()]. The Generator will write the method for you.
Validate the spec
Before generating a test, run the spec validator to catch schema errors early:
pnpm run test:validate-spec
The validator checks every file in tests/specs/ against the Zod schema. It reports
missing fields, wrong types, and malformed step syntax. Fix all errors before
proceeding — the Generator will fail on an invalid spec.
Generate the TypeScript
Once the spec is valid, hand it to the Generator:
- Claude Code
- Codex CLI
- Gemini CLI
- Antigravity
/generate-test
Run generate-test
/generate-test
/generate-test
The AI will prompt you for (or infer from context) the path to your spec file. The
Generator reads the spec, translates each method: line into TypeScript, writes any
[NEW: …] page-object methods, and smoke-runs the result.
For the full command reference, see /generate-test.
Review and commit
After generation:
- Read the produced
.spec.tsfile intests/e2e/<feature>/— verify the selectors and assertions look correct. - Run the test:
pnpm run test --grep "<your spec title or id>"
- If the test is green, commit the spec and the generated file:
git add tests/specs/<feature>/<name>.md \tests/e2e/<feature>/<name>.spec.tsgit commit -m "test: add <description> e2e test"
Remember: edit the spec, not the generated TypeScript. If you need to change what
the test does, update tests/specs/…/<name>.md and re-run /generate-test. Hand-edits
to the .spec.ts file will be overwritten the next time the Generator runs.