guide60 min readUpdated Jan 25, 2026

Playwright Keywords & Concepts

Master every essential Playwright concept with detailed explanations and code examples.

Tool:PlaywrightLevel:intermediateDomain:QA Engineering

Introduction

This comprehensive reference guide covers all essential Playwright APIs, methods, and concepts. Use this as your complete dictionary for Playwright test automation with TypeScript.

💡 What's Covered:
  • Core APIs: Browser, BrowserContext, Page, Locator
  • Locator strategies and selection methods
  • Actions: Click, fill, select, hover, drag & drop
  • Assertions: Web-first assertions with auto-retry
  • Network: Request/response interception and API testing
  • Advanced: Fixtures, trace viewer, codegen, visual testing
📖 How to Use This Guide:
  • Use the alphabetical index to quickly find specific keywords
  • Each entry includes syntax, examples, and best practices
  • Code examples use TypeScript (Playwright's recommended language)
  • Cross-references help you discover related concepts

Quick Navigation Index

Core APIs

Browser

Core API

Purpose: Represents a browser instance (Chromium, Firefox, or WebKit). Manages browser lifecycle and creates isolated browser contexts.

TypeScript - Browser Methods
import { chromium, firefox, webkit } from '@playwright/test';

// Launch browser
const browser = await chromium.launch({
  headless: true,
  slowMo: 100, // Slow down by 100ms (debugging)
  timeout: 30000
});

// Launch with options
const browser = await chromium.launch({
  headless: false,
  args: ['--start-maximized'],
  downloadsPath: './downloads',
  channel: 'chrome' // Use Google Chrome instead of Chromium
});

// Create browser context
const context = await browser.newContext();

// Get browser contexts
const contexts = browser.contexts();
console.log(`Active contexts: ${contexts.length}`);

// Check if browser is connected
console.log(`Connected: ${browser.isConnected()}`);

// Browser version
console.log(`Version: ${browser.version()}`);

// Close browser
await browser.close();

// Different browsers
const chromiumBrowser = await chromium.launch();
const firefoxBrowser = await firefox.launch();
const webkitBrowser = await webkit.launch();
MethodDescription
launch()Launch new browser instance
newContext()Create isolated browser context
contexts()Get all active contexts
close()Close browser and all contexts
isConnected()Check if browser is running
version()Get browser version

Related: BrowserContext,Page

BrowserContext

Core API

Purpose: Isolated incognito-like session with own cookies, cache, and storage. Multiple contexts can exist in one browser - key to fast, parallel test execution.

TypeScript - BrowserContext Usage
import { chromium } from '@playwright/test';

const browser = await chromium.launch();

// Create context with options
const context = await browser.newContext({
  // Viewport
  viewport: { width: 1920, height: 1080 },
  
  // User agent
  userAgent: 'Custom User Agent',
  
  // Locale and timezone
  locale: 'en-US',
  timezoneId: 'America/New_York',
  
  // Geolocation
  geolocation: { latitude: 40.7128, longitude: -74.0060 },
  permissions: ['geolocation'],
  
  // Authentication
  httpCredentials: {
    username: 'admin',
    password: 'password123'
  },
  
  // Storage state (authentication)
  storageState: 'auth.json',
  
  // Ignore HTTPS errors
  ignoreHTTPSErrors: true,
  
  // Offline mode
  offline: true,
  
  // Extra headers
  extraHTTPHeaders: {
    'X-Custom-Header': 'value'
  }
});

// Create new page in context
const page = await context.newPage();

// Get all pages in context
const pages = context.pages();
console.log(`Pages: ${pages.length}`);

// Add cookies
await context.addCookies([{
  name: 'token',
  value: 'abc123',
  domain: 'example.com',
  path: '/'
}]);

// Get cookies
const cookies = await context.cookies();

// Clear cookies
await context.clearCookies();

// Save storage state (for reuse)
await context.storageState({ path: 'auth.json' });

// Set offline
await context.setOffline(true);

// Grant permissions
await context.grantPermissions(['clipboard-read']);

// Close context
await context.close();
💡 Key Benefits:
  • Isolation: Each context is completely independent
  • Speed: Faster than launching new browsers
  • Parallel: Run multiple contexts simultaneously
  • State: Save and reuse authentication state

Related: Browser,Page

Page

Core API

Purpose: Represents a single tab or window in browser context. Primary interface for interacting with web pages.

TypeScript - Page Methods
import { test } from '@playwright/test';

test('page methods', async ({ page }) => {
  // Navigation
  await page.goto('https://example.com');
  await page.goBack();
  await page.goForward();
  await page.reload();
  
  // Get URL and title
  console.log(page.url());
  console.log(await page.title());
  
  // Wait for navigation
  await page.waitForURL('**/dashboard');
  await page.waitForLoadState('networkidle');
  
  // Locators
  const button = page.locator('button');
  const heading = page.getByRole('heading');
  
  // Screenshot
  await page.screenshot({ path: 'screenshot.png' });
  
  // PDF generation
  await page.pdf({ path: 'page.pdf' });
  
  // Viewport
  await page.setViewportSize({ width: 1280, height: 720 });
  
  // Emulate media
  await page.emulateMedia({ colorScheme: 'dark' });
  
  // Evaluate JavaScript
  const result = await page.evaluate(() => {
    return document.title;
  });
  
  // Add script tag
  await page.addScriptTag({ 
    url: 'https://cdn.example.com/script.js' 
  });
  
  // Add style tag
  await page.addStyleTag({ 
    content: 'body { background: red; }' 
  });
  
  // Wait for timeout (use sparingly!)
  await page.waitForTimeout(1000);
  
  // Set extra HTTP headers
  await page.setExtraHTTPHeaders({
    'X-Custom-Header': 'value'
  });
  
  // Get content
  const html = await page.content();
  
  // Close page
  await page.close();
});
CategoryMethods
Navigationgoto(), goBack(), goForward(), reload()
Locatorslocator(), getByRole(), getByText(), getByTestId()
Actionsclick(), fill(), check(), selectOption()
WaitswaitForSelector(), waitForURL(), waitForLoadState()
Screenshotsscreenshot(), pdf()
JavaScriptevaluate(), evaluateHandle()

Related: Locator,goto()

Locator

Core API

Purpose: Represents element query with auto-waiting andauto-retry. Locators are evaluated when action is performed, not when created.

TypeScript - Locator Methods
import { test } from '@playwright/test';

test('locators', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Create locator (doesn't find element yet)
  const button = page.locator('button#submit');
  
  // Actions (triggers element finding)
  await button.click();
  await button.fill('text');
  await button.check();
  
  // Get properties
  const text = await button.textContent();
  const value = await button.inputValue();
  const attr = await button.getAttribute('class');
  
  // Check state
  await button.isVisible();
  await button.isEnabled();
  await button.isChecked();
  await button.isEditable();
  
  // Count matching elements
  const count = await button.count();
  
  // Get multiple elements
  const all = await button.all();
  for (const el of all) {
    console.log(await el.textContent());
  }
  
  // Get first/last/nth element
  await button.first().click();
  await button.last().click();
  await button.nth(2).click();
  
  // Filter locators
  await page.locator('button')
              .filter({ hasText: 'Submit' })
              .click();
  
  await page.locator('li')
              .filter({ has: page.locator('span.icon') })
              .click();
  
  // Chain locators
  await page.locator('.form')
              .locator('input[name="email"]')
              .fill('user@example.com');
  
  // And/Or combinations
  await page.locator('button')
              .and(page.locator('.primary'))
              .click();
  
  await page.locator('button')
              .or(page.locator('a.button'))
              .first()
              .click();
});
💡 Locator Key Features:
  • Lazy: Element not found until action is performed
  • Auto-wait: Waits for element to be actionable
  • Auto-retry: Retries until success or timeout
  • Strict: Fails if multiple elements match (unless using first/last/nth)

Related: getByRole(),click()

Locator Selection Methods

getByRole()

Locator Strategy

Purpose: Locate elements by ARIA role. Most recommended strategy - accessibility-friendly and resilient.

TypeScript - getByRole Examples
import { test } from '@playwright/test';

test('role-based locators', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Buttons
  await page.getByRole('button').click();
  await page.getByRole('button', { name: 'Submit' }).click();
  await page.getByRole('button', { name: /submit/i }).click(); // Regex
  
  // Links
  await page.getByRole('link', { name: 'Home' }).click();
  
  // Textboxes (inputs)
  await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com');
  await page.getByRole('textbox').first().fill('text');
  
  // Checkboxes
  await page.getByRole('checkbox', { name: 'Accept terms' }).check();
  
  // Radio buttons
  await page.getByRole('radio', { name: 'Option 1' }).check();
  
  // Combobox (select dropdown)
  await page.getByRole('combobox').selectOption('value');
  
  // Headings
  await page.getByRole('heading', { name: 'Dashboard' }).isVisible();
  await page.getByRole('heading', { level: 1 }).textContent();
  
  // Lists and list items
  await page.getByRole('list').count();
  await page.getByRole('listitem').first().click();
  
  // Tables
  await page.getByRole('table').isVisible();
  await page.getByRole('row').count();
  await page.getByRole('cell').first().textContent();
  
  // Dialogs (modals)
  await page.getByRole('dialog').isVisible();
  
  // Navigation
  await page.getByRole('navigation').locator('a').first().click();
  
  // Additional options
  await page.getByRole('button', {
    name: 'Submit',
    exact: true,      // Exact match
    disabled: false,  // Only enabled buttons
    pressed: true,    // Toggle buttons
    checked: true,    // Checkboxes/radios
    expanded: true    // Accordion/dropdown state
  }).click();
});
RoleHTML Elements
button<button>, <input type="button">
link<a href>
textbox<input type="text">, <input type="email">
checkbox<input type="checkbox">
radio<input type="radio">
combobox<select>
heading<h1> - <h6>
img<img>

Related: getByText(),getByLabel()

getByText()

Locator Strategy

Purpose: Locate elements by visible text content. User-centric approach - finds what users see.

TypeScript - getByText Examples
import { test } from '@playwright/test';

test('text-based locators', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Exact text match
  await page.getByText('Sign In').click();
  await page.getByText('Welcome Back').isVisible();
  
  // Exact match with quotes
  await page.getByText('Sign In', { exact: true }).click();
  
  // Partial match (contains)
  await page.getByText('Sign').click(); // Matches "Sign In", "Sign Up"
  
  // Regex
  await page.getByText(/sign (in|up)/i).click();
  
  // Case insensitive
  await page.getByText('submit', { exact: false }).click();
});

Related: getByRole(),getByLabel()

getByLabel()

Locator Strategy

Purpose: Locate form inputs by their associated label text.

TypeScript - getByLabel Examples
import { test } from '@playwright/test';

test('label-based locators', async ({ page }) => {
  await page.goto('https://example.com/form');
  
  // Find input by label text
  await page.getByLabel('Email').fill('user@example.com');
  await page.getByLabel('Password').fill('password123');
  await page.getByLabel('Remember me').check();
  
  // Exact match
  await page.getByLabel('Email Address', { exact: true }).fill('test@example.com');
  
  // Works with different label associations:
  // <label for="email">Email</label><input id="email">
  // <label>Email <input></label>
  // <input aria-labelledby="email-label">
});

Related: getByPlaceholder()

getByPlaceholder()

Locator Strategy
TypeScript
await page.getByPlaceholder('Enter email').fill('user@example.com');
await page.getByPlaceholder('Search...').fill('playwright');

getByTestId()

Locator Strategy

Purpose: Locate elements by data-testid attribute. Explicit test hooks - stable and recommended for testing.

HTML + TypeScript
<!-- HTML -->
<button data-testid="submit-button">Submit</button>
<div data-testid="user-profile">Profile Content</div>

// Test
await page.getByTestId('submit-button').click();
await page.getByTestId('user-profile').isVisible();
💡 Configure Custom Test ID Attribute:
playwright.config.ts
export default defineConfig({
  use: {
    testIdAttribute: 'data-test-id'  // Use data-test-id instead
  }
});

Related: getByRole()

getByAltText() / getByTitle()

Locator Strategy
TypeScript
// By alt text (images)
await page.getByAltText('Company Logo').isVisible();
await page.getByAltText('Profile Picture').click();

// By title attribute
await page.getByTitle('Close').click();
await page.getByTitle('Settings').hover();

User Actions & Interactions

click()

Action

Purpose: Click an element. Automatically waits for element to be visible, enabled, stable, and ready to receive events.

TypeScript - Click Methods
import { test } from '@playwright/test';

test('click actions', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Simple click
  await page.click('button#submit');
  await page.locator('button').click();
  
  // Click with options
  await page.click('button', {
    button: 'right',      // 'left' | 'right' | 'middle'
    clickCount: 2,        // Double click
    delay: 100,           // Delay between mousedown and mouseup
    position: { x: 10, y: 10 }, // Click at specific position
    force: true,          // Skip actionability checks
    timeout: 5000,        // Custom timeout
    trial: true           // Dry run (check if clickable without clicking)
  });
  
  // Double click
  await page.dblclick('button#edit');
  
  // Right click
  await page.click('button', { button: 'right' });
  
  // Click with modifiers
  await page.click('button', { modifiers: ['Control'] });
  await page.click('button', { modifiers: ['Shift', 'Meta'] });
  
  // Click at specific coordinates
  await page.click('canvas', { position: { x: 100, y: 200 } });
});

// Locator.click() - same options
test('locator click', async ({ page }) => {
  await page.goto('https://example.com');
  
  const button = page.locator('button#submit');
  await button.click();
  await button.click({ force: true }); // Skip checks
});
💡 Actionability Checks (Automatic):
  • Element is attached to DOM
  • Element is visible (not display:none, visibility:hidden)
  • Element is stable (not animating)
  • Element receives events (not covered by other elements)
  • Element is enabled (not disabled)

Related: dblclick(),hover()

fill()

Action

Purpose: Fill input field. Automatically clears field first, then types text. Waits for element to be editable.

TypeScript - Fill Examples
import { test } from '@playwright/test';

test('fill input fields', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Simple fill (clears first)
  await page.fill('input#username', 'john.doe');
  
  // Locator.fill()
  await page.locator('input[name="email"]').fill('user@example.com');
  
  // Fill with options
  await page.fill('input#search', 'playwright', {
    timeout: 5000,
    force: true  // Skip actionability checks
  });
  
  // Empty string to clear
  await page.fill('input#username', '');
});

// Difference: fill() vs type()
test('fill vs type', async ({ page }) => {
  await page.goto('https://example.com');
  
  // fill() - Clears first, then types (fast)
  await page.fill('input', 'new text'); // Old text replaced
  
  // type() - Just types (char by char)
  await page.locator('input').type('additional text'); // Appends
  
  // pressSequentially() - Types with delay (human-like)
  await page.locator('input').pressSequentially('slow typing', {
    delay: 100 // 100ms between chars
  });
});
⚠️ Important Notes:
  • fill() only works on <input>, <textarea>, [contenteditable]
  • Automatically clears existing value before typing
  • For special keys, use press() instead
  • Waits for element to be editable (not readonly/disabled)

Related: press(),type()

check() / uncheck()

Action

Purpose: Check or uncheck checkboxes and radio buttons.

TypeScript - Checkbox/Radio
import { test } from '@playwright/test';

test('check and uncheck', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Check checkbox
  await page.check('input[type="checkbox"]');
  await page.locator('#terms').check();
  
  // Uncheck checkbox
  await page.uncheck('input[type="checkbox"]');
  
  // Check if already checked
  const isChecked = await page.locator('#terms').isChecked();
  if (!isChecked) {
    await page.check('#terms');
  }
  
  // Radio buttons
  await page.check('input[value="option1"]');
  
  // With options
  await page.check('#terms', {
    force: true,
    timeout: 5000
  });
});

// Verify checkbox state
test('verify checkbox', async ({ page }) => {
  await page.goto('https://example.com');
  
  await page.check('#terms');
  await expect(page.locator('#terms')).toBeChecked();
  
  await page.uncheck('#terms');
  await expect(page.locator('#terms')).not.toBeChecked();
});

Related: click()

selectOption()

Action

Purpose: Select option(s) from <select> dropdown.

TypeScript - Select Dropdown
import { test } from '@playwright/test';

test('select options', async ({ page }) => {
  await page.goto('https://example.com');
  
  // By value attribute
  await page.selectOption('select#country', 'usa');
  
  // By visible text (label)
  await page.selectOption('select#country', { label: 'United States' });
  
  // By index (0-based)
  await page.selectOption('select#country', { index: 2 });
  
  // Multiple options (multi-select)
  await page.selectOption('select#skills', ['java', 'python', 'javascript']);
  
  // Multiple by label
  await page.selectOption('select#skills', [
    { label: 'Java' },
    { label: 'Python' }
  ]);
  
  // Using locator
  await page.locator('select#country').selectOption('usa');
  
  // Get selected value
  const value = await page.locator('select#country').inputValue();
  console.log(`Selected: ${value}`);
});
HTML - Dropdown Example
<select id="country" name="country">
  <option value="">Select Country</option>
  <option value="usa">United States</option>
  <option value="uk">United Kingdom</option>
  <option value="in">India</option>
</select>

<!-- Multi-select -->
<select id="skills" multiple>
  <option value="java">Java</option>
  <option value="python">Python</option>
  <option value="javascript">JavaScript</option>
</select>

Related: fill()

hover()

Action
TypeScript
import { test } from '@playwright/test';

test('hover actions', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Simple hover
  await page.hover('button#menu');
  
  // Hover and click submenu
  await page.hover('.menu-item');
  await page.click('.submenu-item');
  
  // Hover at specific position
  await page.hover('canvas', { position: { x: 100, y: 200 } });
  
  // With modifiers
  await page.hover('element', { modifiers: ['Control'] });
});

dragAndDrop()

Action
TypeScript
import { test } from '@playwright/test';

test('drag and drop', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Simple drag and drop
  await page.dragAndDrop('#source', '#target');
  
  // With options
  await page.dragAndDrop('#source', '#target', {
    sourcePosition: { x: 10, y: 10 },
    targetPosition: { x: 20, y: 20 },
    force: true
  });
  
  // Using locator
  const source = page.locator('#draggable');
  const target = page.locator('#droppable');
  await source.dragTo(target);
});

press() / keyboard

Action

Purpose: Press keyboard keys and key combinations.

TypeScript - Keyboard Actions
import { test } from '@playwright/test';

test('keyboard actions', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Press single key
  await page.press('input#search', 'Enter');
  await page.press('input', 'Tab');
  await page.press('input', 'Escape');
  
  // Keyboard shortcuts
  await page.press('input', 'Control+A'); // Select all
  await page.press('input', 'Control+C'); // Copy
  await page.press('input', 'Control+V'); // Paste
  await page.press('input', 'Meta+A');    // Mac: Cmd+A
  
  // Using keyboard object
  await page.keyboard.press('Enter');
  await page.keyboard.press('ArrowDown');
  
  // Type text (char by char)
  await page.keyboard.type('Hello World');
  
  // Key down/up
  await page.keyboard.down('Shift');
  await page.keyboard.press('KeyA'); // Types 'A'
  await page.keyboard.up('Shift');
  
  // Insert text (fast, no events)
  await page.keyboard.insertText('Quick insert');
  
  // Available keys:
  // F1-F12, Digit0-9, KeyA-Z
  // Enter, Escape, Tab, Backspace, Delete
  // ArrowUp, ArrowDown, ArrowLeft, ArrowRight
  // Home, End, PageUp, PageDown
  // Control, Alt, Shift, Meta
});

Related: fill()

setInputFiles()

Action

Purpose: Upload file(s) to <input type="file"> element.

TypeScript - File Upload
import { test } from '@playwright/test';
import path from 'path';

test('file upload', async ({ page }) => {
  await page.goto('https://example.com/upload');
  
  // Upload single file
  await page.setInputFiles('input[type="file"]', 'path/to/file.pdf');
  
  // Upload multiple files
  await page.setInputFiles('input[type="file"]', [
    'file1.pdf',
    'file2.png',
    'file3.docx'
  ]);
  
  // Using path.join for cross-platform
  await page.setInputFiles(
    'input[type="file"]',
    path.join(__dirname, '../testdata/document.pdf')
  );
  
  // Upload from buffer
  await page.setInputFiles('input[type="file"]', {
    name: 'file.txt',
    mimeType: 'text/plain',
    buffer: Buffer.from('File content here')
  });
  
  // Clear file input
  await page.setInputFiles('input[type="file"]', []);
  
  // Using locator
  const fileInput = page.locator('input[type="file"]');
  await fileInput.setInputFiles('document.pdf');
});

// Handle file chooser dialog
test('file chooser', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Start waiting for file chooser before clicking
  const fileChooserPromise = page.waitForEvent('filechooser');
  await page.click('button#upload');
  const fileChooser = await fileChooserPromise;
  
  await fileChooser.setFiles('path/to/file.pdf');
});

Assertions & Expectations

expect() - Web-First Assertions

Assertion

Purpose: Playwright's auto-retrying assertions for UI elements. Automatically waits until condition is met or timeout.

TypeScript - Element Assertions
import { test, expect } from '@playwright/test';

test('element assertions', async ({ page }) => {
  await page.goto('https://example.com');
  
  const locator = page.locator('h1');
  
  // Visibility
  await expect(locator).toBeVisible();
  await expect(locator).toBeHidden();
  await expect(locator).not.toBeVisible();
  
  // State
  await expect(locator).toBeEnabled();
  await expect(locator).toBeDisabled();
  await expect(locator).toBeEditable();
  await expect(locator).toBeChecked();
  await expect(locator).toBeFocused();
  
  // Text
  await expect(locator).toHaveText('Welcome');
  await expect(locator).toContainText('Wel');
  await expect(locator).toHaveText(/welcome/i); // Regex
  
  // Value (for inputs)
  await expect(page.locator('input')).toHaveValue('john@example.com');
  await expect(page.locator('input')).toHaveValue(/.*@example.com/);
  
  // Attribute
  await expect(locator).toHaveAttribute('href', '/home');
  await expect(locator).toHaveAttribute('class', /btn-primary/);
  
  // CSS
  await expect(locator).toHaveClass('active');
  await expect(locator).toHaveClass(/btn-/);
  await expect(locator).toHaveCSS('color', 'rgb(0, 0, 0)');
  
  // Count
  await expect(page.locator('.item')).toHaveCount(5);
  
  // Empty
  await expect(page.locator('input')).toBeEmpty();
  
  // Attached to DOM
  await expect(locator).toBeAttached();
  
  // In viewport
  await expect(locator).toBeInViewport();
});
TypeScript - Page Assertions
test('page assertions', async ({ page }) => {
  await page.goto('https://example.com');
  
  // URL
  await expect(page).toHaveURL('https://example.com/dashboard');
  await expect(page).toHaveURL(/.*dashboard.*/);
  
  // Title
  await expect(page).toHaveTitle('Dashboard - MyApp');
  await expect(page).toHaveTitle(/Dashboard/);
  
  // Screenshot comparison
  await expect(page).toHaveScreenshot('homepage.png');
  await expect(page.locator('.header')).toHaveScreenshot();
});
TypeScript - Custom Timeout
test('custom assertion timeout', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Wait up to 60 seconds
  await expect(page.locator('.slow-element')).toBeVisible({ 
    timeout: 60000 
  });
  
  // Soft assertions (doesn't stop test on failure)
  await expect.soft(page.locator('h1')).toHaveText('Wrong Text');
  await expect.soft(page.locator('h2')).toBeVisible();
  // Test continues, reports all failures
});
💡 Auto-Retry Magic:

Web-first assertions automatically retry every 100ms until the condition passes or timeout (default 5s). This eliminates flaky tests caused by timing issues.

Related: Locator

Generic Assertions

Assertion

Purpose: Standard Jest-style assertions for non-UI values(numbers, strings, arrays, objects). No auto-retry.

TypeScript - Generic Assertions
import { test, expect } from '@playwright/test';

test('generic assertions', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Get value first, then assert (no auto-retry)
  const count = await page.locator('.item').count();
  expect(count).toBe(5);
  expect(count).toBeGreaterThan(3);
  expect(count).toBeLessThanOrEqual(10);
  
  const text = await page.locator('h1').textContent();
  expect(text).toBe('Welcome');
  expect(text).toContain('Wel');
  expect(text).toMatch(/welcome/i);
  
  // Arrays
  const items = ['a', 'b', 'c'];
  expect(items).toHaveLength(3);
  expect(items).toContain('b');
  expect(items).toEqual(['a', 'b', 'c']);
  
  // Objects
  const user = { name: 'John', age: 30 };
  expect(user).toHaveProperty('name');
  expect(user).toMatchObject({ name: 'John' });
  expect(user).toEqual({ name: 'John', age: 30 });
  
  // Numbers
  expect(5).toBe(5);
  expect(5).not.toBe(10);
  expect(5).toBeGreaterThan(3);
  expect(5).toBeLessThan(10);
  
  // Booleans
  expect(true).toBeTruthy();
  expect(false).toBeFalsy();
  
  // Null/Undefined
  expect(null).toBeNull();
  expect(undefined).toBeUndefined();
  expect('value').toBeDefined();
});

Web-First (Auto-Retry)

  • expect(locator).toBeVisible()
  • expect(locator).toHaveText('text')
  • Use for UI elements
  • Retries automatically

Generic (Immediate)

  • expect(value).toBe(5)
  • expect(array).toHaveLength(3)
  • Use for variables/data
  • Checks once immediately

Related: expect() Web-First

Network Interception & API Testing

route()

Network

Purpose: Intercept and modify network requests/responses. Essential for mocking APIs and controlling network behavior.

TypeScript - Route Examples
import { test } from '@playwright/test';

test('network interception', async ({ page }) => {
  // Mock API response
  await page.route('**/api/users', route => {
    route.fulfill({
      status: 200,
      contentType: 'application/json',
      body: JSON.stringify([
        { id: 1, name: 'John Doe' },
        { id: 2, name: 'Jane Smith' }
      ])
    });
  });
  
  // Modify request
  await page.route('**/api/users', route => {
    const headers = {
      ...route.request().headers(),
      'Authorization': 'Bearer fake-token'
    };
    route.continue({ headers });
  });
  
  // Abort request (block resources)
  await page.route('**/*.{png,jpg,jpeg}', route => route.abort());
  
  // Modify response
  await page.route('**/api/config', async route => {
    const response = await route.fetch();
    const json = await response.json();
    json.feature_flag = true; // Modify
    route.fulfill({ response, json });
  });
  
  await page.goto('https://example.com');
});

// Monitor requests/responses
test('monitor network', async ({ page }) => {
  // Log all requests
  page.on('request', request => {
    console.log(`>> ${request.method()} ${request.url()}`);
  });
  
  // Log all responses
  page.on('response', response => {
    console.log(`<< ${response.status()} ${response.url()}`);
  });
  
  // Filter specific APIs
  page.on('response', async response => {
    if (response.url().includes('/api/users')) {
      console.log(await response.json());
    }
  });
  
  await page.goto('https://example.com');
});

// Wait for specific network activity
test('wait for network', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Wait for API call
  const response = await page.waitForResponse('**/api/users');
  const data = await response.json();
  console.log(data);
  
  // Wait for request
  const request = await page.waitForRequest('**/api/login');
  console.log(request.postDataJSON());
});

Related: request

request (APIRequestContext)

API Testing

Purpose: Built-in API testing without browser. Send HTTP requests directly.

TypeScript - API Testing
import { test, expect } from '@playwright/test';

test('API testing', async ({ request }) => {
  // GET request
  const response = await request.get('https://api.example.com/users');
  expect(response.ok()).toBeTruthy();
  expect(response.status()).toBe(200);
  
  const users = await response.json();
  expect(users).toHaveLength(10);
  
  // POST request
  const createResponse = await request.post('https://api.example.com/users', {
    data: {
      name: 'John Doe',
      email: 'john@example.com',
      role: 'admin'
    }
  });
  expect(createResponse.status()).toBe(201);
  
  const newUser = await createResponse.json();
  expect(newUser.name).toBe('John Doe');
  
  // PUT request
  await request.put(`https://api.example.com/users/${newUser.id}`, {
    data: {
      name: 'John Updated',
      role: 'user'
    }
  });
  
  // PATCH request
  await request.patch(`https://api.example.com/users/${newUser.id}`, {
    data: {
      email: 'newemail@example.com'
    }
  });
  
  // DELETE request
  const deleteResponse = await request.delete(
    `https://api.example.com/users/${newUser.id}`
  );
  expect(deleteResponse.status()).toBe(204);
  
  // With headers
  await request.get('https://api.example.com/protected', {
    headers: {
      'Authorization': 'Bearer token123',
      'Content-Type': 'application/json'
    }
  });
  
  // With query parameters
  await request.get('https://api.example.com/users', {
    params: {
      page: 1,
      limit: 10,
      sort: 'name'
    }
  });
});

// Create request context with base config
test('request context', async ({ playwright }) => {
  const context = await playwright.request.newContext({
    baseURL: 'https://api.example.com',
    extraHTTPHeaders: {
      'Authorization': 'Bearer token123',
      'Accept': 'application/json'
    }
  });
  
  // Now all requests use base URL and headers
  const response = await context.get('/users');
  const users = await response.json();
  
  await context.dispose();
});

// Hybrid: UI + API testing
test('hybrid test', async ({ page, request }) => {
  // Setup: Create data via API (fast)
  const user = await request.post('/api/users', {
    data: { name: 'Test User', email: 'test@example.com' }
  });
  const userId = (await user.json()).id;
  
  // Test: Verify in UI
  await page.goto(`/users/${userId}`);
  await expect(page.getByRole('heading')).toHaveText('Test User');
  
  // Cleanup: Delete via API (fast)
  await request.delete(`/api/users/${userId}`);
});
💡 API Testing Benefits:
  • Speed: No browser overhead
  • Setup/Teardown: Create test data via API
  • Verification: Check backend state directly
  • Hybrid Tests: Combine UI and API validation

Related: route()

Advanced Features

test() - Fixtures

Test Framework

Purpose: Playwright's test function with built-in fixtures for dependency injection and automatic setup/teardown.

TypeScript - Built-in Fixtures
import { test, expect } from '@playwright/test';

// Built-in fixtures
test('using fixtures', async ({ 
  page,         // Page instance (fresh per test)
  context,      // BrowserContext (isolated session)
  browser,      // Browser instance
  browserName,  // 'chromium' | 'firefox' | 'webkit'
  request       // APIRequestContext for API testing
}) => {
  await page.goto('https://example.com');
  console.log(`Running on: ${browserName}`);
  
  // Each test gets fresh fixtures
  // Automatic cleanup after test
});

// Test describe blocks
test.describe('Login Feature', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/login');
  });
  
  test.afterEach(async ({ page }) => {
    // Cleanup after each test
  });
  
  test('successful login', async ({ page }) => {
    // Test code
  });
  
  test('failed login', async ({ page }) => {
    // Test code
  });
});

// Test hooks
test.beforeAll(async () => {
  // Runs once before all tests in file
});

test.afterAll(async () => {
  // Runs once after all tests in file
});
TypeScript - Custom Fixtures
// fixtures/test.ts
import { test as base } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';

type MyFixtures = {
  loginPage: LoginPage;
  authenticatedPage: Page;
};

export const test = base.extend<MyFixtures>({
  // Page Object fixture
  loginPage: async ({ page }, use) => {
    const loginPage = new LoginPage(page);
    await use(loginPage);
    // Automatic cleanup
  },
  
  // Pre-authenticated page
  authenticatedPage: async ({ page }, use) => {
    await page.goto('/login');
    await page.fill('input[name="email"]', 'user@example.com');
    await page.fill('input[name="password"]', 'password123');
    await page.click('button[type="submit"]');
    await page.waitForURL('**/dashboard');
    
    await use(page);
    
    // Optional cleanup
    await page.goto('/logout');
  }
});

export { expect } from '@playwright/test';
TypeScript - Using Custom Fixtures
import { test, expect } from './fixtures/test';

test('test with custom fixtures', async ({ loginPage, authenticatedPage }) => {
  // loginPage and authenticatedPage are ready to use
  await loginPage.goto();
  await loginPage.login('user@example.com', 'password');
  
  // Or use pre-authenticated page
  await authenticatedPage.goto('/settings');
});
💡 Fixture Benefits:
  • Dependency Injection: Automatically provided to tests
  • Isolation: Fresh instance per test
  • Reusability: Share setup across tests
  • Auto-cleanup: Automatic teardown

Related: Page,BrowserContext

Trace Viewer

Debugging

Purpose: Record and debug test execution with screenshots, DOM snapshots, network logs, and source code.

TypeScript - Enable Tracing
// playwright.config.ts
export default defineConfig({
  use: {
    trace: 'on-first-retry',
    // Options: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry'
  }
});

// Programmatic tracing
test('with tracing', async ({ page, context }) => {
  // Start tracing
  await context.tracing.start({ 
    screenshots: true, 
    snapshots: true,
    sources: true
  });
  
  await page.goto('https://example.com');
  await page.click('button#submit');
  
  // Stop and save trace
  await context.tracing.stop({ 
    path: 'trace.zip' 
  });
});
Command Line
# View trace
npx playwright show-trace trace.zip

# Trace viewer features:
# - Timeline of all actions
# - Screenshots at each step
# - DOM snapshots (inspect HTML at any point)
# - Network requests/responses
# - Console logs
# - Source code that executed
# - Time-travel debugging
💡 Trace Viewer Benefits:
  • Post-mortem debugging: Inspect failed test without re-running
  • Time travel: Click any action to see exact state
  • Complete context: See everything that happened
  • Share with team: Send trace.zip file

screenshot() / video()

Capture
TypeScript - Screenshots
import { test } from '@playwright/test';

test('screenshots', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Full page screenshot
  await page.screenshot({ path: 'screenshot.png' });
  
  // Full page (including below fold)
  await page.screenshot({ 
    path: 'fullpage.png', 
    fullPage: true 
  });
  
  // Element screenshot
  const element = page.locator('.header');
  await element.screenshot({ path: 'header.png' });
  
  // Screenshot to buffer
  const buffer = await page.screenshot();
  
  // With options
  await page.screenshot({
    path: 'custom.png',
    type: 'jpeg',           // 'png' | 'jpeg'
    quality: 80,            // JPEG quality 0-100
    clip: { 
      x: 0, 
      y: 0, 
      width: 800, 
      height: 600 
    },
    omitBackground: true    // Transparent background
  });
});

// Auto-screenshots on failure
// playwright.config.ts
export default defineConfig({
  use: {
    screenshot: 'only-on-failure',
    // Options: 'off' | 'on' | 'only-on-failure'
  }
});
TypeScript - Videos
// playwright.config.ts
export default defineConfig({
  use: {
    video: 'retain-on-failure',
    // Options: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry'
    
    videoSize: { width: 1280, height: 720 }
  }
});

// Access video path
test('get video', async ({ page }, testInfo) => {
  await page.goto('https://example.com');
  // Video automatically recorded
});

test.afterEach(async ({ page }, testInfo) => {
  if (testInfo.status !== 'passed') {
    const videoPath = await page.video()?.path();
    console.log(`Video: ${videoPath}`);
  }
});
TypeScript - Visual Regression
import { test, expect } from '@playwright/test';

test('visual regression', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Compare full page
  await expect(page).toHaveScreenshot('homepage.png');
  
  // Compare element
  await expect(page.locator('.header')).toHaveScreenshot('header.png');
  
  // With threshold
  await expect(page).toHaveScreenshot('page.png', {
    maxDiffPixels: 100,
    threshold: 0.2
  });
  
  // Mask dynamic regions
  await expect(page).toHaveScreenshot('page.png', {
    mask: [page.locator('.dynamic-ad')],
  });
});

// Update baselines
// npx playwright test --update-snapshots

Codegen (Test Generator)

Tool

Purpose: Record browser interactions and generate test code automatically.

Command Line
# Start codegen
npx playwright codegen https://example.com

# With specific browser
npx playwright codegen --browser=firefox https://example.com

# With device emulation
npx playwright codegen --device="iPhone 13" https://example.com

# Save to file
npx playwright codegen --target=typescript -o tests/test.spec.ts https://example.com

# With authentication state
npx playwright codegen --load-storage=auth.json https://example.com

# With viewport
npx playwright codegen --viewport-size=1280,720 https://example.com

# Record in specific locale/timezone
npx playwright codegen --lang=de-DE --timezone="Europe/Berlin" https://example.com
💡 Codegen Features:
  • Record: Click, type, navigate - all recorded
  • Locators: Auto-generates resilient locators
  • Inspector: Shows locator for any element
  • Copy: Copy locator or assertion from UI
  • Multiple languages: TypeScript, JavaScript, Python, C#, Java
⚠️ Best Practice:

Use Codegen to learn locators and get started quickly, but always refactor generated code into maintainable Page Objects and proper test structure.

Frame & iFrame Handling

frameLocator()

Frame

Purpose: Locate and interact with elements inside iframes. No need to switch contexts like Selenium.

TypeScript - Frame Handling
import { test } from '@playwright/test';

test('iframe handling', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Using frameLocator (recommended)
  const frame = page.frameLocator('iframe#payment-frame');
  await frame.locator('input#card-number').fill('4111111111111111');
  await frame.locator('button#pay').click();
  
  // Multiple ways to locate frame
  const frameByName = page.frameLocator('[name="iframe-name"]');
  const frameByUrl = page.frameLocator('iframe[src*="payment"]');
  
  // Nested iframes
  const parentFrame = page.frameLocator('iframe#parent');
  const childFrame = parentFrame.frameLocator('iframe#child');
  await childFrame.locator('button').click();
  
  // No need to switch back (unlike Selenium)
  await page.locator('button#main-button').click();
});

// Using frame() - less common
test('using frame method', async ({ page }) => {
  await page.goto('https://example.com');
  
  // By name
  const frame = page.frame({ name: 'iframe-name' });
  await frame?.click('button');
  
  // By URL pattern
  const frame2 = page.frame({ url: /.*payment.*/ });
  await frame2?.fill('input', 'text');
  
  // Get all frames
  const frames = page.frames();
  console.log(`Total frames: ${frames.length}`);
});
💡 Key Differences from Selenium:
  • No switching: Access frame elements directly
  • Auto-retry: Waits for frame to load
  • Chainable: Natural syntax for nested frames
  • No switch back: Main page always accessible

Related: Locator

Dialogs & Popup Handling

Dialog (Alert, Confirm, Prompt)

Dialog

Purpose: Handle JavaScript dialogs (alert, confirm, prompt).

TypeScript - Dialog Handling
import { test } from '@playwright/test';

test('handle dialogs', async ({ page }) => {
  await page.goto('https://example.com');
  
  // Handle alert
  page.on('dialog', async dialog => {
    console.log(`Dialog type: ${dialog.type()}`);
    console.log(`Dialog message: ${dialog.message()}`);
    await dialog.accept();
  });
  
  await page.click('button#show-alert');
  
  // Handle confirm (accept or dismiss)
  page.on('dialog', async dialog => {
    if (dialog.type() === 'confirm') {
      await dialog.accept();  // Click OK
      // OR
      await dialog.dismiss(); // Click Cancel
    }
  });
  
  // Handle prompt (enter text)
  page.on('dialog', async dialog => {
    if (dialog.type() === 'prompt') {
      await dialog.accept('User input text');
    }
  });
  
  // Default behavior (auto-dismiss if no handler)
  page.on('dialog', dialog => dialog.dismiss());
});

// Wait for dialog
test('wait for dialog', async ({ page }) => {
  await page.goto('https://example.com');
  
  const dialogPromise = page.waitForEvent('dialog');
  await page.click('button#trigger-dialog');
  const dialog = await dialogPromise;
  
  console.log(dialog.message());
  await dialog.accept();
});
Dialog TypeJavaScriptActions
alertalert("Message")accept()
confirmconfirm("Question?")accept() or dismiss()
promptprompt("Enter text:")accept("text") or dismiss()
beforeunloadLeave page confirmationaccept() or dismiss()

Quick Reference Summary

🎯 Most Important Keywords to Master:
  • Core APIs: Browser, BrowserContext, Page, Locator
  • Locators: getByRole(), getByText(), getByLabel(), getByTestId()
  • Actions: click(), fill(), check(), selectOption(), hover()
  • Assertions: expect().toBeVisible(), toHaveText(), toBeEnabled()
  • Navigation: goto(), waitForURL(), waitForLoadState()
  • Network: route(), request (API testing)
  • Advanced: test() fixtures, trace viewer, codegen
CategoryEssential KeywordsWhen to Use
LocatorsgetByRole(), getByTestId()Finding elements (prefer role-based)
Actionsclick(), fill(), check()User interactions
Assertionsexpect().toBeVisible()Verifying UI state (auto-retry)
WaitswaitForLoadState()Page loading (avoid waitForTimeout)
Networkroute(), requestMocking APIs, API testing
Debuggingtrace, screenshot()Troubleshooting failures
💡 Playwright vs Selenium - Key API Differences:
  • Locators: Playwright locators are lazy and auto-wait; Selenium elements are eager
  • Waits: Playwright auto-waits; Selenium needs explicit waits
  • Assertions: Playwright has web-first assertions; Selenium uses external libraries
  • API Testing: Playwright has built-in request; Selenium needs RestAssured
  • Context: Playwright uses BrowserContext; Selenium uses WebDriver instances

Playwright Pattern

// Auto-wait, auto-retry
await page.getByRole('button')
  .click();

await expect(page.locator('h1'))
  .toHaveText('Welcome');

Selenium Pattern

// Manual wait required
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement button = wait.until(
  ExpectedConditions.elementToBeClickable(By.id("btn"))
);
button.click();

🎉 You've Mastered Playwright APIs!

This reference guide covered all essential Playwright keywords and concepts. Use it as your go-to resource for test automation with Playwright.

📚 Continue Learning:
  • Explore Playwright Interview Preparation guide
  • Build production frameworks with Framework Best Practices
  • Practice with real projects using TypeScript
  • Master advanced features: fixtures, trace viewer, visual testing

Continue Learning