Understanding the report
The reporter turns story metadata and test results into Markdown and HTML. Each scenario gets a status icon and optional headings, metadata, and source links.


Scenario status icons
Section titled “Scenario status icons”Scenarios show a status icon based on step results, with this precedence:
- ❌ — Any step failed
- ✅ — All steps passed
- 📝 — All steps are todo (or fixme on Playwright)
- ⏩ — All steps are skipped
- ⚠️ — Mixed (e.g. some passed, some skipped)
So a scenario is marked failed if any step failed, even if others passed or were skipped.
Heading levels
Section titled “Heading levels”- When scenarios are grouped by file (default), each scenario title uses heading level 3 (
###). The file group uses level 2 (##). - When grouped by none (
groupBy: "none"), scenario titles use level 2 (##).
You can override with scenarioHeadingLevel / storyHeadingLevel in reporter options (see each framework’s reference).
What appears in the report
Section titled “What appears in the report”- Scenario title — The scenario heading normally comes from the test title used with
story.init(...). If you attach explicit story metadata in a framework-native pattern, that story title is what the report renders. - Steps — Each
story.given/story.when/story.then(andstory.and,story.but) as a bullet. Framework modifiers (skip/todo/fails etc.) are reflected in the step label (e.g. (skipped), (todo)). - Planned scenarios — A bodyless
it.todo("title")in a file that contains story tests appears as a Planned scenario (Jest and Vitest): specified behavior with no implementation yet. Markdown renders the heading with (planned); the HTML report shows a Planned badge instead of Pending. - Tags and options — If you pass
{ tags: [...], meta: {...} }tostory.init(..., options), they can be included (see reporter options). - Step documentation — Notes, key-value pairs, code blocks, tables, and links added via
story.note,story.json,story.table, etc. appear under the corresponding step. - Source link — If
permalinkBaseUrlis set (or in GitHub Actions with the built-in fallback), each scenario can get a “Source: file” line. - Storyboard — When two or more steps carry a screenshot (e.g. Playwright
await story.screenshot({ page, alt })after each step) or a state snapshot, the scenario opens with a horizontal filmstrip: one frame per step, captioned with the step keyword, each linking to the full detail under its step. Nothing to configure and nothing separately authored — the storyboard is derived from the step docs, so it appears in the HTML report and on Astro story pages alike. This is the view to show product owners: Given → When → Then as pictures, data, or both.

Inline documentation renders as semantic HTML. You author tables and diagrams in your test; the report draws them, so there are no image files to keep in sync:



When a scenario fails, the report leads with the error and the step that failed:

State snapshots: storyboards for data
Section titled “State snapshots: storyboards for data”Storyboards are not screenshot-only. story.state({ label?, value }) captures
what the world looks like at a step as a JSON-serializable snapshot, and any
step carrying a state doc becomes a filmstrip frame — so API tests, domain
logic, and batch jobs get the same visual walkthrough UI code gets:
it('adding an item updates the basket total', ({ task }) => { story.init(task);
story.given('an empty basket'); const basket = createBasket(); story.state({ label: 'Basket', value: { items: [], total: 0 } });
story.when('the shopper adds a hoodie'); basket.add({ sku: 'hoodie', price: 45 }); story.state({ label: 'Basket', value: { items: [{ sku: 'hoodie', qty: 1 }], total: 45 } });
story.then('the total reflects the item price'); expect(basket.total).toBe(45);});The report renders this diff-first:
- First appearance shows the snapshot. The first
Basketframe renders the full value (collapsed behind a summary in Markdown output). - Repeats show the change. Consecutive snapshots with the same label are
diffed at render time — the second frame reads
items[0].qty: added,total: 0 → 45— so the reader sees what the step did, not two blobs to compare by eye. Diffs are derived, never stored, and never cross scenario boundaries. - Labels are lanes. Snapshot two entities (
BasketandOrder) and each label gets its own side-by-side lane in consistent order. A step can carry a screenshot and a state — the screen next to the backend record it proves.
The same frames feed the stakeholder surfaces:
- Journey pages (
journey:<id>tags) treat scenarios as chapters and show each chapter’s final state card, so a walkthrough ends every chapter with “here is what the world looked like”. - The
/statescatalog (state:<name>tags) gives non-UI scenarios data-card thumbnails, so the grid is no longer screenshots-only. One concept at two granularities: tags name states,story.state()shows them. See Tagging for your audience.
There is no size cap, but the JS adapters warn above ~100KB per snapshot.
Capture the business-relevant projection, not the ORM entity: { status, total, items } reads as documentation; forty persistence columns read as
noise. Done well, this makes the generated site the document of record for
how a feature behaves — the thing teams otherwise hand-maintain in Confluence
or TestRail — for backend behaviour as much as UI flows.
Run history in the interactive report
Section titled “Run history in the interactive report”When the CLI runs with --history-file (or the reporter is configured with history.filePath), the interactive HTML report layers run-over-run context on top of the current results:
- Per-scenario timeline — a dot per recent run on each scenario card (oldest → newest), with a tooltip summary like “8/10 runs passed · Passing for the last 5 runs”.
- Flaky badge — scenarios whose recent runs flip between pass and fail get a Flaky badge next to the timeline, so an unreliable scenario can’t hide behind a green run.
- “Since last run” strip — one line in the report header summarizing what changed against the previous run: newly failing scenarios (deep-linked), fixed scenarios, and first-seen scenarios. A quiet run says “no behavior changes” rather than nothing.
All of this is presentation-layer data derived from the history store; the StoryReport JSON contract is unchanged. See Run history in the CLI reference.
Enabling or hiding elements
Section titled “Enabling or hiding elements”Reporter options (under markdown in framework reporter config) control what’s included:
- Status icons:
includeStatusIcons: true(default) — show ✅❌⏩ etc. for scenario status. - Errors in Markdown:
includeErrors: true(default) — include failure messages for failed scenarios. - Summary table:
includeSummaryTable: trueto add a table with start time, duration, and counts. - Metadata block:
includeMetadata,metadata.date,metadata.packageVersion,metadata.gitSha. - Source links:
includeSourceLinks: trueandpermalinkBaseUrl(or rely on GitHub Actions fallback).
See Vitest reporter options, Jest reporter options, and Playwright reporter options for the full list.