Playwright BDD Cucumber Tutorial: Complete Guide with TypeScript

Introduction

Modern QA teams need more than browser automation scripts. They also need tests that developers, testers, product owners, and business stakeholders can understand.

This is where Behavior Driven Development (BDD) and Cucumber become useful.

In this playwright bdd cucumber tutorial, you will learn how to combine Playwright browser automation with Cucumber and Gherkin to build a maintainable BDD automation framework using TypeScript.

A typical architecture looks like:

Gherkin Feature

      ↓

Step Definitions

      ↓

Page Objects

      ↓

Playwright

      ↓

Web Application

Playwright provides browser automation, while Cucumber provides the BDD layer for writing executable specifications in Gherkin.

For teams that already use Playwright Automation Testing, Cucumber can add a business-readable layer on top of the automation framework.

This guide covers project setup, feature files, step definitions, hooks, Scenario Outlines, Page Object Model, test data, screenshots, traces, reporting, parallel execution, CI/CD, debugging, and interview preparation.


What Is Playwright BDD Cucumber?

Playwright BDD Cucumber is an automation approach that combines:

For example:

Feature: Login

  Scenario: Successful login

    Given I am on the login page

    When I login with valid credentials

    Then I should see the dashboard

The business-readable scenario is connected to TypeScript step definitions:

Given(‘I am on the login page’, async function () {

  await this.page.goto(‘/login’);

});

This makes the test understandable without requiring every stakeholder to know TypeScript.


BDD, Cucumber, Gherkin, and Playwright Explained

These technologies have different responsibilities.

TechnologyResponsibility
BDDDevelopment/testing approach focused on behavior
CucumberFramework that executes Gherkin scenarios
GherkinHuman-readable syntax
PlaywrightBrowser automation
TypeScriptProgramming language
Page Object ModelAutomation design pattern

What is BDD?

BDD focuses on application behavior.

Instead of describing implementation details, you describe expected behavior.

For example:

Given a customer is logged in

When the customer adds a product

Then the product should appear in the cart

What is Cucumber?

Cucumber connects Gherkin scenarios with executable step definitions.

What is Gherkin?

Gherkin uses keywords such as:

Feature

Scenario

Given

When

Then

And

But

Background

Scenario Outline

Examples

What is Playwright?

Playwright is the browser automation layer.

It interacts with:

The official Cucumber JavaScript documentation provides the Cucumber.js runtime and configuration concepts used for TypeScript-based Cucumber projects.


BDD vs TDD vs Traditional Automation

These approaches are related but different.

ApproachPrimary focus
TDDTest-driven implementation
BDDApplication behavior
Traditional automationAutomated verification
CucumberExecutable BDD specifications
PlaywrightBrowser/API automation

BDD is particularly useful when QA, development, and business teams need a shared language.

However, not every Playwright project needs Cucumber.

For a small technical automation suite, native Playwright Test can be simpler.

Cucumber becomes more valuable when executable specifications and stakeholder-readable scenarios are important.


Why Use Cucumber with Playwright?

Combining Cucumber and Playwright provides several advantages.

1. Business-readable tests

When I search for “laptop”

Then I should see laptop products

is easier for non-developers to understand than raw automation code.

2. Reusable step definitions

A step can be reused across multiple feature files.

3. Scenario Outlines

The same scenario can run with different data.

4. Clear separation

Feature

Steps

Page Object

Application

5. Useful reporting

Cucumber reports can show:

  • Features
  • Scenarios
  • Steps
  • Pass/fail status
  • Execution duration

Playwright BDD Cucumber Project Setup

Create a project:

npm init -y

Install Playwright:

npm install -D @playwright/test

npx playwright install

Install Cucumber:

npm install -D @cucumber/cucumber

Install TypeScript tooling:

npm install -D typescript ts-node

A practical structure is:

playwright-cucumber/

├── features/

│   ├── login.feature

│   └── ecommerce.feature

├── step-definitions/

│   ├── login.steps.ts

│   └── ecommerce.steps.ts

├── pages/

│   ├── LoginPage.ts

│   ├── ProductPage.ts

│   ├── CartPage.ts

│   └── CheckoutPage.ts

├── support/

│   ├── world.ts

│   └── hooks.ts

├── test-data/

│   └── users.json

├── cucumber.js

├── tsconfig.json

└── package.json


Installing Playwright, Cucumber, and TypeScript Dependencies

A useful package.json script is:

{

  “scripts”: {

    “bdd”: “cucumber-js –require-module ts-node/register –require \”step-definitions/**/*.ts\” –require \”support/**/*.ts\” \”features/**/*.feature\””

  }

}

Then run:

npm run bdd

The exact Cucumber configuration can vary by project and Cucumber.js version, so keep your configuration aligned with the installed package version.


Creating the First Gherkin Feature File

Create:

features/login.feature

Add:

Feature: User Login

  As a registered customer

  I want to log in

  So that I can access my account

  Scenario: Successful login

    Given I am on the login page

    When I enter valid login credentials

    And I click the login button

    Then I should see the dashboard

This is a Playwright Gherkin example.

Notice that there is no TypeScript code in the feature file.

The feature describes behavior.


Writing Cucumber Step Definitions with Playwright

Create:

step-definitions/login.steps.ts

Example:

import { Given, When, Then } from ‘@cucumber/cucumber’;

import { expect } from ‘@playwright/test’;

Given(‘I am on the login page’, async function () {

  await this.page.goto(‘/login’);

});

When(‘I enter valid login credentials’, async function () {

  await this.page.getByLabel(‘Email’).fill(

    process.env.TEST_USERNAME!

  );

  await this.page.getByLabel(‘Password’).fill(

    process.env.TEST_PASSWORD!

  );

});

When(‘I click the login button’, async function () {

  await this.page.getByRole(‘button’, {

    name: ‘Login’

  }).click();

});

Then(‘I should see the dashboard’, async function () {

  await expect(this.page).toHaveURL(/dashboard/);

});

The important relationship is:

Given → Given()

When  → When()

Then  → Then()

These are Playwright Cucumber step definitions.


Creating a Custom Cucumber World

Cucumber’s World object can hold scenario-specific state.

Create:

support/world.ts

import {

  World,

  IWorldOptions,

  setWorldConstructor

} from ‘@cucumber/cucumber’;

import {

  Browser,

  BrowserContext,

  Page

} from ‘@playwright/test’;

export class CustomWorld extends World {

  browser!: Browser;

  context!: BrowserContext;

  page!: Page;

  constructor(options: IWorldOptions) {

    super(options);

  }

}

setWorldConstructor(CustomWorld);

Now step definitions can access:

this.page

This avoids creating a global shared page that could cause test interference.


Playwright Cucumber Hooks and Test Lifecycle

Hooks are useful for browser setup and cleanup.

Create:

support/hooks.ts

import {

  Before,

  After

} from ‘@cucumber/cucumber’;

import { chromium } from ‘@playwright/test’;

Before(async function () {

  this.browser = await chromium.launch({

    headless: true

  });

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

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

});

After(async function () {

  await this.page?.close();

  await this.context?.close();

  await this.browser?.close();

});

The lifecycle becomes:

Before

 ↓

Browser

 ↓

Context

 ↓

Page

 ↓

Scenario

 ↓

After

 ↓

Cleanup

This is one of the most important concepts when building a Playwright Cucumber Framework.


Page Object Model with Playwright BDD Cucumber

Avoid putting large amounts of browser logic directly inside step definitions.

Instead, create Page Objects.

LoginPage.ts

import { Page } from ‘@playwright/test’;

export class LoginPage {

  constructor(private page: Page) {}

  async open() {

    await this.page.goto(‘/login’);

  }

  async login(

    username: string,

    password: string

  ) {

    await this.page.getByLabel(‘Email’)

      .fill(username);

    await this.page.getByLabel(‘Password’)

      .fill(password);

    await this.page.getByRole(‘button’, {

      name: ‘Login’

    }).click();

  }

}

Step definition:

import { Given, When } from ‘@cucumber/cucumber’;

import { LoginPage } from ‘../pages/LoginPage’;

Given(‘I am on the login page’, async function () {

  this.loginPage = new LoginPage(this.page);

  await this.loginPage.open();

});

When(‘I login with valid credentials’, async function () {

  await this.loginPage.login(

    process.env.TEST_USERNAME!,

    process.env.TEST_PASSWORD!

  );

});

Now your architecture is cleaner:

Feature

 ↓

Step Definition

 ↓

Page Object

 ↓

Playwright


Managing Test Data and Scenario Outlines

Scenario Outlines are one of the most useful Cucumber features for data-driven BDD.

Example:

Scenario Outline: Login with different credentials

  Given I am on the login page

  When I login with “<username>” and “<password>”

  Then I should see “<result>”

Examples:

  | username              | password    | result          |

  | user@example.com      | Valid123    | Dashboard       |

  | user@example.com      | Wrong123    | Invalid Login   |

  | unknown@example.com   | Valid123    | Invalid Login   |

This creates three executions from one scenario.

The corresponding step definition:

When(

  ‘I login with {string} and {string}’,

  async function (

    username: string,

    password: string

  ) {

    await this.loginPage.login(

      username,

      password

    );

  }

);

This is an excellent Playwright BDD example for parameterized testing.


Background, Tags, Given, When, Then, and Examples

Background

Use Background when every scenario needs the same setup.

Background:

  Given I am on the login page

Tags

Tags allow you to categorize scenarios:

@smoke

Scenario: Successful login

Run tagged tests:

npx cucumber-js –tags “@smoke”

You can also use:

@regression

and:

@ecommerce

Given

Represents the initial state.

Given I am logged in

When

Represents an action.

When I add the laptop to the cart

Then

Represents expected behavior.

Then I should see the laptop in the cart

Examples

Provides data for Scenario Outlines.


Real-World Login and E-Commerce BDD Example

Create:

features/ecommerce.feature

Feature: E-Commerce Shopping

  Background:

    Given I am logged in as a customer

  @smoke

  Scenario: Search for a product

    When I search for “laptop”

    Then I should see laptop products

  @regression

  Scenario: Add product to cart

    When I search for “laptop”

    And I select the first product

    And I add the product to the cart

    Then the product should appear in the cart

  Scenario Outline: Invalid login

    Given I am on the login page

    When I login with “<username>” and “<password>”

    Then I should see the “<message>” message

    Examples:

      | username            | password | message            |

      | wrong@example.com   | Wrong123 | Invalid credentials |

      | unknown@example.com | Test123  | Invalid credentials |

This demonstrates:

  • Background
  • Tags
  • Scenario
  • Scenario Outline
  • Examples
  • Given
  • When
  • Then
  • Reusable business scenarios

Playwright Cucumber Fixtures and Reusable Components

Cucumber uses the World object and hooks for scenario state.

You can centralize:

  • Browser
  • Context
  • Page
  • API clients
  • Page Objects
  • Test data

For example:

Before(async function () {

  this.browser = await chromium.launch();

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

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

  this.loginPage = new LoginPage(this.page);

  this.productPage = new ProductPage(this.page);

  this.cartPage = new CartPage(this.page);

});

This gives every scenario reusable components.

Avoid putting application logic directly into hooks.

Hooks should handle setup and cleanup, while Page Objects should handle application behavior.


Screenshots, Traces, Videos, and Reporting

When a BDD scenario fails, debugging should be easy.

Screenshot on failure

In an After hook:

After(async function (scenario) {

  if (scenario.result?.status === ‘FAILED’) {

    const screenshot = await this.page.screenshot();

    await this.attach(

      screenshot,

      ‘image/png’

    );

  }

  await this.page?.close();

  await this.context?.close();

  await this.browser?.close();

});

Now the Cucumber report can contain the failure screenshot.

Tracing

You can start tracing:

Before(async function () {

  this.browser = await chromium.launch();

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

  await this.context.tracing.start({

    screenshots: true,

    snapshots: true

  });

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

});

Stop it after execution:

After(async function () {

  await this.context.tracing.stop({

    path: `test-results/trace-${Date.now()}.zip`

  });

  await this.browser.close();

});

Playwright Trace Viewer is useful for investigating:

  • Failed locators
  • Navigation
  • Screenshots
  • Timing
  • DOM state
  • Actions

For Playwright-specific debugging, traces can provide much more information than a simple error message.


HTML Reporting

Cucumber can produce JSON output:

npx cucumber-js \

  –format json:test-results/cucumber-report.json

That JSON can then be converted into an HTML report using a Cucumber-compatible reporting tool.

A typical CI artifact structure is:

test-results/

├── cucumber-report.json

├── screenshots/

└── traces/

Your report should make it easy to answer:

  • Which feature failed?
  • Which scenario failed?
  • Which step failed?
  • Which test data was used?
  • What screenshot was captured?
  • Is a trace available?

Parallel Execution and Cross-Browser Testing

Playwright supports Chromium, Firefox, and WebKit.

Your Cucumber framework can create a browser based on configuration:

const browser = await chromium.launch();

For Firefox:

import { firefox } from ‘@playwright/test’;

const browser = await firefox.launch();

For WebKit:

import { webkit } from ‘@playwright/test’;

const browser = await webkit.launch();

For parallel Cucumber execution, make sure each scenario has isolated:

Avoid global shared state.

For example, this is dangerous:

Global Page

Global Browser Context

Shared Shopping Cart

Instead:

Worker/Scenario

   ↓

Own Browser Context

   ↓

Own Page

   ↓

Own Test Data

This is particularly important for e-commerce workflows.


Playwright BDD Cucumber CI/CD Integration

A basic GitHub Actions workflow can look like:

name: Playwright BDD Tests

on:

  push:

    branches: [main]

  pull_request:

jobs:

  bdd-tests:

    runs-on: ubuntu-latest

    steps:

      – name: Checkout

        uses: actions/checkout@v6

      – name: Setup Node

        uses: actions/setup-node@v6

        with:

          node-version: lts/*

      – name: Install dependencies

        run: npm ci

      – name: Install Playwright browsers

        run: npx playwright install –with-deps

      – name: Run Cucumber tests

        run: npm run bdd

        env:

          BASE_URL: ${{ secrets.BASE_URL }}

          TEST_USERNAME: ${{ secrets.TEST_USERNAME }}

          TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}

      – name: Upload test results

        if: ${{ !cancelled() }}

        uses: actions/upload-artifact@v5

        with:

          name: cucumber-results

          path: test-results/

Playwright’s CI documentation covers browser installation and CI execution patterns, while Cucumber.js handles execution of the Gherkin scenarios.

CI/CD best practices

Use:

  • Environment variables
  • CI secrets
  • Dedicated test accounts
  • Stable test data
  • Headless browsers
  • Screenshots on failure
  • Traces on failure
  • Test artifacts
  • Smoke and regression tags

Real-World Playwright Cucumber Automation Framework Project

For a portfolio project, create:

ecommerce-playwright-bdd/

├── features/

│   ├── login.feature

│   ├── products.feature

│   ├── cart.feature

│   └── checkout.feature

├── step-definitions/

│   ├── login.steps.ts

│   ├── product.steps.ts

│   ├── cart.steps.ts

│   └── checkout.steps.ts

├── pages/

│   ├── LoginPage.ts

│   ├── ProductPage.ts

│   ├── CartPage.ts

│   └── CheckoutPage.ts

├── support/

│   ├── world.ts

│   └── hooks.ts

├── test-data/

│   ├── users.json

│   └── products.json

├── test-results/

├── cucumber.js

└── package.json

Login feature

Test:

  • Valid login
  • Invalid login
  • Locked account
  • Empty credentials

Product feature

Test:

  • Product search
  • Category selection
  • Product details
  • Product sorting

Cart feature

Test:

  • Add product
  • Remove product
  • Update quantity
  • Verify total

Checkout feature

Test:

Scenario Outline

Use multiple products:

Scenario Outline: Add different products

  Given I am logged in

  When I search for “<product>”

  And I add the product to the cart

  Then the cart should contain “<product>”

Examples:

  | product    |

  | Laptop     |

  | Headphones |

  | Smartphone |

This project demonstrates genuine Playwright BDD Testing rather than a simple login script.


Common Playwright BDD Cucumber Errors and Solutions

ProblemLikely causeSolution
Undefined stepStep text doesn’t match definitionCheck wording
Duplicate stepMultiple matching definitionsRemove or rename one
this.page undefinedWorld not configuredCheck custom World
Browser not closedMissing After hookAdd cleanup
Login state leaksShared contextCreate context per scenario
Screenshot missingFailure hook issueCheck attach()
Feature not foundIncorrect pathCheck Cucumber config
TypeScript not loadedMissing ts-node/configVerify require setup
CI browser failureBrowser dependencies missingInstall with Playwright
Tests fail in parallelShared stateIsolate data and contexts

Playwright BDD Cucumber Best Practices

Use Gherkin for behavior

Don’t write implementation details:

When I click #submit

Prefer:

When I submit the login form

Keep step definitions thin

Avoid putting hundreds of lines into a step file.

Use:

Step

 ↓

Page Object

 ↓

Action

Avoid duplicate steps

Create reusable business-level steps.

Use Scenario Outlines for similar scenarios

Don’t create ten almost-identical feature scenarios when Examples can express the variation.

Use tags

For example:

@smoke

@regression

@checkout

@authentication

Keep hooks focused

Hooks should manage lifecycle, not business behavior.

Use Page Object Model

Centralize locators and application actions.

Isolate test data

Parallel scenarios should not depend on one shared account or shopping cart.

Capture failure artifacts

Use:

  • Screenshots
  • Traces
  • Videos when required
  • Cucumber reports

Protect credentials

Use environment variables and CI/CD secrets.

Don’t overuse Cucumber

If your team doesn’t need business-readable Gherkin, native Playwright Test may provide a simpler architecture.


Playwright BDD Cucumber Interview Questions with Answers

1. What is Playwright BDD Cucumber?

It combines Playwright browser automation with Cucumber’s BDD execution model and Gherkin feature files.

2. What is Gherkin?

Gherkin is a structured language used to describe application behavior with keywords such as Given, When, Then, Scenario, and Feature.

3. What is a step definition?

A step definition connects a Gherkin statement to executable TypeScript code.

4. What is the difference between Cucumber and Playwright?

Cucumber provides the BDD/Gherkin layer.

Playwright performs browser automation.

5. How do you use Page Object Model with Cucumber?

Step definitions call Page Object methods instead of directly containing all locator and browser logic.

6. What is Scenario Outline?

A Scenario Outline allows the same Gherkin scenario to execute with multiple datasets provided through an Examples table.

7. What are Cucumber hooks?

Hooks such as Before and After execute setup and cleanup around scenarios.

8. How do you handle screenshots?

Capture a screenshot in an After hook when the scenario fails and attach it to the Cucumber report.

9. How do you execute only smoke tests?

Use tags:

npx cucumber-js –tags “@smoke”

10. How do you make Cucumber Playwright tests parallel-safe?

Create isolated browser contexts, pages, accounts, and test data for independent scenarios.


Learning Roadmap for Beginners

If you are new to Playwright BDD Cucumber, follow this order.

Step 1: Learn Playwright

Start with:

  • Locators
  • Assertions
  • Browser
  • Context
  • Page
  • Auto-waiting

Step 2: Learn TypeScript

Focus on:

  • Classes
  • Interfaces
  • Async/await
  • Modules

Step 3: Learn Gherkin

Practice:

Feature

Scenario

Given

When

Then

And

Background

Examples

Step 4: Learn Cucumber

Understand:

  • Step definitions
  • World
  • Hooks
  • Tags
  • Scenario Outlines

Step 5: Combine Cucumber and Playwright

Build:

Feature

 ↓

Step

 ↓

POM

 ↓

Playwright

Step 6: Add framework features

Learn:

  • Test data
  • Authentication
  • Fixtures
  • API testing
  • Reporting
  • Screenshots
  • Tracing

Step 7: Learn CI/CD

Practice:

Step 8: Build the e-commerce project

Publish the sanitized project on GitHub.

This demonstrates practical Playwright Cucumber Framework with TypeScript skills.


FAQs: Playwright BDD Cucumber Tutorial

What is Playwright BDD Cucumber?

Playwright BDD Cucumber combines Playwright browser automation with Cucumber’s BDD framework and Gherkin syntax.

How do I get started with Playwright Cucumber?

Install Playwright, Cucumber.js, and TypeScript, create a .feature file, implement its step definitions, and connect the steps to Playwright Page Objects.

Can Playwright work with Cucumber?

Yes. Playwright can be used as the browser automation engine while Cucumber.js manages Gherkin scenarios and step definitions.

What is the difference between Playwright and Cucumber?

Playwright automates browsers. Cucumber executes behavior specifications written in Gherkin.

Can I use TypeScript with Playwright Cucumber?

Yes. TypeScript can be used for step definitions, hooks, Page Objects, custom World implementations, and utilities.

Can Playwright Cucumber run in CI/CD?

Yes. You can execute Cucumber scenarios in GitHub Actions, Jenkins, Azure DevOps, or other CI systems.

Can I use Page Object Model with Cucumber?

Yes. POM is a common way to keep browser interaction logic out of Gherkin step definitions.

How do I run a specific Cucumber tag?

For example:

npx cucumber-js –tags “@smoke”

Is Cucumber required for Playwright?

No.

Playwright can be used directly with Playwright Test. Cucumber is useful when the project benefits from BDD and Gherkin-based specifications.


Leave a Comment

Your email address will not be published. Required fields are marked *