Cucumber with Playwright

Playwright has powerful features for test automation, but currently lacks native Cucumber integration. Can we get the best of both worlds?

There are a few options out there, but the one I prefer is playwright-bdd. This plugin allows you to keep the awesome features of playwright in the forefront and translates the cucumber language behind the scenes.

Simply write your features and steps as you would normally with cucumber then run npx bddgen before your playwright testing to translate everything.

The plugin supports playwright fixtures (recommended) or cucumber style hooks to cover everything you need for set up and tear down.

To set up, first install your playwright packages, then playwright-bdd

npm i -D playwright-bdd

Set up your config to use the features/steps

import { defineConfig, devices } from '@playwright/test'
import { defineBddConfig } from 'playwright-bdd'
import 'dotenv/config'

const testDir = defineBddConfig({
  features: ['feature/*.feature'],
  steps: ['steps/**/*.ts'],
  importTestFrom: 'fixtures.ts'
  // ...other playwright-bdd options
})

export default defineConfig({
  testDir,
  outputDir: './screenshots',
  fullyParallel: false,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : 4,
  reporter: process.env.CI ? 'dot' : [['list']],
  use: {
    trace: 'retain-on-failure',
    screenshot: 'only-on-failure'
  },
  timeout: 60_000,
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'], viewport: { width: 1920, height: 1080 } }
    }
})

You can then set up your fixtures

// fixtures.ts
import { test as base, createBdd } from 'playwright-bdd';

export const test = base.extend({
  myFixture: // ... define your fixture here
});

export const { Given, When, Then } = createBdd(test);

And use in the steps files

import { expect, type Locator } from '@playwright/test'
import { Given, When, Then } from '../fixtures'

When('user navigates to test run page', async ({ leftSideMenu, testRunsPage }) => {
  await leftSideMenu.testRunsTab.click()
  await testRunsPage.firstTestRun.waitFor()
})

When('user clicks on first test run', async ({ testRunsPage }) => {
  await testRunsPage.firstTestRun.click()
})

Enjoy still being able to use Playwright's reporting, trace viewer, and other great features