Integrations

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-bdd

Initialize 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 that rely on narration, zoom, highlight, click, or narration-boundary markers.

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)
})

Polished clicks and narration beats

Swap page.click() for the click() helper on the interactions you want to emphasise — the renderer gives those a deliberate, held cursor approach. Use waitForNarration() to hold the video until a line finishes speaking before the next step runs:

import { narrate, click, waitForNarration, pace } from 'playwright-recast'

When('the user opens the report', async ({ page }, docString?: string) => {
  await narrate(docString)
  await click(page.getByRole('link', { name: 'Reports' }))
  await waitForNarration()   // let this line finish before moving on
})

Render with .cursorOverlay() and/or .clickEffect() so the marked clicks are drawn; tune the held approach with .cursorOverlay({ approachMs: 600 }).

With a .voiceover() stage you don't need autoWait or pace() for timing: the rendered video freezes at each waitForNarration() for exactly as long as the line needs, so the test can run at full speed and the narration still stays in sync. See Pacing narration without autoWait.

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.

On this page