playwright-bdd
Integrate playwright-recast with playwright-bdd for BDD-driven demo videos.
Overview
playwright-recast has first-class support for playwright-bdd. Gherkin doc strings become voiceover narration, and step titles become subtitles -- turning your BDD scenarios into polished demo videos.
Setup
Install both packages:
npm install playwright-recast playwright-bddInitialize the helpers in your fixtures file:
// steps/fixtures.ts
import { test } from 'playwright-bdd'
import { setupRecast, narrate, pace } from 'playwright-recast'
setupRecast(test)
export { narrate, pace }setupRecast(test) connects the step helpers to the Playwright test context. Call it once before defining any steps.
Using narrate in step definitions
Call narrate(docString) in every step definition. The doc string text from your Gherkin feature file becomes the voiceover narration for that step:
// steps/dashboard.ts
import { Given, When, Then } from './fixtures'
import { narrate, pace } from 'playwright-recast'
Given('the user opens the dashboard', async ({ page }, docString?: string) => {
narrate(docString)
await page.goto('/dashboard')
await pace(page, 4000)
})
When('the user clicks the revenue chart', async ({ page }, docString?: string) => {
narrate(docString)
await page.click('.revenue-chart')
await pace(page, 3000)
})
Then('the breakdown is visible', async ({ page }, docString?: string) => {
narrate(docString)
await page.waitForSelector('.breakdown-panel')
await pace(page, 3000)
})Writing Gherkin feature files
Add doc strings to each step with the narration text:
Feature: Dashboard demo
Scenario: View analytics
Given the user opens the dashboard
"""
Let's open the analytics dashboard to see real-time metrics.
"""
When the user clicks the revenue chart
"""
Clicking on the revenue chart reveals a detailed breakdown
by month and region.
"""
Then the breakdown is visible
"""
Here we can see the full revenue breakdown with trends
highlighted for the current quarter.
"""Hiding setup steps
Steps that should not appear in the demo (login, seeding data) can be hidden:
Given('the admin is logged in', async ({ page }, docString?: string) => {
narrate(docString, { hidden: true })
await page.goto('/login')
await page.fill('#email', 'admin@example.com')
await page.fill('#password', 'password')
await page.click('button[type="submit"]')
})Or use the @hidden tag in the doc string:
Given the admin is logged in
"""
@hidden
"""Using zoom and highlight
Combine zoom and highlight with narrate for richer demos:
When('the user fills in the search', async ({ page }, docString?: string) => {
narrate(docString)
const searchInput = page.locator('[data-testid="search"]')
await zoom(searchInput, 1.4)
await searchInput.fill('quarterly report')
await highlight(searchInput, { text: 'quarterly report' })
await pace(page, 4000)
})Generating the video
Run your Playwright BDD tests with tracing enabled, then pipe the trace through the pipeline:
import { Recast, OpenAIProvider } from 'playwright-recast'
await Recast
.from('./test-results/')
.parse()
.speedUp({ duringIdle: 4.0, duringUserAction: 1.0 })
.subtitlesFromTrace()
.voiceover(OpenAIProvider({ voice: 'nova' }))
.render({ format: 'mp4', burnSubtitles: true })
.toFile('demo.mp4')Use .subtitlesFromTrace() to automatically extract narration text from the BDD step annotations recorded during the test run.