Skip to main content

Playwright Tutorial

Playwright is a modern end-to-end testing framework developed by Microsoft that enables reliable testing across Chromium, Firefox, and WebKit browsers. It provides powerful automation capabilities for web applications with built-in support for modern web features like auto-waiting, network interception, and mobile emulation.


Prerequisites​

  1. Node.js: Ensure Node.js (version 16 or higher) is installed:

    node -v
    npm -v
  2. Basic knowledge of JavaScript/TypeScript: Understanding of modern JavaScript syntax and concepts.

  3. Web development basics: Familiarity with HTML, CSS, and web application concepts.


1. Installation and Setup​

Installing Playwright​

Create a new project and install Playwright:

npm init playwright@latest

This command will:

  • Create a new Playwright project
  • Install Playwright and browsers
  • Create example tests and configuration files

Manual Installation​

For existing projects, install Playwright manually:

npm install -D @playwright/test
npx playwright install

Project Structure​

After installation, your project will have:

tests/
├── example.spec.ts # Example test file
playwright.config.ts # Configuration file
package.json

2. Basic Configuration​

Playwright Configuration (playwright.config.ts)​

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './tests',
timeout: 30 * 1000,
expect: {
timeout: 5000
},
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
});

3. Writing Your First Test​

Basic Test Structure​

Create a test file tests/my-first-test.spec.ts:

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

test('basic test', async ({ page }) => {
await page.goto('https://playwright.dev/');

// Expect a title "to contain" a substring
await expect(page).toHaveTitle(/Playwright/);
});

test('get started link', async ({ page }) => {
await page.goto('https://playwright.dev/');

// Click the get started link
await page.getByRole('link', { name: 'Get started' }).click();

// Expects the URL to contain intro
await expect(page).toHaveURL(/.*intro/);
});

Key Concepts​

  • test(): Defines a test case
  • page: Browser page object for interactions
  • expect(): Assertion library for validations
  • Auto-waiting: Playwright automatically waits for elements to be ready

4. Locating Elements​

Locator Strategies​

// By role (recommended)
await page.getByRole('button', { name: 'Submit' });
await page.getByRole('textbox', { name: 'Username' });

// By text content
await page.getByText('Welcome');
await page.getByText(/Sign in/i);

// By label (for form inputs)
await page.getByLabel('Email');

// By placeholder
await page.getByPlaceholder('Enter your email');

// By test ID
await page.getByTestId('submit-button');

// CSS selectors
await page.locator('#username');
await page.locator('.submit-btn');

// XPath
await page.locator('xpath=//button[contains(text(), "Submit")]');

5. Common Actions and Assertions​

User Interactions​

test('user interactions', async ({ page }) => {
await page.goto('https://example.com/form');

// Fill input fields
await page.getByLabel('Name').fill('John Doe');

// Click buttons
await page.getByRole('button', { name: 'Submit' }).click();

// Select dropdown options
await page.getByLabel('Country').selectOption('US');

// Check/uncheck checkboxes
await page.getByLabel('Subscribe').check();
await page.getByLabel('Terms').uncheck();

// Upload files
await page.getByLabel('Upload').setInputFiles('path/to/file.pdf');

// Hover over elements
await page.getByRole('link', { name: 'Menu' }).hover();
});

Assertions​

test('assertions', async ({ page }) => {
await page.goto('https://example.com');

// Page assertions
await expect(page).toHaveTitle('Expected Title');
await expect(page).toHaveURL('https://example.com');

// Element assertions
const button = page.getByRole('button', { name: 'Submit' });
await expect(button).toBeVisible();
await expect(button).toBeEnabled();
await expect(button).toHaveText('Submit');
await expect(button).toHaveAttribute('type', 'submit');

// Text content assertions
await expect(page.getByText('Welcome')).toBeVisible();
await expect(page.locator('h1')).toContainText('Hello');

// Count assertions
await expect(page.getByRole('listitem')).toHaveCount(5);
});

6. Advanced Features​

Page Object Model​

Create reusable page objects for better test organization:

// pages/login-page.ts
export class LoginPage {
constructor(private page: Page) {}

async goto() {
await this.page.goto('/login');
}

async login(username: string, password: string) {
await this.page.getByLabel('Username').fill(username);
await this.page.getByLabel('Password').fill(password);
await this.page.getByRole('button', { name: 'Login' }).click();
}

async getErrorMessage() {
return await this.page.getByTestId('error-message').textContent();
}
}

// tests/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/login-page';

test('login test', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('user@example.com', 'password');

await expect(page).toHaveURL('/dashboard');
});

Network Interception​

test('mock API responses', async ({ page }) => {
// Mock API response
await page.route('**/api/users', async route => {
const json = [{ id: 1, name: 'John Doe' }];
await route.fulfill({ json });
});

await page.goto('/users');
await expect(page.getByText('John Doe')).toBeVisible();
});

Mobile Testing​

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

test('mobile test', async ({ browser }) => {
const context = await browser.newContext({
...devices['iPhone 13'],
});

const page = await context.newPage();
await page.goto('https://example.com');

// Test mobile-specific functionality
await expect(page.getByRole('button', { name: 'Menu' })).toBeVisible();
});

7. Running Tests​

Command Line Options​

# Run all tests
npx playwright test

# Run tests in specific file
npx playwright test tests/login.spec.ts

# Run tests in headed mode (visible browser)
npx playwright test --headed

# Run tests in specific browser
npx playwright test --project=chromium

# Run tests with debugging
npx playwright test --debug

# Generate test report
npx playwright show-report

Debugging Tests​

test('debug test', async ({ page }) => {
await page.goto('https://example.com');

// Pause execution for debugging
await page.pause();

// Take screenshot for debugging
await page.screenshot({ path: 'debug-screenshot.png' });
});

8. Best Practices​

Test Organization​

  1. Use descriptive test names
  2. Group related tests using test.describe()
  3. Use Page Object Model for complex applications
  4. Keep tests independent and isolated

Performance Tips​

// Use beforeAll for expensive setup
test.beforeAll(async ({ browser }) => {
// Setup that runs once per test file
});

// Use fixtures for common test data
test('test with fixture', async ({ page }) => {
// Test implementation
});

// Parallel execution
test.describe.configure({ mode: 'parallel' });

Reliable Testing​

// Wait for network requests to complete
await page.waitForLoadState('networkidle');

// Wait for specific elements
await page.waitForSelector('[data-testid="content"]');

// Use soft assertions for non-critical checks
await expect.soft(page.getByText('Optional content')).toBeVisible();

9. Continuous Integration​

GitHub Actions Example​

name: Playwright Tests
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/

Summary​

This tutorial covered the essential aspects of Playwright:

  1. Installation and setup with automatic browser management
  2. Basic test writing with modern locators and auto-waiting
  3. Element interaction and assertion patterns
  4. Advanced features like Page Object Model and network mocking
  5. Testing strategies for mobile and cross-browser compatibility
  6. Best practices for maintainable and reliable tests

Playwright provides a comprehensive solution for modern web testing with excellent developer experience and powerful automation capabilities.

Content Review​

The content in this repository has been reviewed by chevp. Chevp is dedicated to ensuring that the information provided is accurate, relevant, and up-to-date, helping users to learn and implement programming skills effectively.

About the Reviewer​

For more insights and contributions, visit chevp's GitHub profile: chevp's GitHub Profile.