playwright-bdd

repository·main·Indexed 20 days ago

https://github.com/vitalets/playwright-bdd

A tool for Behavior-Driven Development (BDD) that converts Gherkin-style .feature files into executable tests using the native Playwright test runner. It supports TypeScript (CommonJS and ESM), decorator-based steps, and Cucumber-style step definitions. Key features include API testing, dynamic and static authentication patterns, and a 'Fix with AI' feature for repairing failing tests. Version 9.2.0.

Tokens
57.9K
Snippets
211
Records
252
Agent score
72%

What's inside playwright-bdd

  1. Run BDD tests with the Playwright runner

    main

    Playwright-BDD allows you to run Behavior-Driven Development (BDD) scenarios (written in .feature files using Given / When / Then syntax) directly using the Playwright test runner.

    By converting BDD scenarios into native Playwright tests, you gain access to all standard Playwright runner features, including:

    • Automatic browser setup and cleanup
    • Auto-waiting for page elements
    • Auto-capture of screenshots, videos, and traces
    • Parallel execution and sharding
    • Built-in reports and visual comparison testing
    • Playwright fixtures
  2. Use Cucumber Reporters in Playwright-BDD

    main

    Playwright-BDD provides a cucumberReporter adapter to output test results using standard Cucumber formatters. This allows you to generate reports compatible with the Cucumber ecosystem (HTML, JSON, JUnit, Message, or Custom formatters).

    Automatic Attachments

    Playwright-BDD supports auto-attaching screenshots, videos, and traces to all Cucumber reports. To enable this, configure the recording options in your standard Playwright configuration (use object).

    Project Support

    While Cucumber formatters do not natively support Playwright's projects concept, Playwright-BDD adapts the results so that project names are visible in the reports (e.g., prepended to feature file paths in HTML reports).

    import { defineConfig, devices } from '@playwright/test';
    import { defineBddConfig, cucumberReporter } from 'playwright-bdd';
    
    const testDir = defineBddConfig({
      features: 'features/**/*.feature',
      steps: 'features/steps/**/*.ts',
    });
    
    export default defineConfig({
      testDir,
      reporter: [ 
        cucumberReporter('html', { outputFile: 'cucumber-report/index.html' })
      ],
      projects: [
        { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
        { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
      ],
    });
  3. Playwright-BDD features and extras

    main

    Beyond standard BDD support, Playwright-BDD provides several advanced capabilities to enhance the testing experience:

    • Advanced Tagging: Support for tagging scenarios by file path or using special reserved tags.
    • Step Decorators: Use decorators on class methods to define step definitions.
    • Scoped Step Definitions: Restrict the scope of step definitions.
    • Exporting Steps: Capability to export steps to facilitate AI-driven development (e.g., for ChatGPT).
    • Re-usable Step Functions: Ability to reuse step logic across different scenarios.
  4. Understand generated test files

    main

    When you run bddgen, Playwright-BDD generates standard Playwright spec files in a .features-gen directory. These generated files wrap your Gherkin scenarios into test.describe and test blocks. Inside the test body, the steps are called as functions (e.g., await Given('...')), which internally look up the implementation you provided in your step files.

    // Example of a generated file in .features-gen/sample.feature.spec.js
    import { test } from 'playwright-bdd';
    
    test.describe('Playwright site', () => {
      test('Check get started link', async ({ Given, When, Then }) => {
        await Given('I am on home page');
        await When('I click link "Get started"');
        await Then('I see in title "Installation"');
      });
    });
  5. Why Playwright-BDD generates test files

    main

    Playwright-BDD decouples test generation from test execution. Instead of running BDD scenarios on-the-fly, it generates standard Playwright test files. This allows you to leverage the full Playwright ecosystem, including:

    • Running single tests via the VS Code extension.
    • Debugging and setting breakpoints on specific BDD steps.
    • Using Playwright's --ui mode to watch for changes.
    • Using all standard Playwright tooling and features.

    This approach avoids issues with circular dependencies in watch mode and excessive re-generation caused by the Playwright config being executed multiple times by different sources (workers, UI mode, etc.).

  6. Use featuresRoot to simplify configuration

    main

    Since Playwright-BDD v8, you can use the featuresRoot option as a common base directory for both features and steps. This allows for more concise configurations.

    • featuresRoot: A directory (cannot contain glob patterns) that serves as the base for both features and steps.
    • If features and steps are omitted, the patterns are automatically calculated as {featuresRoot} + /**/*.feature and {featuresRoot} + /**/*.{js,mjs,cjs,ts,mts,cts} respectively.

    Example: Before v8

    const testDir = defineBddConfig({
      features: './features/**/*.feature',
      steps: './features/steps/**/*.js',
      featuresRoot: './features',
    });

    Example: Since v8

    const testDir = defineBddConfig({
      featuresRoot: './features',
    });

    Note on Output Structure: featuresRoot also controls how files are organized inside the outputDir. If you set featuresRoot: 'features', the features/ prefix will be stripped from the generated paths inside .features-gen/.

    // Concise v8+ configuration
    const testDir = defineBddConfig({
      featuresRoot: './features',
    });
    
    // Overriding features within a featuresRoot setup
    const testDir = defineBddConfig({
      featuresRoot: './features',
      features: './features/game/**/*.feature', 
    });
  7. How Playwright-BDD works: The two-phase workflow

    main

    Playwright-BDD operates by converting BDD scenarios into native Playwright tests. This ensures that you get the full power of the Playwright runner (fixtures, reporting, parallelization) while maintaining a BDD workflow.

    Phase 1: Generate tests

    The npx bddgen command reads your .feature files and generates corresponding .js or .ts files.

    Example Input (Gherkin):

    Feature: Playwright Home Page
    
        Scenario: Check title
            Given I am on Playwright home page
            When I click link "Get started"
            Then I see in title "Installation"

    Example Output (Generated Playwright Test):

    import { test } from 'playwright-bdd';
    
    test.describe('Playwright Home Page', () => {
    
      test('Check title', async ({ Given, When, Then }) => {
        await Given('I am on Playwright home page');
        await When('I click link "Get started"');
        await Then('I see in title "Installation"');
      });
    
    });

    Phase 2: Run tests

    You run the generated files using the standard Playwright command: npx playwright test.

    Step definitions have full access to Playwright APIs and fixtures (like page).

    Example Step Definitions:

    Given('I am on Playwright home page', async ({ page }) => {
      await page.goto('https://playwright.dev');
    });
    
    When('I click link {string}', async ({ page }, name) => {
      await page.getByRole('link', { name }).click();
    });
    
    Then('I see in title {string}', async ({ page }, text) => {
      await expect(page).toHaveTitle(new RegExp(text));
    });  
    npx bddgen && npx playwright test
  8. Use Cucumber expressions or Regular expressions for step patterns

    main

    Each step is defined with a pattern that matches the Gherkin step text. You can use two types of patterns:

    1. Cucumber expressions: String patterns using typed parameters like {string}, {int}, or {float}.
    2. Regular expressions: Used for complex matching. Capture groups in the regex are passed as step parameters to the function.

    Cucumber expression example

    Given('I open url {string}', async ({ page }, url: string) => {
      await page.goto(url);
    });

    Regular expression example

    Then(/I should see (success|error) message/, async ({ page }, status: string) => {
      await expect(page.getByRole('alert')).toHaveText(status);
    });
  9. Access the Default World via `this`

    main

    While arrow functions are preferred in Playwright-style, you can use the this context (the Default World) for migration from CucumberJS. To do this, you must use regular functions instead of arrow functions.

    Playwright-BDD provides an empty object {} as the default world.

    Given('step 1', async function ({ page }) {
      this.foo = 'bar';
    });
    
    Then('step 2', async function () {
      expect(this.foo).toEqual('bar');
    });
    Given('step 1', async function ({ page }) {
      this.foo = 'bar';
    });
    
    Then('step 2', async function () {
      expect(this.foo).toEqual('bar');
    });
  10. Apply multiple decorators to a single method

    main

    You can apply multiple step decorators (e.g., multiple @When or a mix of @Given and @When) to the same method. This allows a single implementation to support different natural language phrasings or different step keywords without duplicating code.

    Each decorator registers a separate step definition that points to the same underlying method implementation.

    export @Fixture('todoPage') class TodoPage {
      @When('a item {string} exists')
      @When('a item called {string} is added')
      async addItem(itemName: string) {
        await this.inputField.fill(itemName);
        await this.addItemButton.click();
      }
    
      @Then('result is {int}')
      @Then('I see result {int}')
      async checkResult(value: number) {
        await expect(this.resultElement).toHaveText(String(value));
      }
    }
  11. How inheritance works with @Fixture decorators

    main

    When Page Object Models use inheritance, Playwright-BDD automatically attempts to resolve steps using a single fixture. If a scenario uses a step from a parent class and a step from a child class, Playwright-BDD will use the child class's fixture for both steps to avoid creating multiple separate fixtures.

    Important: Ensure that both the parent and child POMs are covered by the steps pattern defined in your playwright.config.ts.

    Forcing a specific fixture

    If you want to override the automatic resolution and force a specific fixture to be used for a scenario or feature, use the @fixture:%name% tag in your Gherkin file.

    @fixture:adminTodoPage
    Feature: Some feature
    
        Background: 
          Given I am on todo page # <- will use AdminTodoPage
    
        Scenario: Adding todos
          When I add todo "foo"   # <- will use AdminTodoPage