This package allows to run CucumberJS BDD tests with Playwright test runner.
Inspired by issue in Playwright repo microsoft/playwright#11975
- Why Playwright runner
- How it works
- Installation
- Usage
- World
- Custom World
- Examples
- Debugging
- VS Code Integration
- Limitations
- Changelog
- Feedback
- License
Both Playwright and Cucumber have their own test runners. You can use Cucumber runner with Playwright included as a library. Alternative way (provided by this package) is to convert BDD scenarios into Playwright tests and run them using Playwright runner. It gives the following benefits:
- Automatic browser initialization and cleanup
- Usage of Playwright fixtures instead of
before / after
hooks - Parallelize tests with sharding
- ...lot more
There are 2 phases:
CLI command bddgen
reads Cucumber config and converts features into Playwright test files in .features-gen
directory
Example of generated test
From
Feature: Playwright site
Scenario: Check title
Given I open url "https://playwright.dev"
When I click link "Get started"
Then I see in title "Playwright"
To
import { test } from 'playwright-bdd';
test.describe('Playwright site', () => {
test('Check title', async ({ Given, When, Then }) => {
await Given('I open url "https://playwright.dev"');
await When('I click link "Get started"');
await Then('I see in title "Playwright"');
});
});
Playwright runner takes generated test files and runs them as usual. For each test playwright-bdd
creates isolated Cucumber World with injected Playwright fixtures (page
, browser
, etc). It allows to write step definitions using Playwright API:
Example of step definition
import { expect } from '@playwright/test';
import { Given, When, Then } from '@cucumber/cucumber';
import { World } from 'playwright-bdd';
Given('I open url {string}', async function (this: World, url: string) {
await this.page.goto(url);
});
When('I click link {string}', async function (this: World, name: string) {
await this.page.getByRole('link', { name }).click();
});
Then('I see in title {string}', async function (this: World, text: string) {
await expect(this.page).toHaveTitle(new RegExp(text));
});
Run BDD tests in one command:
npx bddgen && npx playwright test
Install from npm:
npm i -D playwright-bdd
This package uses @playwright/test
and @cucumber/cucumber
as peer dependencies, so you may need to install them as well:
npm i -D @playwright/test @cucumber/cucumber
After installing Playwright you may need to install browsers:
npx playwright install
-
Create Cucumber config file
cucumber.cjs
:module.exports = { default: { paths: [ 'features/**/*.feature' ], require: [ 'features/steps/**/*.{ts,js}' ], // uncomment if using TypeScript // requireModule: ['ts-node/register'], publishQuiet: true, }, };
Or in ESM format
cucumber.mjs
:export default { paths: [ 'features/**/*.feature' ], import: [ 'features/steps/**/*.{ts,js}' ], // uncomment if using TypeScript // requireModule: ['ts-node/register'], publishQuiet: true, };
-
Create Playwright config file
playwright.config.ts
. SettestDir
pointing to.features-gen
directory. That directory does not exist yet but will be created during tests generation:import { defineConfig } from '@playwright/test'; export default defineConfig({ testDir: '.features-gen', // <- generated BDD tests projects: [{ name: 'e2e' }], });
-
Create feature descriptions in
features/*.feature
files:Feature: Playwright site Scenario: Check title Given I open url "https://playwright.dev" When I click link "Get started" Then I see in title "Playwright"
-
Create step definitions in
features/steps/*.{ts,js}
files. UseWorld
fromplaywright-bdd
:import { expect } from '@playwright/test'; import { Given, When, Then } from '@cucumber/cucumber'; import { World } from 'playwright-bdd'; Given('I open url {string}', async function (this: World, url: string) { await this.page.goto(url); }); When('I click link {string}', async function (this: World, name: string) { await this.page.getByRole('link', { name }).click(); }); Then('I see in title {string}', async function (this: World, keyword: string) { await expect(this.page).toHaveTitle(new RegExp(keyword)); });
-
Run command to generate and execute tests:
npx bddgen && npx playwright test
Output:
Running 1 test using 1 worker 1 passed (2.0s) To open last HTML report run: npx playwright show-report
Playwright-bdd extends Cucumber World with Playwright built-in fixtures and testInfo. Just use this.page
or this.testInfo
in step definitions:
import { Given, When, Then } from '@cucumber/cucumber';
Given('I open url {string}', async function (url) {
await this.page.goto(url);
});
In TypeScript you should import World
from playwright-bdd
for propper typing:
import { Given, When, Then } from '@cucumber/cucumber';
import { World } from 'playwright-bdd';
Given('I open url {string}', async function (this: World, url: string) {
await this.page.goto(url);
});
Check out all available props of World.
To use Custom World you should inherit it from playwright-bdd World
and pass to Cucumber's setWorldConstructor
:
import { setWorldConstructor } from '@cucumber/cucumber';
import { World, WorldOptions } from 'playwright-bdd';
export class CustomWorld extends World {
myBaseUrl: string;
constructor(options: WorldOptions) {
super(options);
this.myBaseUrl = 'https://playwright.dev';
}
async init() {
await this.page.goto(this.myBaseUrl);
}
}
setWorldConstructor(CustomWorld);
Perform asynchronous setup and teardown before each test with
init()
/destroy()
methods.
There several working examples depending on your project setup (ESM/CJS and TS/JS):
You can debug tests as usual with --debug
flag:
npx bddgen && npx playwright test --debug
-
Playwright extension works as usual. You can run/debug tests in
.features-gen
directory: -
Cucumber autocompletion works as usual:
Currently there are some limitations:
- Cucumber tags not supported yet (wip, #8)
- Cucumber hooks do not run. (use Playwright hooks instead?)
- Support Gherkin i18n #13
- Print parsing errors to the console while generating #2
- Initial public release
Feel free to share your feedback in issues.
MIT