Specs: the English description of a test
A spec is a plain-English markdown file that describes one test scenario.
It lives in tests/specs/ and is the source of truth for everything the AI
generates. If you want a test to do something different, you update the spec —
not the generated TypeScript.
What a spec file looks like
Every spec has two parts: a YAML header (called frontmatter) and a body with named sections.
The frontmatter
The frontmatter sits between the --- fences at the very top of the file.
---
id: PROJECT-TEAM-ADD-KEY-MEMBER-001
feature: project
title: 'Case: Add Account Manager key member'
priority: high
tags: [happy-path, project-team]
fixture: projectTeamPage
data:
- team.account_manager_smith
---
| Field | What it means |
|---|---|
id | Unique identifier used by the generator and CI to track this spec. |
feature | The PACE feature area this spec belongs to (project, estimates, clients, …). |
title | Human-readable name shown in test reports. |
priority | How critical the scenario is — used to decide which tests run in a short smoke suite. |
fixture | The Playwright fixture that sets up the browser state (logs in, navigates to the right page). |
data | One or more dotted keys into tests/data/*.jsonc — the real values the test will use. |
The body sections
After the frontmatter the spec has three sections:
## Background— A sentence or two explaining what the test is proving and any setup assumptions. Written for a human reader, not the test runner.## Steps— A numbered list of actions. Each step has three parts (see below).## Expected— What the tester should observe at the end of a successful run.
Inside a step
Each numbered step has three pieces:
3. Click "Add new member" and capture the new row index
- intent: "Append a fresh row so the new key member's role + person can be set."
- method: `tp.clickAddNewMember()` then `(await grid.getRowCount()) - 1`
| Part | Purpose |
|---|---|
| prose | The human description — what a tester would say out loud while doing this manually. |
intent | The why behind the step, written in plain English. The AI uses this to write assertions. |
method | The page-object or helper calls the Generator will translate into TypeScript. |
The method: field is essentially a recipe written in pseudo-code using the
page-object API. You do not need to know TypeScript to read it — it is meant to
be self-explanatory.
A real excerpt
Here are steps 3–6 from tests/specs/project/team/add-key-member.md, the spec
that tests adding an Account Manager to a project's Key Members grid:
3. Click "Add new member" and capture the new row index
- intent: "Append a fresh row so the new key member's role + person can be set."
- method: `tp.clickAddNewMember()` then `(await grid.getRowCount()) - 1`
4. Pick the role in the new row's role dropdown
- intent: "Assign the Account Manager role — driven by data so the spec can be
re-run for other roles by swapping data refs."
- method: `grid.selectCellDropdownOption('role_id', newRowIndex, member.role_id)`
5. Pick the person in the new row's person dropdown
- intent: "Pick the specific named person for the role."
- method: `grid.selectCellDropdownOption('person_id', newRowIndex, member.person_id)`
6. Save and wait for the success toast
- intent: "Persist the change and assert the server accepted it via the toast —
a single round-trip validation."
- method: `tp.wait.expectToastAfterAction('success', () => tp.save())`
Notice that member.role_id and member.person_id are data references — not
hard-coded strings. The real values live in tests/data/team.jsonc and flow in
via the data: frontmatter key. See Test data for details.
Requesting a new page-object method
Sometimes a spec needs a page-object method that does not exist yet. You signal
this with a [NEW: ClassName.methodName()] annotation in the method: field:
2. Open the project settings panel
- intent: "Navigate to the settings tab so we can edit the project name."
- method: `[NEW: ProjectPage.openSettingsPanel()]`
When the Generator sees [NEW: ...] it reads the application source to learn the
correct selectors, writes the method, and adds it to the page object before
producing the test file.
Specs vs. generated test files
The spec (tests/specs/…/*.md) is the thing you own and maintain. The generated
test file (tests/e2e/…/*.spec.ts) is produced by the Generator agent and should
not be hand-edited — if you edit it directly, those changes will be lost the
next time the Generator runs. When you want to change a test's behaviour, update
the spec and regenerate.