LearnThatStack Ace your next interview
Testing · Free

Playwright.
Interview cheat sheet.

Quick reference for Playwright - sectioned for fast scanning. Skim the part you're shaky on, walk in confident.

Testing 15-section reference ~6 min read

Summary

Playwright is a modern end-to-end testing framework developed by Microsoft that enables reliable cross-browser automation for web applications. It supports Chromium, Firefox, and WebKit with a single API, providing features like auto-waiting, network interception, and mobile emulation. Unlike traditional testing tools, Playwright runs tests in parallel, handles multiple browser contexts, and offers superior debugging capabilities with trace viewer and codegen tools. Its architecture allows for fast, reliable tests with built-in retry mechanisms and comprehensive reporting. This cheatsheet covers Playwright fundamentals, best practices, and advanced patterns essential for technical interviews.

1. Introduction & Setup

What is Playwright?

  • Cross-browser automation library by Microsoft
  • Supports Chromium, Firefox, and WebKit
  • Auto-wait functionality, network interception, and mobile emulation

Installation

npm init playwright@latest
# or
npm install -D @playwright/test

Basic Test Structure

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

test('basic test', async ({ page }) => {
  await page.goto('https://example.com');
  await expect(page).toHaveTitle('Example Domain');
});

2. Selectors & Locators

Locator Strategies

// Text selector
await page.locator('text=Submit').click();

// CSS selector
await page.locator('.button-class').click();

// XPath
await page.locator('xpath=//button[@id="submit"]').click();

// Role-based (Accessibility)
await page.getByRole('button', { name: 'Submit' }).click();

// Test ID (Recommended)
await page.getByTestId('submit-button').click();

// Chaining locators
await page.locator('article').filter({ hasText: 'Product' }).click();

Advanced Selectors

// nth element
await page.locator('li').nth(2).click();

// Has/Has-Text
await page.locator('article', { has: page.locator('.highlight') }).click();

// Multiple selectors
await page.locator('button:has-text("Submit"), input[type="submit"]').click();

3. Actions & Interactions

Basic Actions

// Click
await page.click('button');
await page.dblclick('button');
await page.click('button', { button: 'right' }); // right-click

// Type
await page.fill('input[name="email"]', 'test@example.com');
await page.type('input[name="email"]', 'test@example.com'); // types char by char

// Select
await page.selectOption('select#country', 'USA');

// Check/Uncheck
await page.check('input[type="checkbox"]');
await page.uncheck('input[type="checkbox"]');

// Hover
await page.hover('button');

// Focus
await page.focus('input');

File Upload & Download

// Upload
await page.setInputFiles('input[type="file"]', 'path/to/file.pdf');

// Download
const [download] = await Promise.all([
  page.waitForEvent('download'),
  page.click('a#download-link')
]);
await download.saveAs('/path/to/save/file.pdf');

Keyboard & Mouse

// Keyboard
await page.keyboard.press('Enter');
await page.keyboard.down('Shift');
await page.keyboard.up('Shift');
await page.keyboard.type('Hello World');

// Mouse
await page.mouse.move(100, 100);
await page.mouse.down();
await page.mouse.up();
await page.mouse.click(100, 100);

4. Waits & Assertions

Auto-waiting

Playwright automatically waits for:

  • Element to be attached to DOM
  • Element to be visible
  • Element to be stable (not animating)
  • Element to be enabled
  • Element to receive events

Explicit Waits

// Wait for selector
await page.waitForSelector('.success-message');

// Wait for load state
await page.waitForLoadState('networkidle');

// Wait for URL
await page.waitForURL('**/success');

// Wait for function
await page.waitForFunction(() => document.querySelector('.counter').innerText === '5');

// Wait with timeout
await page.waitForSelector('.modal', { timeout: 5000 });

Assertions

// Page assertions
await expect(page).toHaveTitle('Home Page');
await expect(page).toHaveURL(/.*success/);

// Element assertions
await expect(page.locator('.message')).toBeVisible();
await expect(page.locator('.message')).toHaveText('Success');
await expect(page.locator('.message')).toContainText('Success');
await expect(page.locator('input')).toHaveValue('john@example.com');
await expect(page.locator('button')).toBeEnabled();
await expect(page.locator('input[type="checkbox"]')).toBeChecked();

// Count assertions
await expect(page.locator('li')).toHaveCount(5);

// Attribute assertions
await expect(page.locator('img')).toHaveAttribute('alt', 'Logo');

5. Page Object Model (POM)

Basic POM Structure

// pages/LoginPage.js
export class LoginPage {
  constructor(page) {
    this.page = page;
    this.emailInput = page.locator('input[name="email"]');
    this.passwordInput = page.locator('input[name="password"]');
    this.loginButton = page.locator('button[type="submit"]');
  }

  async login(email, password) {
    await this.emailInput.fill(email);
    await this.passwordInput.fill(password);
    await this.loginButton.click();
  }
}

// test.spec.js
import { LoginPage } from './pages/LoginPage';

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

6. Advanced Features

Network Interception

// Mock API response
await page.route('**/api/users', route => {
  route.fulfill({
    status: 200,
    body: JSON.stringify([{ id: 1, name: 'John' }])
  });
});

// Modify request
await page.route('**/api/data', route => {
  const headers = {
    ...route.request().headers(),
    'Authorization': 'Bearer token123'
  };
  route.continue({ headers });
});

// Abort requests
await page.route('**/*.png', route => route.abort());

Multiple Pages/Contexts

// New page in same context
const newPage = await context.newPage();

// New context (isolated session)
const newContext = await browser.newContext();
const page2 = await newContext.newPage();

// Handle popups
const [popup] = await Promise.all([
  page.waitForEvent('popup'),
  page.click('a[target="_blank"]')
]);

Screenshots & Videos

// Screenshot
await page.screenshot({ path: 'screenshot.png' });
await page.screenshot({ fullPage: true });

// Element screenshot
await page.locator('.header').screenshot({ path: 'header.png' });

// Video recording (in config)
use: {
  video: 'retain-on-failure'
}

Mobile Emulation

// Device emulation
const iPhone = playwright.devices['iPhone 12'];
const context = await browser.newContext({
  ...iPhone,
  locale: 'en-US',
  geolocation: { longitude: 12.4924, latitude: 41.8902 },
  permissions: ['geolocation']
});

7. Configuration

Basic Config (playwright.config.js)

module.exports = {
  timeout: 30000,
  retries: 2,
  workers: 4,
  use: {
    baseURL: 'http://localhost:3000',
    headless: true,
    viewport: { width: 1280, height: 720 },
    screenshot: 'only-on-failure',
    trace: 'on-first-retry'
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } }
  ]
};

8. API Testing

test('API test', async ({ request }) => {
  // GET request
  const response = await request.get('/api/users');
  expect(response.ok()).toBeTruthy();
  const users = await response.json();
  expect(users).toHaveLength(3);

  // POST request
  const newUser = await request.post('/api/users', {
    data: {
      name: 'John Doe',
      email: 'john@example.com'
    }
  });
  expect(newUser.status()).toBe(201);
});

9. Best Practices

1. Use Data Test IDs

<button data-testid="submit-button">Submit</button>
await page.getByTestId('submit-button').click();

2. Avoid Hard Waits

// ❌ Bad
await page.waitForTimeout(5000);

// ✅ Good
await page.waitForSelector('.success-message');

3. Use Locator over ElementHandle

// ❌ Bad
const button = await page.$('button');
await button.click();

// ✅ Good
await page.locator('button').click();

4. Proper Error Handling

try {
  await page.click('button', { timeout: 3000 });
} catch (error) {
  console.log('Button not found:', error);
}

5. Reusable Functions

async function login(page, username, password) {
  await page.fill('#username', username);
  await page.fill('#password', password);
  await page.click('#login-button');
}

10. Key Interview Concepts

Playwright vs Other Testing Tools

Feature Playwright Cypress Selenium
Browser Support Chrome, Firefox, Safari Chrome-family All browsers
Architecture Out-of-process In-browser WebDriver protocol
Auto-waiting Built-in Built-in Manual
Network Control Full control Limited Limited
Multiple Tabs Yes No Yes
Speed Fast (parallel) Fast Slower
Mobile Testing Device emulation Viewport only Real devices

Handling Dynamic Content

// Strategy 1: Wait for specific text
await page.waitForSelector('text=Loading complete');

// Strategy 2: Wait for element state
await page.waitForSelector('.spinner', { state: 'hidden' });
await page.waitForSelector('.content', { state: 'visible' });

// Strategy 3: Wait for network idle
await page.waitForLoadState('networkidle');

// Strategy 4: Custom wait function
await page.waitForFunction(() => 
  document.querySelector('.counter').innerText === '100'
);

Frame Handling Strategies

// Method 1: frameLocator (Recommended)
const frame = page.frameLocator('#frame-id');
await frame.locator('button').click();

// Method 2: frame() for direct access
const frameElement = await page.frame({ url: /frame\.html/ });
await frameElement.click('button');

// Method 3: Nested frames
const nestedFrame = page
  .frameLocator('#outer-frame')
  .frameLocator('#inner-frame');

Dialog & Popup Handling

// Alert/Confirm/Prompt handling
page.on('dialog', async dialog => {
  console.log(`Dialog message: ${dialog.message()}`);
  if (dialog.type() === 'confirm') {
    await dialog.accept();
  } else if (dialog.type() === 'prompt') {
    await dialog.accept('Input value');
  }
});

// Popup window handling
const [popup] = await Promise.all([
  page.waitForEvent('popup'),
  page.click('a[target="_blank"]')
]);
await popup.waitForLoadState();

Authentication Patterns

// Pattern 1: Session Storage
await context.storageState({ path: 'auth.json' });
const authenticatedContext = await browser.newContext({
  storageState: 'auth.json'
});

// Pattern 2: Global Setup
// global-setup.js
module.exports = async config => {
  const browser = await chromium.launch();
  const page = await browser.newPage();
  await page.goto('https://app.com/login');
  await page.fill('#email', 'user@example.com');
  await page.fill('#password', 'password');
  await page.click('#login');
  await page.context().storageState({ path: 'auth.json' });
  await browser.close();
};

// Pattern 3: API Authentication
const context = await browser.newContext({
  extraHTTPHeaders: {
    'Authorization': `Bearer ${token}`
  }
});

Debugging Techniques

Method Use Case Command/Code
Pause Interactive debugging await page.pause()
Slow Motion Visual debugging { slowMo: 500 }
Headed Mode See browser --headed
Debug Mode Step through --debug
Trace Viewer Post-mortem analysis trace: 'on'
Videos Visual recordings video: 'on'
Console Logs Runtime logs page.on('console')

11. Performance Testing

// Measure page load time
const startTime = Date.now();
await page.goto('https://example.com');
const loadTime = Date.now() - startTime;

// Performance metrics
const metrics = await page.evaluate(() => ({
  domContentLoaded: performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart,
  load: performance.timing.loadEventEnd - performance.timing.navigationStart
}));

12. Accessibility Testing

// Check accessibility
const accessibilitySnapshot = await page.accessibility.snapshot();

// ARIA attributes
await expect(page.getByRole('button', { name: 'Submit' })).toHaveAttribute('aria-disabled', 'false');

// Keyboard navigation
await page.keyboard.press('Tab');
await page.keyboard.press('Enter');

Quick Reference Commands

# Run tests
npx playwright test

# Run specific test
npx playwright test login.spec.js

# Run in headed mode
npx playwright test --headed

# Run with specific browser
npx playwright test --project=chromium

# Debug mode
npx playwright test --debug

# Generate code
npx playwright codegen https://example.com

# Show report
npx playwright show-report

Interview Preparation Checklist

  • ✅ Core Concepts: Auto-waiting, locators, selectors
  • ✅ Actions: Click, type, select, upload, keyboard/mouse
  • ✅ Assertions: Built-in expect API, auto-retry
  • ✅ Page Object Model: Structure, implementation
  • ✅ Network: Mocking, interception, API testing
  • ✅ Multi-page: Contexts, popups, frames
  • ✅ Debugging: Trace viewer, screenshots, videos
  • ✅ Configuration: Projects, devices, parallelization
  • ✅ Best Practices: Test isolation, data-testid, retries
  • ✅ Advanced: Authentication, performance, accessibility

Key Takeaway: Playwright excels at reliable cross-browser testing with features like auto-waiting, network control, and parallel execution. Focus on understanding locator strategies, handling dynamic content, and implementing maintainable test architectures using Page Object Model. Its superior debugging tools and built-in features make it ideal for modern web application testing.

Found this useful? Pass it on.
Pro · $10/mo

The sheet is free. Pro goes deeper.

Pro opens the full question library behind every sheet, every refresher and a monthly AI allowance. One subscription, all formats.

Full question library All refreshers Cancel anytime