Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Latest commit

 

History

History
336 lines (247 loc) · 10.9 KB

File metadata and controls

336 lines (247 loc) · 10.9 KB

Test Generation

When to use: Generating Playwright test code from interactive CLI sessions — recording user flows, building test scaffolds, converting manual testing into automated tests. Prerequisites: core-commands.md for basic CLI usage

Quick Reference

# Every CLI action outputs the equivalent Playwright code
playwright-cli open https://example.com/login
playwright-cli snapshot
playwright-cli fill e1 "[email protected]"
# Output: await page.getByRole('textbox', { name: 'Email' }).fill('[email protected]');

playwright-cli fill e2 "password123"
# Output: await page.getByRole('textbox', { name: 'Password' }).fill('password123');

playwright-cli click e3
# Output: await page.getByRole('button', { name: 'Sign In' }).click();

How It Works

Every action you perform with playwright-cli automatically generates the corresponding Playwright TypeScript code in the output. This code uses the same role-based locators that Playwright recommends for production tests.

The workflow:

  1. Open a page → generates await page.goto(url)
  2. Take a snapshot → see element refs and their accessible roles
  3. Interact → each action generates one line of Playwright code
  4. Collect the code → assemble generated lines into a complete test

Recording a Flow

Example: Login Flow

playwright-cli open https://example.com/login
# Ran Playwright code:
# await page.goto('https://example.com/login');

playwright-cli snapshot
# Output:
# e1 [textbox "Email"]
# e2 [textbox "Password"]
# e3 [button "Sign In"]
# e4 [link "Forgot password?"]

playwright-cli fill e1 "[email protected]"
# Ran Playwright code:
# await page.getByRole('textbox', { name: 'Email' }).fill('[email protected]');

playwright-cli fill e2 "password123"
# Ran Playwright code:
# await page.getByRole('textbox', { name: 'Password' }).fill('password123');

playwright-cli click e3
# Ran Playwright code:
# await page.getByRole('button', { name: 'Sign In' }).click();

Assemble into a Test

Collect the generated code and wrap it in a Playwright test:

import { test, expect } from '@playwright/test';

test('user can log in', async ({ page }) => {
  await page.goto('https://example.com/login');
  await page.getByRole('textbox', { name: 'Email' }).fill('[email protected]');
  await page.getByRole('textbox', { name: 'Password' }).fill('password123');
  await page.getByRole('button', { name: 'Sign In' }).click();

  // Add assertions (not generated — you add these)
  await expect(page).toHaveURL(/.*dashboard/);
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});
const { test, expect } = require('@playwright/test');

test('user can log in', async ({ page }) => {
  await page.goto('https://example.com/login');
  await page.getByRole('textbox', { name: 'Email' }).fill('[email protected]');
  await page.getByRole('textbox', { name: 'Password' }).fill('password123');
  await page.getByRole('button', { name: 'Sign In' }).click();

  await expect(page).toHaveURL(/.*dashboard/);
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});

Locator Strategies in Generated Code

The generated code uses Playwright's recommended locator hierarchy:

Priority Locator Type Example When used
1 Role-based getByRole('button', { name: 'Submit' }) Elements with ARIA roles
2 Label-based getByLabel('Email') Form inputs with labels
3 Placeholder getByPlaceholder('Search...') Inputs with placeholder text
4 Text-based getByText('Welcome back') Static text content
5 Test ID getByTestId('submit-btn') Elements with data-testid

These locators are resilient to markup changes — they mirror how users perceive the page rather than relying on CSS selectors or XPath.

Recording Complex Flows

E-Commerce Checkout

playwright-cli open https://shop.example.com

# Browse products
playwright-cli snapshot
playwright-cli click e5                # "Add to Cart" button
# await page.getByRole('button', { name: 'Add to Cart' }).click();

playwright-cli click e12               # Cart icon
# await page.getByRole('link', { name: 'Cart' }).click();

playwright-cli snapshot
playwright-cli click e3                # "Proceed to Checkout"
# await page.getByRole('button', { name: 'Proceed to Checkout' }).click();

# Shipping info
playwright-cli snapshot
playwright-cli fill e1 "Jane Doe"
# await page.getByRole('textbox', { name: 'Full Name' }).fill('Jane Doe');
playwright-cli fill e2 "123 Main St"
# await page.getByRole('textbox', { name: 'Address' }).fill('123 Main St');
playwright-cli fill e3 "Springfield"
# await page.getByRole('textbox', { name: 'City' }).fill('Springfield');
playwright-cli select e4 "IL"
# await page.getByRole('combobox', { name: 'State' }).selectOption('IL');
playwright-cli fill e5 "62701"
# await page.getByRole('textbox', { name: 'ZIP Code' }).fill('62701');
playwright-cli click e6               # "Continue to Payment"

Multi-Step Wizard

playwright-cli open https://example.com/onboarding

# Step 1
playwright-cli snapshot
playwright-cli fill e1 "Acme Corp"
playwright-cli select e2 "technology"
playwright-cli click e3    # Next

# Step 2
playwright-cli snapshot
playwright-cli check e1    # Feature checkbox
playwright-cli check e3    # Another feature
playwright-cli click e5    # Next

# Step 3
playwright-cli snapshot
playwright-cli click e2    # "Complete Setup"

Search with Dynamic Results

playwright-cli open https://example.com
playwright-cli snapshot

playwright-cli fill e1 "playwright testing"
# await page.getByRole('searchbox', { name: 'Search' }).fill('playwright testing');

playwright-cli press Enter
# await page.keyboard.press('Enter');

playwright-cli snapshot    # See search results — new refs assigned

playwright-cli click e3    # First result
# await page.getByRole('link', { name: 'Getting Started with Playwright' }).click();

Adding Assertions

Generated code captures actions but not assertions. Always add assertions manually to create meaningful tests.

Common Assertions to Add

// URL changed after navigation
await expect(page).toHaveURL(/.*dashboard/);
await expect(page).toHaveURL('https://example.com/success');

// Element is visible
await expect(page.getByRole('heading', { name: 'Welcome' })).toBeVisible();
await expect(page.getByText('Order confirmed')).toBeVisible();

// Element contains text
await expect(page.getByTestId('total')).toHaveText('$99.99');
await expect(page.getByRole('alert')).toContainText('saved');

// Element has specific attribute
await expect(page.getByRole('button', { name: 'Submit' })).toBeDisabled();
await expect(page.getByRole('checkbox')).toBeChecked();

// Element count
await expect(page.getByRole('listitem')).toHaveCount(5);

// Page title
await expect(page).toHaveTitle(/Dashboard/);

// Screenshot comparison
await expect(page).toHaveScreenshot('checkout.png');

Where to Place Assertions

test('complete checkout flow', async ({ page }) => {
  await page.goto('https://shop.example.com/products');

  // Action: Add item to cart
  await page.getByRole('button', { name: 'Add to Cart' }).click();

  // Assertion: Cart badge updates
  await expect(page.getByTestId('cart-count')).toHaveText('1');

  // Action: Go to cart
  await page.getByRole('link', { name: 'Cart' }).click();

  // Assertion: Correct page
  await expect(page).toHaveURL(/.*cart/);
  await expect(page.getByRole('heading', { name: 'Your Cart' })).toBeVisible();

  // Action: Proceed to checkout
  await page.getByRole('button', { name: 'Checkout' }).click();

  // Action: Fill shipping
  await page.getByRole('textbox', { name: 'Full Name' }).fill('Jane Doe');
  await page.getByRole('textbox', { name: 'Address' }).fill('123 Main St');
  await page.getByRole('button', { name: 'Place Order' }).click();

  // Assertion: Order confirmed
  await expect(page.getByText('Order confirmed')).toBeVisible();
  await expect(page.getByTestId('order-number')).toBeVisible();
});

Best Practices

1. Explore Before Recording

Take a snapshot first to understand the page structure. Don't blindly click — know what elements are available:

playwright-cli open https://example.com
playwright-cli snapshot
# Review the elements, plan your flow, then start interacting

2. Use Semantic Locators

The generated code already prefers role-based locators. If you see CSS selectors in generated output, consider filing an issue — role-based locators are more resilient:

// Generated (good — semantic, resilient)
await page.getByRole('button', { name: 'Submit' }).click();

// Avoid writing manually (fragile — breaks if CSS changes)
await page.locator('#submit-btn').click();
await page.locator('.btn.btn-primary').click();

3. Keep Tests Focused

One test = one user behavior. Don't record an entire session into a single test:

// Good: Focused test
test('user can add item to cart', async ({ page }) => {
  // Just the add-to-cart flow
});

test('user can complete checkout', async ({ page }) => {
  // Just the checkout flow (use auth state to skip login)
});

// Bad: Monolith test
test('user journey', async ({ page }) => {
  // Login + browse + add to cart + checkout + verify email...
});

4. Parameterize Test Data

Replace hardcoded values from the recording with variables or test data:

// Instead of hardcoded values from recording
test('registration', async ({ page }) => {
  const user = {
    name: 'Jane Doe',
    email: `test+${Date.now()}@example.com`,
    password: 'SecurePass123!'
  };

  await page.goto('/register');
  await page.getByRole('textbox', { name: 'Name' }).fill(user.name);
  await page.getByRole('textbox', { name: 'Email' }).fill(user.email);
  await page.getByRole('textbox', { name: 'Password' }).fill(user.password);
  await page.getByRole('button', { name: 'Create Account' }).click();

  await expect(page).toHaveURL(/.*welcome/);
});

5. Add Wait Strategies for Flaky Steps

If a recorded action depends on async content loading, add explicit waits:

// Before clicking a dynamically loaded element
await page.waitForSelector('.results-loaded');
await page.getByRole('link', { name: 'First Result' }).click();

// Or use Playwright's auto-waiting (preferred)
await expect(page.getByRole('link', { name: 'First Result' })).toBeVisible();
await page.getByRole('link', { name: 'First Result' }).click();

Tips

  • Generated code is a starting point — always review, add assertions, and parameterize before committing to your test suite
  • Re-snapshot after dynamic changes — refs change when the DOM updates
  • Combine with state-save — record a login flow once, save state, then start all other recordings from the authenticated state
  • Use --filename for snapshots — save before/after snapshots for complex flows to help write assertions later