Playwright Testing Guide
for QA Engineers
Table of Contents
Chapter 1: Introduction to Playwright
●​ What is Playwright?​
●​ Why Choose Playwright Over Selenium or Cypress?​
●​ Supported Languages and Browsers​
●​ Installing Playwright​
●​ First Script: A "Hello World" Test​
Chapter 2: Core Concepts and Architecture
●​ Understanding the Playwright Test Runner​
●​ Browsers and Contexts​
●​ Pages and Selectors​
●​ Handling Events and Waits​
●​ Playwright vs Puppeteer​
Chapter 3: Writing Tests with Playwright
●​ Test Syntax and Structure​
●​ Locators and Assertions​
●​ Interacting with UI Elements (click, type, check, etc.)​
●​ Handling Alerts, Frames, and Popups​
●​ Parallel Testing and Test Hooks​
Chapter 4: Advanced Testing Scenarios
●​ Visual Regression Testing​
●​ Handling Authentication (Basic, OAuth, Token)​
●​ API Testing with Playwright​
●​ File Uploads and Downloads​
●​ Emulating Devices and Geolocation​
Chapter 5: Test Automation and CI Integration
●​ Headless Mode and CLI Options​
●​ Integrating with CI Tools (GitHub Actions, Jenkins)​
●​ Parallel Execution in CI​
●​ Generating HTML Reports and Logs​
●​ Test Retries and Flaky Test Management​
Chapter 6: Best Practices and Debugging
●​ Structuring Test Suites for Scalability​
●​ Using Trace Viewer and Debug Mode​
●​ Test Data Management and Fixtures​
●​ Common Pitfalls and Troubleshooting​
●​ Migration Tips (from Selenium or Cypress to Playwright)​
Chapter 1: Introduction to Playwright
What is Playwright?
Playwright is an open-source test automation framework developed by Microsoft that
enables reliable end-to-end testing for modern web applications. It allows developers and
QA engineers to script browser interactions with ease, covering everything from simple UI
clicks to complex multi-page flows.
At its core, Playwright automates Chromium, Firefox, and WebKit browsers using a single
API. This cross-browser capability makes it a powerful tool for cross-platform testing —
including both desktop and mobile views — without switching libraries or rewriting tests.
What is Playwright Testing?
Playwright testing refers to the practice of using the Playwright framework to perform
automated checks on web applications. These tests simulate real user interactions — like
clicking, scrolling, typing, and navigation — to ensure your app behaves as expected across
different browsers and devices.
With Playwright, you can write tests for:
●​ UI workflows: Test full user journeys like login, checkout, or onboarding.​
●​ Responsive design: Verify how your site behaves on different screen sizes and
devices.​
●​ Cross-browser compatibility: Catch bugs that appear only in specific browsers.​
●​ Network conditions and API handling: Simulate slow network speeds or mock API
responses.​
Playwright's ability to launch browsers in headless or headed mode, along with parallel
test execution and auto-waiting for elements, makes it fast and reliable — especially
when integrated into CI/CD pipelines.
Why Choose Playwright Over Selenium or Cypress?
While Selenium has long been the industry standard, and Cypress has gained popularity for
front-end testing, Playwright brings a unique blend of performance, modern APIs, and
multi-browser support. Here's how it stands out:
Feature Selenium Cypress Playwright
Browser Support Chrome, Firefox,
Safari, Edge
Chrome-family
only
Chrome, Firefox, Safari
(WebKit)
Language Support Java, Python, C#,
JS
JavaScript
only
JavaScript, TypeScript,
Python, Java, C#
Native Frames &
Tabs Support
Limited Basic Full native support
Test Parallelism Depends on config Limited Built-in
Auto-wait & Network
control
Manual waits often
needed
Good Excellent, auto-waiting by
default
CI/CD Friendly Yes Yes Yes, with full CLI support
Playwright combines the ease of use found in Cypress with the versatility of Selenium — but
with a modern and developer-friendly approach.
Supported Languages and Browsers
Languages:
●​ JavaScript / TypeScript (official support)​
●​ Python​
●​ Java​
●​ C#​
Browsers:
●​ Chromium (Google Chrome, Microsoft Edge)​
●​ Firefox​
●​ WebKit (Safari engine)​
Playwright runs tests on real browser engines, not headless simulators, ensuring greater
accuracy and fidelity in test results.
Installing Playwright
To get started, all you need is Node.js installed. Then:
npm init playwright@latest
This command scaffolds a test project with default configurations, installing dependencies
and even downloading browser binaries.
You can also install it manually with:
npm install -D @playwright/test
npx playwright install
Or use Playwright for Python:
pip install playwright
playwright install
It’s that simple — no extra WebDriver setup or browser management needed.
First Script: A "Hello World" Test
Here’s a basic test in JavaScript using the Playwright test runner:
// tests/example.spec.js
const { test, expect } = require('@playwright/test');
test('homepage has title', async ({ page }) => {
await page.goto('https://example.com');
await expect(page).toHaveTitle(/Example Domain/);
});
Run the test with:
npx playwright test
You’ll see the browser launch (headless by default), the test run, and a result printed in the
terminal. Add the --headed flag to see the browser UI in action.
Chapter 2: Core Concepts and
Architecture
To build effective automation with Playwright, it’s crucial to understand its core architecture
and how its components interact. This chapter explores the building blocks that make
Playwright robust, flexible, and scalable for modern testing needs.
1. Playwright Test Runner
Playwright includes its own built-in test runner, @playwright/test, which supports:
●​ Test isolation​
●​ Parallel execution​
●​ Fixtures​
●​ Hooks (beforeEach, afterEach)​
●​ Snapshot testing​
●​ Report generation​
You can use other runners like Jest or Mocha, but the official runner is optimized for
Playwright’s APIs and is highly recommended for new projects.
Folder structure example:
/tests
└── example.spec.ts
/playwright.config.ts
Run with:
npx playwright test
2. Browsers, Contexts, and Pages
Playwright's architecture mimics how real users interact with browsers. This abstraction
allows powerful automation flows with better isolation and performance.
Browser
Playwright launches a browser instance (Chromium, Firefox, or WebKit). It's equivalent to
opening the actual browser manually.
const browser = await playwright.chromium.launch();
Browser Context
A browser context is like a brand-new, isolated incognito browser session. Each test runs in
its own context to avoid state leakage.
const context = await browser.newContext();
Pro Tip: Using multiple contexts is faster and lighter than launching multiple
browser instances.
Page
A page is a single tab in the browser. Most interactions—like navigating, clicking, or
typing—happen here.
const page = await context.newPage();
await page.goto('https://example.com');
3. Locators and Selectors
Playwright introduces a powerful locator API to target elements, replacing traditional
CSS/XPath selectors.
Example:
const button = page.getByRole('button', { name: 'Submit' });
await button.click();
Other supported selector types:
●​ CSS (page.locator('.btn-primary'))​
●​ Text (page.getByText('Learn more'))​
●​ Role-based (page.getByRole('link'))​
●​ Custom attributes​
Locators are auto-waiting, meaning they wait for elements to appear or become actionable.
4. Events, Waits, and Assertions
Playwright automatically waits for UI events—like clicks or navigation—to complete before
continuing.
Auto-waiting:
await page.click('button#submit'); // Waits for button to be clickable
Assertions:
Playwright uses expect() syntax for assertions:
await expect(page).toHaveTitle('Dashboard');
await expect(page.locator('h1')).toContainText('Welcome');
Manual waits (rarely needed):
await page.waitForTimeout(2000); // Not recommended
Built-in auto-waits and robust selectors reduce test flakiness significantly.
5. Multi-tab, iFrames, and Downloads
Playwright natively supports multi-tab workflows, iFrames, and file downloads:
New Tab Handling:
const [newPage] = await Promise.all([
context.waitForEvent('page'),
page.click('a[target=_blank]')
]);
await newPage.waitForLoadState();
iFrame Handling:
const frame = page.frame({ name: 'my-frame' });
await frame.click('#inside-frame-button');
File Download:
const [ download ] = await Promise.all([
page.waitForEvent('download'),
page.click('text=Download File')
]);
const path = await download.path();
6. Playwright vs Puppeteer (Architecture Snapshot)
Feature Puppeteer Playwright
Cross-browser Chromium only Chromium, Firefox, WebKit
Browser Contexts Limited Full support
Network Interception Basic Advanced
Test Runner External Built-in
iFrames/Popups Manual workarounds Native support
Playwright was originally created by ex-Puppeteer engineers to address architectural
limitations in Puppeteer.
Chapter 3: Writing Tests with Playwright
Once you've grasped the core architecture of Playwright, the next step is writing actual tests.
This chapter walks you through the syntax, patterns, and techniques to build solid test cases
using Playwright's test runner.
1. Test Structure and Syntax
Playwright uses a behavior-driven testing structure, similar to Jest or Mocha.
Here’s the basic format:
import { test, expect } from '@playwright/test';
test('should navigate to homepage', async ({ page }) => {
await page.goto('https://example.com');
await expect(page).toHaveTitle(/Example Domain/);
});
●​ test() defines a test block.​
●​ expect() performs assertions.​
●​ The { page } argument is injected by Playwright’s built-in fixtures.​
2. Locators and Assertions
Playwright encourages using locators for element selection. Locators are strict, wait-aware,
and offer better debugging output.
Using Locators
const loginButton = page.getByRole('button', { name: 'Login' });
await loginButton.click();
Common Assertions
await expect(page).toHaveURL(/dashboard/);
await expect(page.locator('h1')).toHaveText('Welcome');
await expect(page.locator('.alert')).toBeVisible();
You can also assert on attributes, classes, or visibility.
3. Interacting with UI Elements
Playwright supports all common user actions, including typing, clicking, hovering, and
dragging.
Click, Fill, and Submit
await page.click('#login');
await page.fill('#username', 'john');
await page.fill('#password', 'secret');
await page.click('text=Submit');
Dropdowns
await page.selectOption('#country', 'India');
Checkboxes and Radios
await page.check('#terms');
await page.uncheck('#newsletter');
Mouse and Keyboard
await page.hover('.menu');
await page.keyboard.press('Enter');
4. Hooks: Before and After
Use hooks to set up preconditions or clean up after tests:
test.beforeEach(async ({ page }) => {
await page.goto('https://app.example.com/login');
});
test.afterEach(async ({ page }) => {
await page.close();
});
Playwright also supports beforeAll and afterAll for setup/teardown at the suite level.
5. Test Grouping with describe()
You can group tests using describe():
test.describe('Login flow', () => {
test('shows login form', async ({ page }) => {
await expect(page.locator('#login-form')).toBeVisible();
});
test('logs in with valid credentials', async ({ page }) => {
await page.fill('#email', 'test@example.com');
await page.fill('#pass', 'password');
await page.click('text=Login');
await expect(page).toHaveURL(/dashboard/);
});
});
6. Parallelization and Tagging
Playwright runs tests in parallel by default across files and even inside test files (workers and
projects).
Configure Parallel Tests
In playwright.config.ts:
projects: [
{ name: 'Chrome', use: { browserName: 'chromium' } },
{ name: 'Firefox', use: { browserName: 'firefox' } },
],
This allows cross-browser testing in one run.
Tagging Tests
You can use annotations for conditional logic:
test.skip('this test is skipped');
test.only('run only this test');
test.fixme('to be fixed later');
7. Screenshots and Videos
Playwright supports capturing screenshots and video for debugging.
Manual Screenshot
await page.screenshot({ path: 'screenshot.png' });
Auto-recording
In config:
use: {
screenshot: 'on',
video: 'retain-on-failure',
}
These assets are stored in the test-results/ folder by default.
Writing tests in Playwright is fast, expressive, and clean. With rich locators, automatic waits,
full UI interaction APIs, and test lifecycle hooks, it becomes easy to cover complex user
flows with minimal flakiness. Mastering these features is key to building reliable and
maintainable automation suites.
Chapter 4: Advanced Testing Scenarios
Once you're comfortable writing basic UI tests, it’s time to push the boundaries. Playwright’s
advanced capabilities let you automate complex workflows such as API testing,
authentication, file handling, and even emulation. This chapter dives into those real-world
testing needs.
1. Visual Regression Testing
Playwright supports screenshot comparisons for visual testing. You can take baseline
screenshots and compare future UI changes against them.
Take and Compare Snapshots
await expect(page).toHaveScreenshot('homepage.png');
You can run npx playwright test --update-snapshots to regenerate baselines.
Use Cases:
●​ Catching unintended UI shifts​
●​ Validating responsive layout changes​
●​ Pixel-perfect design checks​
2. Authentication Handling
Playwright can handle all common authentication mechanisms: basic, token, OAuth, and
even multi-step login flows.
Basic Auth Example
await context = await browser.newContext({
httpCredentials: {
username: 'user',
password: 'pass'
}
});
Token-Based Auth
You can programmatically sign in via API and reuse the storage state:
// login.js
test('authenticate and save state', async ({ page }) => {
await page.goto('https://example.com/login');
await page.fill('#username', 'admin');
await page.fill('#password', 'admin123');
await page.click('text=Sign in');
await page.context().storageState({ path: 'auth.json' });
});
Use it in other tests:
use: {
storageState: 'auth.json'
}
3. API Testing with Playwright
Playwright can test REST APIs directly alongside UI tests.
Example API Test
test('GET user info', async ({ request }) => {
const response = await request.get('https://api.example.com/user');
expect(response.status()).toBe(200);
const data = await response.json();
expect(data.email).toBe('test@example.com');
});
You can also mix API and UI flows in a single test.
4. File Uploads and Downloads
Playwright supports file operations without needing third-party plugins.
File Upload
await page.setInputFiles('input[type="file"]', 'path/to/file.pdf');
File Download
const [ download ] = await Promise.all([
page.waitForEvent('download'),
page.click('text=Download Report')
]);
const path = await download.path();
console.log(`File saved at ${path}`);
5. Emulating Devices and Geolocation
Playwright supports mobile emulation and simulating geo-data, making it ideal for testing
responsive and location-aware apps.
Emulate Devices
const iPhone = devices['iPhone 12'];
const context = await browser.newContext({ ...iPhone });
const page = await context.newPage();
Geolocation
const context = await browser.newContext({
geolocation: { latitude: 37.7749, longitude: -122.4194 },
permissions: ['geolocation']
});
You can simulate GPS-based app behavior using the above configuration.
6. Handling Captchas and Two-Factor Flows
While automated CAPTCHA bypassing is often discouraged for ethical reasons, Playwright
allows test environments to disable CAPTCHA or use test tokens.
For 2FA, you can:
●​ Mock the second factor via environment toggle​
●​ Use test accounts with backup codes​
●​ Leverage storage state for bypassing re-logins​
Playwright goes far beyond traditional UI testing. Whether you're validating backend APIs,
simulating mobile users, handling secure logins, or verifying visual consistency, Playwright
gives you the tools to automate advanced scenarios with ease. These capabilities make it
ideal for full-stack end-to-end testing in modern web projects.
Chapter 5: Test Automation and CI
Integration
Writing reliable tests is only part of the equation — true testing power comes from
automating those tests as part of your development workflow. In this chapter, you’ll learn
how to run Playwright tests in Continuous Integration (CI) pipelines, enable reporting, and
configure for scalable test execution.
1. Headless Mode and CLI Options
Playwright runs in headless mode by default, which is ideal for CI environments.
Basic CLI Usage
npx playwright test # Run all tests
npx playwright test login.spec.ts # Run a specific test file
npx playwright test --headed # Show browser UI
npx playwright test --debug # Open in debug mode
Useful Flags
●​ --project=chromium – run specific browser project​
●​ --grep="Login" – run tests by name or tag​
●​ --reporter=html – generate visual reports​
2. Configuring playwright.config.ts
The config file is central to customizing how tests are run.
Sample Configuration
import { defineConfig } from '@playwright/test';
export default defineConfig({
timeout: 30000,
retries: 1,
use: {
headless: true,
screenshot: 'only-on-failure',
video: 'retain-on-failure',
baseURL: 'https://example.com',
},
projects: [
{ name: 'Chromium', use: { browserName: 'chromium' } },
{ name: 'Firefox', use: { browserName: 'firefox' } },
{ name: 'WebKit', use: { browserName: 'webkit' } },
],
});
This configuration supports:
●​ Base URLs​
●​ Parallel browser execution​
●​ Retry on failure​
●​ Conditional screenshots/videos​
3. Integrating with CI Tools
Playwright works smoothly with GitHub Actions, GitLab CI, Jenkins, CircleCI, Bitbucket
Pipelines, and more.
GitHub Actions Example
name: Playwright Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v4
with:
node-version: 18
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test
- uses: actions/upload-artifact@v3
with:
name: playwright-report
path: playwright-report
This workflow:
●​ Installs dependencies​
●​ Sets up Playwright and browsers​
●​ Runs tests​
●​ Uploads test reports​
4. Generating HTML Reports
Playwright offers a beautiful built-in HTML reporter.
Enable in config:
reporter: [['html', { outputFolder: 'playwright-report' }]],
Generate and Open:
npx playwright show-report
This opens an interactive dashboard with:
●​ Test status​
●​ Screenshots​
●​ Videos​
●​ Logs and errors​
You can also use other reporters:
●​ list​
●​ json​
●​ junit (for CI pipelines)​
●​ Custom reporters​
5. Test Retries and Flaky Test Management
Retry Failing Tests Automatically
In playwright.config.ts:
retries: 2
Useful for network-flaky environments or CI jobs.
Detect Flaky Tests
Playwright highlights retries and failures in reports, helping identify unstable tests.
6. Best Practices for CI Integration
●​ Use storage state to bypass login in tests​
●​ Run browsers in headless mode for speed​
●​ Use baseURL in config for cleaner test URLs​
●​ Split test suites across parallel jobs for faster feedback​
●​ Upload trace/video on failure to debug easily​
Playwright was built with automation in mind. With built-in support for headless execution,
powerful CLI options, and seamless CI integration, it’s easy to fit Playwright into any modern
development pipeline. The result is faster feedback loops, more reliable deployments, and a
QA process that scales with your team.
Chapter 6: Best Practices and
Debugging
As your Playwright test suite grows, so does the need to keep it maintainable, fast, and
reliable. This chapter focuses on tried-and-true best practices and powerful debugging
strategies that will help you scale your test automation efficiently.
1. Keep Tests Isolated and Independent
Each test should start from a clean state and avoid relying on the results of others.
Do:
●​ Use beforeEach to set up state​
●​ Clean up with afterEach if needed​
●​ Leverage browser.newContext() to isolate sessions​
❌Avoid:
●​ Sharing variables or state across tests​
●​ Relying on test execution order​
2. Use Locators Over Selectors
Favor Playwright’s locator() and getBy* methods over brittle CSS or XPath selectors.
await page.getByRole('button', { name: 'Sign in' }).click();
They are:
●​ Resilient to DOM changes​
●​ Automatically retry until visible or actionable​
●​ Easier to debug and maintain
3. Implement Custom Fixtures for Reuse
Fixtures help abstract repetitive setup logic like user login or test data creation.
Example: Custom Login Fixture
export const test = baseTest.extend({
loggedInPage: async ({ page }, use) => {
await page.goto('/login');
await page.fill('#email', 'admin@test.com');
await page.fill('#password', 'password');
await page.click('text=Login');
await use(page);
}
});
Then in your test:
test('dashboard loads', async ({ loggedInPage }) => {
await expect(loggedInPage.locator('h1')).toHaveText('Dashboard');
});
4. Use Tags and Filters for Test Suites
Organize tests with tags or grep-friendly titles.
test('smoke - homepage loads', async ({ page }) => {
await page.goto('/');
});
Then run only smoke tests:
npx playwright test --grep=smoke
5. Debug with Trace Viewer
Playwright can capture step-by-step traces for failed tests.
Enable Tracing
In playwright.config.ts:
use: {
trace: 'on-first-retry',
}
View the Trace
npx playwright show-trace trace.zip
This opens a visual timeline showing DOM snapshots, console logs, and network activity —
ideal for pinpointing issues.
6. Use .only, .skip, and .fixme Wisely
These tools are powerful but should be cleaned up before committing.
●​ test.only() – focus on a single test​
●​ test.skip() – temporarily skip known flaky tests​
●​ test.fixme() – mark tests that need attention​
Always remove .only() before pushing to CI
7. Manage Test Data Smartly
Avoid relying on static or shared test data.
●​ Use unique data per test when possible​
●​ Use API setup for backend data (faster than UI)​
●​ Clean up after tests via API or database hooks​
8. Parallelize and Shard Tests
Split tests by browser, device, or test group to speed up execution.
projects: [
{ name: 'Mobile Safari', use: { ...devices['iPhone 13'] } },
{ name: 'Desktop Chrome', use: { browserName: 'chromium' } }
]
Use sharding in CI:
npx playwright test --shard=1/2
npx playwright test --shard=2/2
Playwright is incredibly powerful — but like any tool, it shines when used well. By following
best practices around test isolation, locator usage, fixtures, and debugging, you’ll create a
stable automation suite that delivers real value. Combine this with CI integration and
trace-based debugging, and you'll have a battle-tested QA workflow ready for scale.

Playwright Testing Guide for QA Engineers.pdf

  • 1.
  • 2.
    Table of Contents Chapter1: Introduction to Playwright ●​ What is Playwright?​ ●​ Why Choose Playwright Over Selenium or Cypress?​ ●​ Supported Languages and Browsers​ ●​ Installing Playwright​ ●​ First Script: A "Hello World" Test​ Chapter 2: Core Concepts and Architecture ●​ Understanding the Playwright Test Runner​ ●​ Browsers and Contexts​ ●​ Pages and Selectors​ ●​ Handling Events and Waits​ ●​ Playwright vs Puppeteer​ Chapter 3: Writing Tests with Playwright ●​ Test Syntax and Structure​ ●​ Locators and Assertions​ ●​ Interacting with UI Elements (click, type, check, etc.)​ ●​ Handling Alerts, Frames, and Popups​ ●​ Parallel Testing and Test Hooks​ Chapter 4: Advanced Testing Scenarios ●​ Visual Regression Testing​
  • 3.
    ●​ Handling Authentication(Basic, OAuth, Token)​ ●​ API Testing with Playwright​ ●​ File Uploads and Downloads​ ●​ Emulating Devices and Geolocation​ Chapter 5: Test Automation and CI Integration ●​ Headless Mode and CLI Options​ ●​ Integrating with CI Tools (GitHub Actions, Jenkins)​ ●​ Parallel Execution in CI​ ●​ Generating HTML Reports and Logs​ ●​ Test Retries and Flaky Test Management​ Chapter 6: Best Practices and Debugging ●​ Structuring Test Suites for Scalability​ ●​ Using Trace Viewer and Debug Mode​ ●​ Test Data Management and Fixtures​ ●​ Common Pitfalls and Troubleshooting​ ●​ Migration Tips (from Selenium or Cypress to Playwright)​
  • 4.
    Chapter 1: Introductionto Playwright What is Playwright? Playwright is an open-source test automation framework developed by Microsoft that enables reliable end-to-end testing for modern web applications. It allows developers and QA engineers to script browser interactions with ease, covering everything from simple UI clicks to complex multi-page flows. At its core, Playwright automates Chromium, Firefox, and WebKit browsers using a single API. This cross-browser capability makes it a powerful tool for cross-platform testing — including both desktop and mobile views — without switching libraries or rewriting tests. What is Playwright Testing? Playwright testing refers to the practice of using the Playwright framework to perform automated checks on web applications. These tests simulate real user interactions — like clicking, scrolling, typing, and navigation — to ensure your app behaves as expected across different browsers and devices. With Playwright, you can write tests for: ●​ UI workflows: Test full user journeys like login, checkout, or onboarding.​ ●​ Responsive design: Verify how your site behaves on different screen sizes and devices.​ ●​ Cross-browser compatibility: Catch bugs that appear only in specific browsers.​ ●​ Network conditions and API handling: Simulate slow network speeds or mock API responses.​ Playwright's ability to launch browsers in headless or headed mode, along with parallel test execution and auto-waiting for elements, makes it fast and reliable — especially when integrated into CI/CD pipelines. Why Choose Playwright Over Selenium or Cypress? While Selenium has long been the industry standard, and Cypress has gained popularity for front-end testing, Playwright brings a unique blend of performance, modern APIs, and multi-browser support. Here's how it stands out: Feature Selenium Cypress Playwright
  • 5.
    Browser Support Chrome,Firefox, Safari, Edge Chrome-family only Chrome, Firefox, Safari (WebKit) Language Support Java, Python, C#, JS JavaScript only JavaScript, TypeScript, Python, Java, C# Native Frames & Tabs Support Limited Basic Full native support Test Parallelism Depends on config Limited Built-in Auto-wait & Network control Manual waits often needed Good Excellent, auto-waiting by default CI/CD Friendly Yes Yes Yes, with full CLI support Playwright combines the ease of use found in Cypress with the versatility of Selenium — but with a modern and developer-friendly approach. Supported Languages and Browsers Languages: ●​ JavaScript / TypeScript (official support)​ ●​ Python​ ●​ Java​ ●​ C#​ Browsers: ●​ Chromium (Google Chrome, Microsoft Edge)​ ●​ Firefox​ ●​ WebKit (Safari engine)​ Playwright runs tests on real browser engines, not headless simulators, ensuring greater accuracy and fidelity in test results. Installing Playwright To get started, all you need is Node.js installed. Then:
  • 6.
    npm init playwright@latest Thiscommand scaffolds a test project with default configurations, installing dependencies and even downloading browser binaries. You can also install it manually with: npm install -D @playwright/test npx playwright install Or use Playwright for Python: pip install playwright playwright install It’s that simple — no extra WebDriver setup or browser management needed. First Script: A "Hello World" Test Here’s a basic test in JavaScript using the Playwright test runner: // tests/example.spec.js const { test, expect } = require('@playwright/test'); test('homepage has title', async ({ page }) => { await page.goto('https://example.com'); await expect(page).toHaveTitle(/Example Domain/); }); Run the test with: npx playwright test You’ll see the browser launch (headless by default), the test run, and a result printed in the terminal. Add the --headed flag to see the browser UI in action.
  • 7.
    Chapter 2: CoreConcepts and Architecture To build effective automation with Playwright, it’s crucial to understand its core architecture and how its components interact. This chapter explores the building blocks that make Playwright robust, flexible, and scalable for modern testing needs. 1. Playwright Test Runner Playwright includes its own built-in test runner, @playwright/test, which supports: ●​ Test isolation​ ●​ Parallel execution​ ●​ Fixtures​ ●​ Hooks (beforeEach, afterEach)​ ●​ Snapshot testing​ ●​ Report generation​ You can use other runners like Jest or Mocha, but the official runner is optimized for Playwright’s APIs and is highly recommended for new projects. Folder structure example: /tests └── example.spec.ts /playwright.config.ts Run with: npx playwright test 2. Browsers, Contexts, and Pages Playwright's architecture mimics how real users interact with browsers. This abstraction allows powerful automation flows with better isolation and performance.
  • 8.
    Browser Playwright launches abrowser instance (Chromium, Firefox, or WebKit). It's equivalent to opening the actual browser manually. const browser = await playwright.chromium.launch(); Browser Context A browser context is like a brand-new, isolated incognito browser session. Each test runs in its own context to avoid state leakage. const context = await browser.newContext(); Pro Tip: Using multiple contexts is faster and lighter than launching multiple browser instances. Page A page is a single tab in the browser. Most interactions—like navigating, clicking, or typing—happen here. const page = await context.newPage(); await page.goto('https://example.com'); 3. Locators and Selectors Playwright introduces a powerful locator API to target elements, replacing traditional CSS/XPath selectors. Example: const button = page.getByRole('button', { name: 'Submit' }); await button.click(); Other supported selector types: ●​ CSS (page.locator('.btn-primary'))​ ●​ Text (page.getByText('Learn more'))​
  • 9.
    ●​ Role-based (page.getByRole('link'))​ ●​Custom attributes​ Locators are auto-waiting, meaning they wait for elements to appear or become actionable. 4. Events, Waits, and Assertions Playwright automatically waits for UI events—like clicks or navigation—to complete before continuing. Auto-waiting: await page.click('button#submit'); // Waits for button to be clickable Assertions: Playwright uses expect() syntax for assertions: await expect(page).toHaveTitle('Dashboard'); await expect(page.locator('h1')).toContainText('Welcome'); Manual waits (rarely needed): await page.waitForTimeout(2000); // Not recommended Built-in auto-waits and robust selectors reduce test flakiness significantly. 5. Multi-tab, iFrames, and Downloads Playwright natively supports multi-tab workflows, iFrames, and file downloads: New Tab Handling: const [newPage] = await Promise.all([ context.waitForEvent('page'), page.click('a[target=_blank]') ]); await newPage.waitForLoadState();
  • 10.
    iFrame Handling: const frame= page.frame({ name: 'my-frame' }); await frame.click('#inside-frame-button'); File Download: const [ download ] = await Promise.all([ page.waitForEvent('download'), page.click('text=Download File') ]); const path = await download.path(); 6. Playwright vs Puppeteer (Architecture Snapshot) Feature Puppeteer Playwright Cross-browser Chromium only Chromium, Firefox, WebKit Browser Contexts Limited Full support Network Interception Basic Advanced Test Runner External Built-in iFrames/Popups Manual workarounds Native support Playwright was originally created by ex-Puppeteer engineers to address architectural limitations in Puppeteer.
  • 11.
    Chapter 3: WritingTests with Playwright Once you've grasped the core architecture of Playwright, the next step is writing actual tests. This chapter walks you through the syntax, patterns, and techniques to build solid test cases using Playwright's test runner. 1. Test Structure and Syntax Playwright uses a behavior-driven testing structure, similar to Jest or Mocha. Here’s the basic format: import { test, expect } from '@playwright/test'; test('should navigate to homepage', async ({ page }) => { await page.goto('https://example.com'); await expect(page).toHaveTitle(/Example Domain/); }); ●​ test() defines a test block.​ ●​ expect() performs assertions.​ ●​ The { page } argument is injected by Playwright’s built-in fixtures.​ 2. Locators and Assertions Playwright encourages using locators for element selection. Locators are strict, wait-aware, and offer better debugging output. Using Locators const loginButton = page.getByRole('button', { name: 'Login' }); await loginButton.click(); Common Assertions await expect(page).toHaveURL(/dashboard/);
  • 12.
    await expect(page.locator('h1')).toHaveText('Welcome'); await expect(page.locator('.alert')).toBeVisible(); Youcan also assert on attributes, classes, or visibility. 3. Interacting with UI Elements Playwright supports all common user actions, including typing, clicking, hovering, and dragging. Click, Fill, and Submit await page.click('#login'); await page.fill('#username', 'john'); await page.fill('#password', 'secret'); await page.click('text=Submit'); Dropdowns await page.selectOption('#country', 'India'); Checkboxes and Radios await page.check('#terms'); await page.uncheck('#newsletter'); Mouse and Keyboard await page.hover('.menu'); await page.keyboard.press('Enter'); 4. Hooks: Before and After Use hooks to set up preconditions or clean up after tests:
  • 13.
    test.beforeEach(async ({ page}) => { await page.goto('https://app.example.com/login'); }); test.afterEach(async ({ page }) => { await page.close(); }); Playwright also supports beforeAll and afterAll for setup/teardown at the suite level. 5. Test Grouping with describe() You can group tests using describe(): test.describe('Login flow', () => { test('shows login form', async ({ page }) => { await expect(page.locator('#login-form')).toBeVisible(); }); test('logs in with valid credentials', async ({ page }) => { await page.fill('#email', '[email protected]'); await page.fill('#pass', 'password'); await page.click('text=Login'); await expect(page).toHaveURL(/dashboard/); }); }); 6. Parallelization and Tagging
  • 14.
    Playwright runs testsin parallel by default across files and even inside test files (workers and projects). Configure Parallel Tests In playwright.config.ts: projects: [ { name: 'Chrome', use: { browserName: 'chromium' } }, { name: 'Firefox', use: { browserName: 'firefox' } }, ], This allows cross-browser testing in one run. Tagging Tests You can use annotations for conditional logic: test.skip('this test is skipped'); test.only('run only this test'); test.fixme('to be fixed later'); 7. Screenshots and Videos Playwright supports capturing screenshots and video for debugging. Manual Screenshot await page.screenshot({ path: 'screenshot.png' }); Auto-recording In config: use: { screenshot: 'on', video: 'retain-on-failure', }
  • 15.
    These assets arestored in the test-results/ folder by default. Writing tests in Playwright is fast, expressive, and clean. With rich locators, automatic waits, full UI interaction APIs, and test lifecycle hooks, it becomes easy to cover complex user flows with minimal flakiness. Mastering these features is key to building reliable and maintainable automation suites.
  • 16.
    Chapter 4: AdvancedTesting Scenarios Once you're comfortable writing basic UI tests, it’s time to push the boundaries. Playwright’s advanced capabilities let you automate complex workflows such as API testing, authentication, file handling, and even emulation. This chapter dives into those real-world testing needs. 1. Visual Regression Testing Playwright supports screenshot comparisons for visual testing. You can take baseline screenshots and compare future UI changes against them. Take and Compare Snapshots await expect(page).toHaveScreenshot('homepage.png'); You can run npx playwright test --update-snapshots to regenerate baselines. Use Cases: ●​ Catching unintended UI shifts​ ●​ Validating responsive layout changes​ ●​ Pixel-perfect design checks​ 2. Authentication Handling Playwright can handle all common authentication mechanisms: basic, token, OAuth, and even multi-step login flows. Basic Auth Example await context = await browser.newContext({ httpCredentials: { username: 'user', password: 'pass' } });
  • 17.
    Token-Based Auth You canprogrammatically sign in via API and reuse the storage state: // login.js test('authenticate and save state', async ({ page }) => { await page.goto('https://example.com/login'); await page.fill('#username', 'admin'); await page.fill('#password', 'admin123'); await page.click('text=Sign in'); await page.context().storageState({ path: 'auth.json' }); }); Use it in other tests: use: { storageState: 'auth.json' } 3. API Testing with Playwright Playwright can test REST APIs directly alongside UI tests. Example API Test test('GET user info', async ({ request }) => { const response = await request.get('https://api.example.com/user'); expect(response.status()).toBe(200); const data = await response.json(); expect(data.email).toBe('[email protected]'); });
  • 18.
    You can alsomix API and UI flows in a single test. 4. File Uploads and Downloads Playwright supports file operations without needing third-party plugins. File Upload await page.setInputFiles('input[type="file"]', 'path/to/file.pdf'); File Download const [ download ] = await Promise.all([ page.waitForEvent('download'), page.click('text=Download Report') ]); const path = await download.path(); console.log(`File saved at ${path}`); 5. Emulating Devices and Geolocation Playwright supports mobile emulation and simulating geo-data, making it ideal for testing responsive and location-aware apps. Emulate Devices const iPhone = devices['iPhone 12']; const context = await browser.newContext({ ...iPhone }); const page = await context.newPage(); Geolocation const context = await browser.newContext({ geolocation: { latitude: 37.7749, longitude: -122.4194 },
  • 19.
    permissions: ['geolocation'] }); You cansimulate GPS-based app behavior using the above configuration. 6. Handling Captchas and Two-Factor Flows While automated CAPTCHA bypassing is often discouraged for ethical reasons, Playwright allows test environments to disable CAPTCHA or use test tokens. For 2FA, you can: ●​ Mock the second factor via environment toggle​ ●​ Use test accounts with backup codes​ ●​ Leverage storage state for bypassing re-logins​ Playwright goes far beyond traditional UI testing. Whether you're validating backend APIs, simulating mobile users, handling secure logins, or verifying visual consistency, Playwright gives you the tools to automate advanced scenarios with ease. These capabilities make it ideal for full-stack end-to-end testing in modern web projects. Chapter 5: Test Automation and CI Integration Writing reliable tests is only part of the equation — true testing power comes from automating those tests as part of your development workflow. In this chapter, you’ll learn how to run Playwright tests in Continuous Integration (CI) pipelines, enable reporting, and configure for scalable test execution. 1. Headless Mode and CLI Options Playwright runs in headless mode by default, which is ideal for CI environments. Basic CLI Usage npx playwright test # Run all tests npx playwright test login.spec.ts # Run a specific test file
  • 20.
    npx playwright test--headed # Show browser UI npx playwright test --debug # Open in debug mode Useful Flags ●​ --project=chromium – run specific browser project​ ●​ --grep="Login" – run tests by name or tag​ ●​ --reporter=html – generate visual reports​ 2. Configuring playwright.config.ts The config file is central to customizing how tests are run. Sample Configuration import { defineConfig } from '@playwright/test'; export default defineConfig({ timeout: 30000, retries: 1, use: { headless: true, screenshot: 'only-on-failure', video: 'retain-on-failure', baseURL: 'https://example.com', }, projects: [ { name: 'Chromium', use: { browserName: 'chromium' } }, { name: 'Firefox', use: { browserName: 'firefox' } }, { name: 'WebKit', use: { browserName: 'webkit' } },
  • 21.
    ], }); This configuration supports: ●​Base URLs​ ●​ Parallel browser execution​ ●​ Retry on failure​ ●​ Conditional screenshots/videos​ 3. Integrating with CI Tools Playwright works smoothly with GitHub Actions, GitLab CI, Jenkins, CircleCI, Bitbucket Pipelines, and more. GitHub Actions Example name: Playwright Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v4 with: node-version: 18 - run: npm ci - run: npx playwright install --with-deps
  • 22.
    - run: npxplaywright test - uses: actions/upload-artifact@v3 with: name: playwright-report path: playwright-report This workflow: ●​ Installs dependencies​ ●​ Sets up Playwright and browsers​ ●​ Runs tests​ ●​ Uploads test reports​ 4. Generating HTML Reports Playwright offers a beautiful built-in HTML reporter. Enable in config: reporter: [['html', { outputFolder: 'playwright-report' }]], Generate and Open: npx playwright show-report This opens an interactive dashboard with: ●​ Test status​ ●​ Screenshots​ ●​ Videos​ ●​ Logs and errors​
  • 23.
    You can alsouse other reporters: ●​ list​ ●​ json​ ●​ junit (for CI pipelines)​ ●​ Custom reporters​ 5. Test Retries and Flaky Test Management Retry Failing Tests Automatically In playwright.config.ts: retries: 2 Useful for network-flaky environments or CI jobs. Detect Flaky Tests Playwright highlights retries and failures in reports, helping identify unstable tests. 6. Best Practices for CI Integration ●​ Use storage state to bypass login in tests​ ●​ Run browsers in headless mode for speed​ ●​ Use baseURL in config for cleaner test URLs​ ●​ Split test suites across parallel jobs for faster feedback​ ●​ Upload trace/video on failure to debug easily​ Playwright was built with automation in mind. With built-in support for headless execution, powerful CLI options, and seamless CI integration, it’s easy to fit Playwright into any modern development pipeline. The result is faster feedback loops, more reliable deployments, and a QA process that scales with your team.
  • 24.
    Chapter 6: BestPractices and Debugging As your Playwright test suite grows, so does the need to keep it maintainable, fast, and reliable. This chapter focuses on tried-and-true best practices and powerful debugging strategies that will help you scale your test automation efficiently. 1. Keep Tests Isolated and Independent Each test should start from a clean state and avoid relying on the results of others. Do: ●​ Use beforeEach to set up state​ ●​ Clean up with afterEach if needed​ ●​ Leverage browser.newContext() to isolate sessions​ ❌Avoid: ●​ Sharing variables or state across tests​ ●​ Relying on test execution order​ 2. Use Locators Over Selectors Favor Playwright’s locator() and getBy* methods over brittle CSS or XPath selectors. await page.getByRole('button', { name: 'Sign in' }).click(); They are: ●​ Resilient to DOM changes​ ●​ Automatically retry until visible or actionable​ ●​ Easier to debug and maintain 3. Implement Custom Fixtures for Reuse Fixtures help abstract repetitive setup logic like user login or test data creation.
  • 25.
    Example: Custom LoginFixture export const test = baseTest.extend({ loggedInPage: async ({ page }, use) => { await page.goto('/login'); await page.fill('#email', '[email protected]'); await page.fill('#password', 'password'); await page.click('text=Login'); await use(page); } }); Then in your test: test('dashboard loads', async ({ loggedInPage }) => { await expect(loggedInPage.locator('h1')).toHaveText('Dashboard'); }); 4. Use Tags and Filters for Test Suites Organize tests with tags or grep-friendly titles. test('smoke - homepage loads', async ({ page }) => { await page.goto('/'); }); Then run only smoke tests: npx playwright test --grep=smoke 5. Debug with Trace Viewer Playwright can capture step-by-step traces for failed tests. Enable Tracing
  • 26.
    In playwright.config.ts: use: { trace:'on-first-retry', } View the Trace npx playwright show-trace trace.zip This opens a visual timeline showing DOM snapshots, console logs, and network activity — ideal for pinpointing issues. 6. Use .only, .skip, and .fixme Wisely These tools are powerful but should be cleaned up before committing. ●​ test.only() – focus on a single test​ ●​ test.skip() – temporarily skip known flaky tests​ ●​ test.fixme() – mark tests that need attention​ Always remove .only() before pushing to CI 7. Manage Test Data Smartly Avoid relying on static or shared test data. ●​ Use unique data per test when possible​ ●​ Use API setup for backend data (faster than UI)​ ●​ Clean up after tests via API or database hooks​ 8. Parallelize and Shard Tests Split tests by browser, device, or test group to speed up execution.
  • 27.
    projects: [ { name:'Mobile Safari', use: { ...devices['iPhone 13'] } }, { name: 'Desktop Chrome', use: { browserName: 'chromium' } } ] Use sharding in CI: npx playwright test --shard=1/2 npx playwright test --shard=2/2 Playwright is incredibly powerful — but like any tool, it shines when used well. By following best practices around test isolation, locator usage, fixtures, and debugging, you’ll create a stable automation suite that delivers real value. Combine this with CI integration and trace-based debugging, and you'll have a battle-tested QA workflow ready for scale.