Executable Stories
Open Source BDD Framework
Tests that document.
Reports that convince.
Write BDD stories inside your test framework. Generate living documentation your whole team can read.
- No Gherkin files
- No step matching
- No separate runtime

From terminal noise to team documentation
Same test run. Completely different audience.
Terminal output
Generated report
Write. Run. Share.
Three steps from test code to stakeholder-ready documentation.
story.init(task, { ticket: 'PAY-2847' });story.given('a cart with 3 items');story.when('the payment is submitted');story.then('a receipt is generated');story.table({ label: 'Receipt breakdown', columns: ['Item', 'Qty', 'Total'], rows: [['Widget Pro', '2', '178.00']],});vitest runnpx executable-stories format --html --markdown
Rich documentation from a single method call
No separate files. No templates. Every doc entry is one line in your test.

Mermaid diagrams
Sequence diagrams, flowcharts, and ER diagrams rendered live in the report.

Code blocks
Syntax-highlighted TypeScript, SQL, YAML, or any language.

Tables and JSON
Structured data, key-value pairs, and JSON objects.

Ticket linking
Connect scenarios to JIRA, Linear, or any issue tracker.

OTel traces
Waterfall view of OpenTelemetry spans with durations.

Search and filter
Full-text search, tag filters, keyboard shortcuts.
Same tests, different readers
Teach your AI agent to write documentation for your audience.
Product Owner
Feature: User Authentication
Scenario: User logs in successfully
A registered user can access their
dashboard by entering valid credentials.
Outcome: User sees personalised dashboard
Business rule: Session expires after 30 min Developer
## User logs in successfully
Given a registered user exists in the database
When POST /api/auth/login { email, password }
Then response status is 200
And body contains { token, expiresIn: 1800 }
And session is stored in Redis with TTL 1800s QA / Compliance
TC-AUTH-001: User logs in successfully
Preconditions:
- User account exists and is active
- Account is not locked (< 5 failed attempts)
Steps:
1. Submit valid credentials
2. Verify 200 response with JWT token
3. Verify session created with correct TTL
4. Verify audit log entry created
Traceability: REQ-AUTH-001, SOC2-CC6.1 Built with AI agent skills — markdown files you drop into your project. Use ours or write your own.
Compare runs across sprints
Generate diff reports to spot regressions, track fixes, and see what changed between any two test runs.
RegressedTests that started failing
FixedTests that were repaired
ChangedDuration or status shifts

6 themes, light and dark
Set with —html-theme. Every theme supports automatic dark mode.
Native to your framework
Same API across frameworks. Same report output. 10 languages supported.
import { story, doc } from 'executable-stories-vitest';
it('user logs in', ({ task }) => { const s = story.init(task); s.given('a registered user'); s.when('they submit valid credentials'); s.then('they see the dashboard'); s.json({ label: 'Session', value: { role: 'admin' } });});import { story, given, when, then } from 'executable-stories-jest';
it('user logs in', () => { story.init(); given('a registered user'); when('they submit valid credentials'); then('they see the dashboard'); story.json({ label: 'Session', value: { role: 'admin' } });});import { story, given, when, then } from 'executable-stories-playwright';
test('user logs in', async ({ page }, testInfo) => { story.init(testInfo); given('a registered user'); when('they submit valid credentials'); then('they see the dashboard');});import { story, given, when, then } from 'executable-stories-cypress';
it('user logs in', () => { story.init(); given('a registered user'); when('they submit valid credentials'); then('they see the dashboard');});Output formats
.html HTML Report.md Markdown.junit.xml JUnit XML.cucumber.json Cucumber JSON.html Cucumber HTML.ndjson Cucumber MessagesAlso available
Get started in under a minute
Install the adapter and formatter, then write your first story.
pnpm add -D executable-stories-vitest executable-stories-formattersnpm install -D executable-stories-vitest executable-stories-formattersyarn add -D executable-stories-vitest executable-stories-formattersgem install executable-stories-ruby










