Back to AI Prompt Pad

Documentation

BDD EXAMPLES

BDD Examples - Gherkin vs Playwright

This document shows side-by-side comparisons of the same test written in both styles.


Example 1: Generate AI Response

BDD (Gherkin) - Business Perspective

gherkin
Feature: AI Prompt Generation
  As a user
  I want to generate AI responses
  So that I can interact with the AI

  Scenario: Generate a streaming AI response
    Given I am on the AI Prompt Pad page
    When I enter the prompt "What is machine learning?"
    And I click the Generate button
    Then I should see my message in the conversation
    And I should see the streaming indicator
    And I should see the AI response streaming
    And the streaming should complete successfully

Who reads this: Product managers, stakeholders, QA, developers Focus: What the user experiences Language: Business terminology


Playwright - Technical Perspective

typescript
test('should generate and display streaming response', async ({ page }) => {
  await page.goto('/')

  const promptInput = page.getByTestId('prompt-input')
  const generateButton = page.getByTestId('generate-button')

  // Enter prompt
  await promptInput.fill('Tell me about AI')

  // Click generate
  await generateButton.click()

  // Wait for user message to appear
  await expect(page.locator('.message.user')).toBeVisible()
  await expect(page.locator('.message.user .message-content'))
    .toContainText('Tell me about AI')

  // Wait for assistant response to start streaming
  await expect(page.locator('.message.assistant')).toBeVisible({
    timeout: 10000
  })

  // Verify streaming indicator (cursor) appears
  await expect(page.locator('.cursor')).toBeVisible({
    timeout: 5000
  })

  // Wait for response to complete (cursor should disappear)
  await expect(page.locator('.cursor')).not.toBeVisible({
    timeout: 15000
  })

  // Verify assistant message has content
  const assistantMessage = page.locator('.message.assistant .message-content')
  await expect(assistantMessage).not.toBeEmpty()
})

Who reads this: Developers, QA engineers Focus: How it's implemented, timing, selectors Language: Code, technical details


Example 2: Stop Generation

BDD (Gherkin)

gherkin
Scenario: Stop generation mid-stream
  Given I am on the AI Prompt Pad page
  When I enter the prompt "Long response test"
  And I click the Generate button
  And I wait for streaming to start
  And I click the Stop button
  Then the streaming should stop immediately
  And the response should contain the stopped marker

Clarity: Clear user workflow Abstraction: High-level actions Implementation: Hidden in step definitions


Playwright

typescript
test('should stop generation when stop button is clicked', async ({ page }) => {
  await page.goto('/')
  const promptInput = page.getByTestId('prompt-input')
  const generateButton = page.getByTestId('generate-button')

  await promptInput.fill('Test stopping generation')
  await generateButton.click()

  // Wait for generation to start
  await expect(page.locator('.cursor')).toBeVisible({ timeout: 5000 })

  // Click stop button
  const stopButton = page.getByTestId('stop-button')
  await stopButton.click()

  // Verify generation stopped
  await expect(page.locator('.cursor')).not.toBeVisible({
    timeout: 2000
  })
  await expect(page.locator('.message.assistant'))
    .toContainText('[stopped]')
})

Clarity: Shows exact timing and selectors Abstraction: Low-level browser interactions Implementation: Visible and explicit


Example 3: Multi-turn Conversation

BDD (Gherkin)

gherkin
Scenario: Have a multi-turn conversation
  Given I am on the AI Prompt Pad page
  When I enter the prompt "First question"
  And I click the Generate button
  And I wait for the response to complete
  And I enter the prompt "Second question"
  And I click the Generate button
  And I wait for the response to complete
  Then I should see 2 user messages
  And I should see 2 assistant messages

Benefits:

  • Easy to understand by anyone
  • Documents user flow clearly
  • Can be written before implementation

Playwright

typescript
test('should allow multiple prompts in conversation', async ({ page }) => {
  await page.goto('/')
  const promptInput = page.getByTestId('prompt-input')
  const generateButton = page.getByTestId('generate-button')

  // First prompt
  await promptInput.fill('First question')
  await generateButton.click()
  await expect(page.locator('.message.user').first())
    .toContainText('First question')
  await expect(page.locator('.cursor')).not.toBeVisible({
    timeout: 15000
  })

  // Second prompt
  await promptInput.fill('Second question')
  await generateButton.click()
  await expect(page.locator('.message.user').nth(1))
    .toContainText('Second question')
  await expect(page.locator('.cursor')).not.toBeVisible({
    timeout: 15000
  })

  // Verify we have 2 user messages and 2 assistant messages
  await expect(page.locator('.message.user')).toHaveCount(2)
  await expect(page.locator('.message.assistant')).toHaveCount(2)
})

Benefits:

  • Full control over assertions
  • Can test specific message order
  • Easy to debug failures

Example 4: Keyboard Shortcuts

BDD (Gherkin)

gherkin
Scenario: Use keyboard shortcuts
  Given I am on the AI Prompt Pad page
  When I enter the prompt "Test Enter key"
  And I press Enter
  Then I should see my message in the conversation

Scenario: Create multiline prompt with Shift+Enter
  Given I am on the AI Prompt Pad page
  When I enter "Line 1"
  And I press Shift+Enter
  And I enter "Line 2"
  Then the prompt should contain multiple lines
  And no message should be sent yet

Natural language: Describes behavior, not implementation Reusable steps: "I press Enter" can be used anywhere


Playwright

typescript
test('should support Enter key to submit prompt', async ({ page }) => {
  await page.goto('/')
  const promptInput = page.getByTestId('prompt-input')

  await promptInput.fill('Test with Enter key')
  await promptInput.press('Enter')

  // Verify message was sent
  await expect(page.locator('.message.user'))
    .toContainText('Test with Enter key')
})

test('should support Shift+Enter for new line without submitting',
  async ({ page }) => {
    await page.goto('/')
    const promptInput = page.getByTestId('prompt-input')

    await promptInput.fill('Line 1')
    await promptInput.press('Shift+Enter')
    await promptInput.type('Line 2')

    // Verify textarea contains multiline text
    const textareaValue = await promptInput.inputValue()
    expect(textareaValue).toContain('\n')

    // Verify no message was sent yet
    await expect(page.locator('.message')).toHaveCount(0)
  }
)

Detailed checks: Verifies exact textarea value Technical assertions: Tests implementation specifics


Example 5: Error Handling

BDD (Gherkin)

gherkin
Feature: Error Handling
  As a user
  I want graceful error handling
  So that I understand what went wrong

  Scenario: Handle backend server errors
    Given I am on the AI Prompt Pad page
    And the backend returns an error
    When I enter the prompt "Test error"
    And I click the Generate button
    Then I should see an error message
    And the error message should be helpful

User-focused: Describes what user sees Behavioral: Tests error handling from user perspective


Playwright

typescript
test('should handle backend errors gracefully', async ({ page }) => {
  await page.goto('/')

  // Intercept API call and return error
  await page.route('**/api/generate', route => {
    route.fulfill({
      status: 500,
      body: 'Internal Server Error',
    })
  })

  const promptInput = page.getByTestId('prompt-input')
  const generateButton = page.getByTestId('generate-button')

  await promptInput.fill('This should fail')
  await generateButton.click()

  // Verify error message appears
  await expect(page.locator('.message.assistant')).toContainText(
    'Sorry, there was an error generating the response.'
  )
})

Network mocking: Simulates specific error conditions Technical setup: Shows how errors are triggered


Step Definitions (The Bridge)

Step definitions connect Gherkin to Playwright:

Gherkin Step

gherkin
When I click the Generate button

Step Definition (TypeScript)

typescript
import { When } from '@cucumber/cucumber'

When('I click the Generate button', async function () {
  const button = this.page.getByTestId('generate-button')
  await button.click()
})

What It Executes (Playwright)

typescript
// Under the hood, it runs this Playwright code:
const button = page.getByTestId('generate-button')
await button.click()


When to Choose Which?

Choose BDD (Gherkin) When:

gherkin
# ✅ Good for BDD
Scenario: User completes checkout process
  Given I have items in my cart
  When I proceed to checkout
  And I enter payment information
  And I confirm the order
  Then I should see an order confirmation
  And I should receive a confirmation email

  • Multi-step user journeys
  • Business acceptance criteria
  • Cross-team collaboration
  • Living documentation

Choose Playwright When:

typescript
// ✅ Good for Playwright
test('should debounce search input by 300ms', async ({ page }) => {
  const searchInput = page.getByTestId('search')

  await searchInput.type('test')

  // Should not trigger search immediately
  await page.waitForTimeout(100)
  await expect(page.locator('.search-results')).not.toBeVisible()

  // Should trigger after 300ms
  await page.waitForTimeout(250)
  await expect(page.locator('.search-results')).toBeVisible()
})

  • Timing and performance
  • Browser-specific behavior
  • Complex state management
  • Technical edge cases

Hybrid Example

Combine both for comprehensive coverage:

BDD (User Journey)

gherkin
Scenario: Search for a product
  When I search for "laptop"
  Then I should see search results
  And results should be relevant

Playwright (Technical Details)

typescript
test('should debounce search requests', ...)
test('should show loading spinner during search', ...)
test('should handle empty results gracefully', ...)
test('should support keyboard navigation in results', ...)

Result:

  • BDD covers the happy path
  • Playwright covers edge cases and technical requirements

Summary Comparison

AspectBDD (Gherkin)Playwright
AudienceEveryoneDevelopers
LanguageNaturalCode
AbstractionHighLow
Detail LevelUser behaviorImplementation
Setup RequiredStep definitionsNone
DebuggingHarderEasier
CollaborationExcellentLimited
Speed to WriteSlower (need steps)Faster
Maintenance2 files (feature + steps)1 file
CI ReportingBusiness-friendlyTechnical
Best Practice: Use both!
  • Write BDD scenarios for acceptance criteria
  • Write Playwright tests for technical coverage

© 2026 Jonathan Leahy · v1.0.9