Playwright and Cucumber Automation Tutorial (2026)

Learn how Playwright and Cucumber work together for BDD testing with setup steps, code examples, reports, and best practices.

Written by Rushabh Shroff Rushabh Shroff
Reviewed by Bhumika Babbar Bhumika Babbar
Last updated: 16 January 2026 18 min read

Playwright and Cucumber Automation Tutorial (2026)

Playwright, now adopted by over 45% of testing teams worldwide, offers fast, reliable, cross-browser automation. Cucumber adds a Behavior-Driven Development layer that lets teams write test scenarios in plain Gherkin syntax.

Together, they help teams build tests that are both executable and readable. The feature file explains the expected behavior in simple language. The step definition uses Playwright to perform the browser actions behind that behavior.

In this article, I’ll explain how to integrate Playwright with Cucumber, set up a clean project structure, write feature files, create step definitions, configure hooks, and generate reports.

Why Integrate Playwright with Cucumber?

Playwright and Cucumber solve different problems in test automation.

Playwright handles browser automation. It opens pages, clicks elements, fills forms, checks UI behavior, manages browser contexts, captures traces, and runs tests across Chromium, Firefox, and WebKit. Cucumber adds a behavior-driven layer on top of that by letting teams write test scenarios in Gherkin syntax.

When both are used together, the framework becomes easier to read for business users and still powerful enough for technical testers.

For example, a Cucumber scenario can describe a login flow in plain language:

Scenario: User logs in with valid credentials

 Given the user is on the login page

 When the user enters valid credentials

 Then the user should be redirected to the dashboard

The Gherkin scenario explains the expected behavior. The Playwright step definitions perform the actual browser actions behind each step.

This separation helps teams in a few practical ways:

  • Better readability: Test scenarios are written in plain language, which makes them easier for non-technical stakeholders to review.
  • Cleaner test structure: Business behavior stays in feature files, while browser automation logic stays in step definition files.
  • Reusable steps: Common steps such as login, navigation, search, and checkout can be reused across multiple scenarios.
  • Stronger collaboration: QA, development, and product teams can discuss scenarios before automation is written.
  • Cross-browser coverage: Playwright can run the same BDD scenarios across different browsers and environments.
  • Better debugging: Playwright screenshots and logs can be connected with Cucumber reports. Teams can also add traces and videos later when deeper debugging is needed.

Step-by-Step Guide to Set Up Playwright with Cucumber

Before writing feature files or step definitions, set up the project in a way that keeps test behavior, browser actions, and support logic separate. This matters because Playwright Cucumber frameworks can become hard to maintain when all code is placed inside one step definition file.

A clean structure makes it easier to reuse steps, manage browser setup, capture screenshots, add reports, and scale the framework later.

Prerequisites

Before starting, make sure you have:

  • Node.js installed
  • npm installed with Node.js
  • Visual Studio Code or any preferred code editor
  • Basic understanding of JavaScript
  • Basic understanding of Gherkin syntax such as Given, When, and Then

Recommended Project Structure

Use a structure like this:

Recommended Project Structure

Here is what each part does:

  • features contains Cucumber feature files written in Gherkin syntax.
  • steps contains Playwright code that maps to each Gherkin step.
  • support contains shared setup logic such as browser launch, page creation, teardown, screenshots, and hooks.
  • cucumber.json stores the Cucumber configuration.
  • package.json stores project dependencies and test scripts.

Keeping this separation helps avoid large, hard-to-read step files. Feature files stay focused on business behavior, while Playwright code stays inside step definitions and support files.

Step 1: Create a New Project Folder

Create a new folder for the project. For example:

mkdir PlaywrightCucumberDemo

cd PlaywrightCucumberDemo

Then initialize a Node.js project:

npm init -y

This creates a package.json file, which will track the dependencies and scripts used in the framework.

Create a New Project Folder

Step 2: Install Playwright and Cucumber

Install Playwright and Cucumber as development dependencies:

npm i -D @playwright/test @cucumber/cucumber

Then install the required Playwright browsers:

npx playwright install

This installs the browser binaries required for running Playwright tests across supported browsers.

Install Playwright and Cucumber

Step 3: Configure Cucumber

After installing Playwright and Cucumber, the next step is to configure Cucumber so it knows where to find feature files, step definitions, hooks, and reports.

Create a cucumber.json file in the root folder of the project.

For this project, the file should be placed here:

PlaywrightCucumberDemo/

 cucumber.json

Add the following configuration:

{

 "default": {

   "paths": [

     "tests/features/**/*.feature"

   ],

   "require": [

     "tests/steps/**/*.js",

     "tests/support/**/*.js"

   ],

   "formatOptions": {

     "snippetInterface": "async-await"

   },

   "format": [

     "progress-bar",

     "summary",

     "html:cucumber-report.html",

     "json:cucumber-report.json"

   ]

 }

}

Cucumber.js looks for configuration files such as cucumber.json in the project root, and it supports format options like HTML and JSON reports through the format configuration.

Here is what this configuration does:

  • paths tells Cucumber where the .feature files are stored.
  • require loads both step definition files and support files.
  • formatOptions controls how Cucumber generates step snippets.
  • format defines the report outputs shown in the terminal and saved as files.

This setup is better than placing everything in one file because the framework keeps feature files, browser logic, and setup logic separate.

Step 4: Write Cucumber Feature Files

A Cucumber test usually has two main parts:

  • A feature file, which describes the scenario in Gherkin syntax.
  • A step definition file, which contains the Playwright code for each Gherkin step.

Create a file named homepage.feature inside the tests/features folder:

tests/features/homepage.feature

Add the following scenarios:

Feature: Todo App Functionality

Scenario: Add a new todo item

Given the user is on the Todo app page

When the user adds "Buy groceries" to the todo list

Then the todo list should show "Buy groceries"

Scenario: Mark a todo item as completed

Given the user is on the Todo app page

When the user adds "Complete Playwright setup" to the todo list

And the user marks "Complete Playwright setup" as completed

Then "Complete Playwright setup" should be marked as completed

The feature file should describe user behavior, not browser implementation details. Avoid steps such as “click the button with aria-label Products” in the feature file. That detail belongs inside the step definition.

Write Cucumber Feature Files

Step 5: Configure Playwright Hooks for Cucumber

Next, create a hook file to manage browser setup and cleanup.

Create a file named hooks.js inside the tests/support folder.

tests/support/hooks.js

Add the following code:

const { Before, After, Status, setDefaultTimeout } = require("@cucumber/cucumber");

const { chromium } = require("@playwright/test");

setDefaultTimeout(60 * 1000);

Before(async function () {

 this.browser = await chromium.launch({ headless: false });

 this.context = await this.browser.newContext();

 this.page = await this.context.newPage();

});

After(async function (scenario) {

 if (scenario.result?.status === Status.FAILED && this.page) {

   const screenshot = await this.page.screenshot({ fullPage: true });

   await this.attach(screenshot, "image/png");

 }

 await this.page?.close();

 await this.context?.close();

 await this.browser?.close();

});

The Before hook runs before each scenario. It launches the browser, creates a new browser context, and opens a new page.

The After hook runs after each scenario. If the scenario fails, the After hook captures a screenshot and attaches it to the Cucumber result output. When the HTML formatter is enabled, the screenshot becomes available in the generated report.

This approach keeps browser setup outside the step definition file. It also gives each scenario a fresh browser context, which reduces test dependency between scenarios.

Step 6: Create Step Definitions

Create a file named homepage.step.js inside the tests/steps folder:

tests/steps/homepage.step.js

Add the following implementation:

const { Given, When, Then } = require("@cucumber/cucumber");

const { expect } = require("@playwright/test");


Given("the user is on the Todo app page", async function () {

  await this.page.goto("https://demo.playwright.dev/todomvc");

});


When("the user adds {string} to the todo list", async function (todoText) {

  await this.page.getByPlaceholder("What needs to be done?").fill(todoText);

  await this.page.keyboard.press("Enter");

});


Then("the todo list should show {string}", async function (todoText) {

  await expect(this.page.getByText(todoText)).toBeVisible();

});


When("the user marks {string} as completed", async function (todoText) {

  const todoItem = this.page.locator(".todo-list li").filter({

    hasText: todoText

  });

  await todoItem.getByRole("checkbox").check();

});

Then("{string} should be marked as completed", async function (todoText) {

  const todoItem = this.page.locator(".todo-list li").filter({

    hasText: todoText

  });

  await expect(todoItem).toHaveClass(/completed/);

});

In this code, each Gherkin step is mapped to a Playwright action or assertion.

The page object is accessed through this.page. This page is created in the Before hook and shared with the step definition through Cucumber’s scenario context. This avoids global page variables and makes the framework easier to extend later.

The step definitions use regular async function () syntax instead of arrow functions. This matters because Cucumber binds scenario context to this. If arrow functions are used here, this.page will not work as expected.

In the above example, the feature file defines two Todo app scenarios in plain language. The step definition file contains the Playwright actions for adding and completing todo items. The hook file manages browser setup, screenshot capture, and cleanup.

This separation is important in a Playwright Cucumber framework. Feature files should stay readable for QA leads, product owners, and other stakeholders. Step definitions should contain only the automation logic needed to execute those steps. Hooks should handle common setup and teardown tasks that apply to every scenario.

Execute Playwright Cucumber Tests

After the feature file, step definitions, and hooks are ready, run the test suite from the project root.

Use the following command:

npx cucumber-js

This command reads the cucumber.json configuration file, finds the feature files from tests/features, loads the step definitions from tests/steps, loads the hooks from tests/support, and then executes the scenarios.

You can also add the command inside package.json to make test execution easier.

{

 "scripts": {

   "test": "cucumber-js"

 }

}

Now run the tests using:

npm test

When the test starts, Cucumber executes each scenario step by step. For every scenario, the Before hook launches a browser and creates a new page. After the scenario finishes, the After hook closes the page, context, and browser.

If a scenario fails, the hook captures a screenshot and attaches it to the report.

A successful run should show progress in the terminal and generate report files in the project root.

cucumber-report.html

cucumber-report.json

This works because the format field in cucumber.json already includes HTML and JSON report output. Cucumber.js supports formatter options through configuration and command-line usage.

Execute Playwright Cucumber Tests

Run Specific Scenarios with Tags

As the test suite grows, you may not want to run every scenario each time. Cucumber tags help you group and execute selected tests.

Update the feature file like this:

Feature: Todo App Functionality

  @smoke

  Scenario: Add a new todo item

    Given the user is on the Todo app page

    When the user adds "Buy groceries" to the todo list

    Then the todo list should show "Buy groceries"


  @regression

  Scenario: Mark a todo item as completed

    Given the user is on the Todo app page

    When the user adds "Complete Playwright setup" to the todo list

    And the user marks "Complete Playwright setup" as completed

    Then "Complete Playwright setup" should be marked as completed

Feature file with tags

Run only smoke tests:

npx cucumber-js --tags "@smoke"

SMOKE TEST

Given the user is on the Todo app page

todo list should show Buy groceries

Run only regression tests:

npx cucumber-js --tags "@regression"

Regression Test

When the user adds Complete Playwright setup to the todo list

And the user marks Complete Playwright setup as completed

Then Complete Playwright setup should be marked as completed

Tags are useful in CI pipelines because teams can run a smaller smoke suite on every pull request and run the larger regression suite before release.

Validate Step Mapping Before Running Full Tests

Before running the full browser test suite, you can check whether all Gherkin steps are mapped correctly to step definitions.

Use:

npx cucumber-js --dry-run

In dry-run mode, Cucumber loads the feature files and support code but does not execute the test steps or hooks. This helps identify undefined or ambiguous steps before opening the browser. Cucumber.js documents –dry-run for this exact validation flow.

This is useful when multiple people are adding feature files. A dry run quickly tells you whether the plain-language steps already have matching automation code.

Generate and Read Cucumber Reports

The earlier cucumber.json configuration generates two reports:

cucumber-report.html

cucumber-report.json

The HTML report is useful for manual review. It shows the executed features, scenarios, steps, status, and attached screenshots for failed scenarios.

The JSON report is useful when the result needs to be processed by another reporting tool, dashboard, or CI system.

For example, after execution, open the HTML report from the project root:

cucumber-report.html

A report is more useful when it gives enough failure context. That is why the After hook added earlier captures a screenshot when a scenario fails.

HTML

In a real test pipeline, this helps the tester understand:

  • which scenario failed
  • which step failed
  • what the browser looked like at the time of failure
  • whether the issue was caused by UI behavior, test data, timing, or a locator problem

Do not treat reports as only a pass or fail summary. For a Playwright Cucumber framework, reports should support debugging. Screenshots, scenario names, tags, and clean step names make the report easier to use during defect triage.

Failed Test Error in HTML Report

JSON Test Report

Optional: Retry Failed Scenarios

Retries can help with temporary failures caused by network delay, test data dependency, or unstable environments. Use them carefully because retries should not hide real product issues.

To retry failed scenarios once, use:

npx cucumber-js --retry 1

Cucumber.js retry behavior applies to failing scenarios. When a scenario is retried, its hooks and steps run again from the start with a fresh World.

Retries should be used with failure evidence. If a retry passes, the team should still review why the first attempt failed. Otherwise, flaky tests will remain hidden until they start failing more often.

Retry Failed Scenarios

Common Issues and Fixes in Playwright Cucumber Setup

Even when the setup looks simple, Playwright Cucumber projects can fail because of small configuration or structure mistakes. These issues usually happen when Cucumber cannot find the right files, steps do not match, or browser objects are not shared correctly between hooks and step definitions.

Here are the common problems to check.

1. Cucumber Does Not Find Feature Files

This usually happens when the paths value in cucumber.json does not match the actual folder structure.

Check that your feature file exists here:

tests/features/homepage.feature

And confirm that cucumber.json has the correct path:

"paths": [

 "tests/features/**/*.feature"

]

The **/*.feature pattern helps Cucumber find feature files even when you add more folders later.

Cucumber Does Not Find Feature Files

2. Step Definitions Are Not Detected

If Cucumber shows undefined step errors, it means the Gherkin steps are not matching any step definition.

First, confirm that the step definition file exists here:

tests/steps/homepage.step.js

Then confirm that cucumber.json loads the step files:

"require": [

 "tests/steps/**/*.js",

 "tests/support/**/*.js"

]

Also check that the step text matches exactly.

For example, this Gherkin step:

Given the user is on the Todo app page

Must match this step definition:

Given("the user is on the Todo app page", async function () {

 await this.page.goto("https://demo.playwright.dev/todomvc");

});

Even small wording changes can cause undefined step errors.

Step Definitions Are Not Detected

3. this.page Is Undefined

This is one of the most common issues in a Playwright Cucumber setup.

It usually happens for one of these reasons:

  • The hook file is not loaded in cucumber.json.
  • The step definition uses an arrow function.
  • The page object is not created inside the Before hook.

Use regular async function () syntax in step definitions:

Given("the user is on the Todo app page", async function () {

  await this.page.goto("https://demo.playwright.dev/todomvc");

});

Do not use an arrow function when accessing this.page:

Given("the user is on the Todo app page", async () => {

  await this.page.goto("https://demo.playwright.dev/todomvc");

});

Arrow functions do not bind Cucumber’s scenario context. Because of that, this.page will not point to the page created in the hook.

this.page Is Undefined

4. Assertion Fails Before the UI Is Ready

This can happen when the scenario fails early or when there is no wait before an assertion.

Playwright already has auto-waiting, but you should still write assertions that wait for the expected UI state.

Use Playwright assertions like this:

await expect(

 this.page.getByText("Buy groceries")

).toBeVisible();

Avoid checking visibility with a simple boolean unless you have a specific reason:

const isVisible = await this.page.getByText("Buy groceries").isVisible();

expect(isVisible).toBeTruthy();

The first approach is better because Playwright waits until the condition is met or the timeout is reached.

Assertion Fails Before the UI Is Ready

5. Scenario Passes Locally but Fails in CI

Local machines and CI environments are different. A test may pass locally because the browser is visible, the network is faster, or the system has more resources. In CI, the same test may fail due to timing, browser dependencies, or environment differences.

Before blaming the application, check these areas:

  • Run browsers in headless mode in CI.
  • Use stable locators such as roles, labels, and test IDs.
  • Avoid fixed waits such as waitForTimeout.
  • Capture screenshots on failure.
  • Use traces or logs for deeper debugging.
  • Keep test data stable across environments.

For CI, update the browser launch setting in hooks.js:

this.browser = await chromium.launch({ headless: true });

For local debugging, you can keep it as:

this.browser = await chromium.launch({ headless: false });

A better approach is to control this through an environment variable later, but for this basic framework, the above setup is enough.

Scenario Passes Locally but Fails in CI

Conclusion

Integrating Playwright with Cucumber helps teams build browser automation tests that are readable, maintainable, and easier to review. Playwright handles browser actions, waits, assertions, and cross-browser execution. Cucumber keeps scenarios clear through Gherkin syntax.

To get the best results, keep feature files focused on user behavior, not implementation details. Use step definitions for Playwright actions, hooks for browser setup and cleanup, and reports for debugging failed scenarios. Avoid fragile selectors, fixed waits, and oversized step files, as these can make the framework harder to maintain over time.

When the framework is structured this way, teams can keep BDD scenarios readable while still using Playwright’s speed, assertions, browser contexts, and debugging features effectively.

Tags
Automation Frameworks Automation Testing Playwright
Rushabh Shroff
Rushabh Shroff

Lead - Customer Engineer

I work on browser automation frameworks, BDD-style test suites, flaky test debugging, and cross-browser test pipelines across local, CI, and cloud environments.

Playwright Cucumber tests failing often?
Debug faster with logs, videos, screenshots, and real browser test sessions