Overview
Playwright is a Node.js library for cross-browser end-to-end testing. It enables reliable testing across Chromium, Firefox and WebKit.
// Install Playwright
npm i -D @playwright/test
Key Features
Core Concepts
Browser Types
Playwright supports 3 browser types -
const { chromium } = require('playwright');
const browser = await chromium.launch();
Browser Contexts
Browser contexts isolates browser state like cookies, storage etc. New context guarantees clean state.
const context = await browser.newContext();
Pages
Pages represent tabs and hold the actual page state. New pages open fresh empty tabs.
const page = await context.newPage();
Basic Examples
Navigate to page
await page.goto('<https://www.example.com>');
Get page title
await page.title();
Click element
await page.click('button');
Type text
await page.fill('input', 'text');
Assertions
// Assertion helpers
expect(page.url()).toBe('<https://example.com>');
await expect(page.locator('h1')).toHaveText('Title');
Screenshot
await page.screenshot({ path: 'screenshot.png' });
Advanced Interactions
Clicking Elements
Options like click count, button type etc.:
await page.click('#submit', { clickCount: 2 });
await page.click('#checkbox', { button: 'right' });
Typing Text
Handle delays while typing, useful for UI/UX testing:
await page.type('#address', 'Hello World', { delay: 100 });
Element States
Force element states before interacting:
await page.focus('#email');
await page.check('#checkbox');
Selector Strategies
Playwright offers different selector engines to query elements:
CSS Selector
await page.click('button');
Text Selector
Selects elements based on inner text
await page.click('text=Login');
XPath Selector
Full XPath support
await page.click('//button[text()="Login"]');
ID Selector
Select element by ID attribute
await page.click('#login-button');
Data Test ID
Custom test id attributes for unique selection
await page.click('[data-testid="submit-form"]');
By Role
Semantic selector by element role
await page.click('.role-button');
Advanced Element Selectors
Pass selector functions to customize selection:
await page.locator(({ hasText }) => hasText('Save')).click();
DOM Path:
await page.getByTestId('email-id', { path: 'form div' });
Select visible elements:
await page.getByText('Save', { exact: true, visible: true });
Advanced Usage
Configure Browser Settings
Settings like viewport size, user agent etc. can be configured for browsers using
await chromium.launch({
headless: false,
slowMo: 50,
viewport: {width: 1280, height: 720}
});
Intercept Network Requests
Network requests can be mocked and stubbed for testing using route handlers.
await context.route('**/*.{png,jpg}', route => {
route.abort();
});
This aborts all image requests.
Emulate Devices
Playwright allows emulation of devices like iPhone, iPad etc.
const context = await browser.newContext({
...devices['iPhone X']
});
Local Storage
Handle browser storage (localStorage, sessionStorage)
await context.storageState({path: 'state.json'}); // save storage state
await context.storageState({path: 'state.json'}); // restore state
Multi-Browser Testing
Run same tests across Chromium, Firefox and WebKit using test runner.
npm test // runs tests over all 3 browsers
Docker Images
Playwright provides official docker images with browsers installed. This removes need for browser drivers on CI.
docker pull mcr.microsoft.com/playwright:v1.24.0-focal
Tracing Viewer
Playwright captures browser traces during test execution which helps debug tests.
npx playwright show-trace trace.zip
Additional Assertions
Element State
Assert element states like disabled, visible etc.
await expect(page.locator('button')).toBeDisabled();
Visual Comparison
Compare screenshots to baseline images
await expect(page.screenshot()).toMatchSnapshot('landing.png');
Wait Helpers
Wait For Selector
Wait until selector is available before performing action
await page.waitForSelector('div.loaded');
Wait For Navigation
Wait for navigation to finish before asserting page state
await page.waitForNavigation();
Authentication
Persist Credentials
Use context storage state to persist login sessions
await context.storageState({path: 'state.json'});
Reporting
Playwright Cloud
Upload test results and artifacts to Playwright Cloud
npm test --project=myCloudProject
Dynamic Mock Response
Return different mock data based on request:
await context.route('**/*.json', route => {
if (route.request().url().includes('data')) {
route.fulfill({
status: 200,
body: JSON.stringify({data: 'mock'})
});
} else {
route.abort();
}
});
GraphQL Mocking
Stub GraphQL API response with dummy data:
await context.route('<https://api.graph.cool/simple/v1/movies>', route => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({data: {movies: ['Movie 1']}})
});
});
Devices and Emulation
Emulating various mobile devices:
// iPhone XR
await context.emulate(devices['iPhone XR']);
// Google Pixel 5
await context.emulate(devices['Pixel 5']);
Device specific viewports:
await context.emulateViewport(1920, 1080); // Full HD
await context.emulateViewport(360, 640); // iPhone 5/SE
Assertions & Validation
Element count assertion:
await expect(page.locator('.items')).toHaveCount(5);
Validate JS expression:
await page.waitForFunction(() => window.innerWidth < 1000);
Assert response times:
await expect(page).toRespondIn(50); // ms
Browser Context Sharing
You can share data between browser contexts using the
// Saving browser context storage state
const storageState = await context.storageState({ path: 'auth.json' });
// Create a new browser context and restore storage state
const newContext = await browser.newContext();
await newContext.addCookies(storageState.cookies);
await newContext.clearCookies(); // If needed, clear cookies before adding
await newContext.addCookies(storageState.cookies);
Element Hover and Scroll
You can hover over an element using the
// Hover over an element
await page.hover('#element-to-hover');
// Scroll to an element
await page.$eval('#element-to-scroll-to', (element) => {
element.scrollIntoView();
});
File Upload and Download
To interact with file upload buttons, you can use the
// Upload a file
await page.setInputFiles('#file-input', 'path/to/file.txt');
// Handle file downloads
const [download] = await Promise.all([
context.waitForEvent('download'),
page.click('#download-button'),
]);
await download.saveAs('path/to/save/file.txt');
Working with Frames and iframes
You can interact with frames and iframes using the
// Switch to a frame by name or ID
const frame = page.frame('frameName');
await frame.click('#element-in-frame');
// Switch back to the main frame
await page.waitForLoadState('domcontentloaded');
Headless Mode
You can run Playwright tests in headless mode by configuring the
await chromium.launch({ headless: true });
Page Events
You can listen for and handle various page events using the
page.on('dialog', async (dialog) => {
console.log('Dialog message:', dialog.message());
await dialog.accept();
});
page.on('console', (message) => {
console.log('Console message:', message.text());
});
Error Handling
Use try-catch blocks to handle errors that may occur during test execution.
try {
// Perform actions that may throw errors
} catch (error) {
console.error('An error occurred:', error.message);
}
Page Navigation Strategies
You can navigate back and forward in the browser's history using the
await page.goBack(); // Navigate back
await page.goForward(); // Navigate forward
Parallel Testing
To run tests in parallel, you can leverage Playwright's built-in test runner.
npx playwright test --workers 4
Custom Test Configuration
Set up custom test configurations for different environments using Playwright's configuration files.
// playwright.config.js
module.exports = {
projects: [
{
name: 'dev',
use: { ... },
},
{
name: 'prod',
use: { ... },
},
],
};
Page Objects
Implement the page object pattern to separate page interactions from test code.
// Example page object
class LoginPage {
constructor(page) {
this.page = page;
}
async login(username, password) {
// Implement login logic
}
}
Test Structure
Follow a clear test structure that includes setup, execution, and teardown phases. Use before and after hooks to handle common setup and cleanup tasks.
// Example using Jest hooks
beforeEach(async () => {
// Perform common setup steps here
await page.goto('<https://example.com>');
});
afterEach(async () => {
// Perform common cleanup tasks here
});
test('Test case description', async () => {
// Test execution code
});
Timing Issues
Ensure that your tests handle asynchronous operations correctly. Use
// Wait for an element to become visible
await page.waitForSelector('.my-element', { state: 'visible' });
Unhandled Errors
Always include error handling in your tests to catch and handle exceptions gracefully. Use try-catch blocks to capture errors and provide informative error messages.
try {
// Test actions that may throw errors
} catch (error) {
console.error('An error occurred:', error.message);
}
Measuring Page Load Time
You can measure the time it takes for a page to load using the
const startTime = Date.now();
await page.goto('<https://example.com>');
const loadTime = Date.now() - startTime;
console.log(`Page loaded in ${loadTime}ms`);
Network Throttling
Playwright allows you to simulate different network conditions, such as slow 3G or offline mode, to test how your application behaves under varying network speeds.
// Simulate slow 3G network
await context.route('**/*', (route) => {
route.throttle('Regular3G');
route.continue();
});
Analyzing Performance Metrics
You can gather various performance metrics using the
await page.goto('<https://example.com>');
const metrics = await page.metrics();
console.log('Performance metrics:', metrics);