0% found this document useful (0 votes)
380 views73 pages

Playwright Automation Guide with TypeScript

The Playwright Handbook by Vaibhav Sahu is a comprehensive guide on using Playwright for automation testing with TypeScript, covering foundational concepts, core principles, and practical examples across eight chapters. It emphasizes the advantages of Playwright over traditional testing frameworks, including its speed, reliability, and cross-browser support. The document also provides detailed instructions on setting up the environment, writing tests, and utilizing advanced features like locators, actions, and assertions.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
380 views73 pages

Playwright Automation Guide with TypeScript

The Playwright Handbook by Vaibhav Sahu is a comprehensive guide on using Playwright for automation testing with TypeScript, covering foundational concepts, core principles, and practical examples across eight chapters. It emphasizes the advantages of Playwright over traditional testing frameworks, including its speed, reliability, and cross-browser support. The document also provides detailed instructions on setting up the environment, writing tests, and utilizing advanced features like locators, actions, and assertions.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Author-Vaibhav Sahu

PLAYWRIGHT HANDBOOK

Playwright Automation with


TypeScript
The Enterprise Edition

PART I & II: Complete Detailed Guide

Chapters 1-8 with Advanced Patterns

1
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

Table of Contents

PART I: FOUNDATIONS (Chapters 1-4)


• Chapter 1: Introduction to Playwright
• Chapter 2: Setting Up Your Environment
• Chapter 3: Writing Your First Test
• Chapter 4: Understanding Playwright Architecture

PART II: CORE CONCEPTS (Chapters 5-8)


• Chapter 5: Locators and Selectors
• Chapter 6: Actions and Interactions
• Chapter 7: Assertions and Validations
• Chapter 8: Page Object Model & Component Object Model

2
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

PART I: FOUNDATIONS

3
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 1: INTRODUCTION TO PLAYWRIGHT

1.1 What is Playwright & The Testing Trophy Strategy


Playwright is a modern, open-source automation framework developed by Microsoft
that enables reliable end-to-end testing for web applications. Released in 2020, it
was created by the same team that built Puppeteer at Google.

The Testing Trophy vs Testing Pyramid


Traditional software testing followed the "Testing Pyramid" approach: 70% unit tests,
20% integration tests, and 10% E2E tests. This was designed when E2E tests were
slow and unreliable.

With modern tools like Playwright, the Testing Trophy represents a better approach:

• 10% Static Analysis (TypeScript, ESLint)


• 20% Unit Tests (Pure business logic)
• 50% Integration Tests (Components + API together)
• 20% E2E Tests (Critical user journeys)

🏢 ENTERPRISE: The Trophy approach works because Playwright makes


E2E tests as fast as unit tests but with 10x the confidence. A single E2E
test validates frontend, backend, database, and APIs together.

Why the Trophy Strategy Wins


Integration and E2E tests provide more value because they:

• Test how components actually work together


• Catch integration bugs that unit tests miss
• Verify real user workflows
• Test database interactions
• Validate API contracts

Banking Login Example - Trophy in Action


test('complete banking login flow', async ({ page }) => {

4
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

await [Link]('/login');
await [Link]('Account Number').fill('123456789');
await [Link]('PIN').fill('1234');
await [Link]('button', { name: 'Login' }).click();

// This ONE test validates:


// ✓ UI rendering ✓ Form validation ✓ Auth API
// ✓ Database lookup ✓ Session creation ✓ Routing

await expect(page).toHaveURL('/dashboard');
await expect([Link]('Account Balance')).toBeVisible();
});

✅ BEST PRACTICE: Focus 70% of your testing effort on integration and


E2E tests. They provide the highest return on investment for catching real
bugs.

1.2 Why Playwright for Enterprise Applications

Cross-Browser Support
Playwright natively supports ALL major browser engines:

• Chromium - Chrome, Edge, Opera, Brave


• Firefox - Mozilla Firefox
• WebKit - Safari (desktop and iOS)

💡 NOTE: Playwright bundles specific browser versions, ensuring


consistent behavior across all environments - no "works on my machine"
issues.

Auto-Wait Mechanism
Playwright automatically waits for elements to be ready before acting:

• Attached to DOM
• Visible on screen
• Stable (not animating)
• Receives events (not covered)
5
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

• Enabled (not disabled)

// Playwright - auto-waits built in


await [Link]('button', { name: 'Submit' }).click();
// vs Selenium - manual waiting required
WebDriverWait wait = new WebDriverWait(driver, 10);
[Link]([Link](...));

✅ BEST PRACTICE: Auto-waiting eliminates 80% of flaky test issues.


Trust it - resist adding sleep() or arbitrary timeouts.

1.3 Playwright vs Selenium, Cypress, Puppeteer

vs Selenium
• Speed: Playwright 20-50% faster
• Setup: Playwright auto-downloads browsers
• API: Playwright modern async/await
• Reliability: Playwright built-in auto-wait

vs Cypress
• Browsers: Playwright supports Safari/WebKit
• Multi-tab: Playwright full support
• Mobile: Playwright native mobile browsers
• Languages: Playwright supports Python, .NET, Java

vs Puppeteer
• Browsers: Playwright adds Firefox + WebKit
• Testing: Playwright built for testing
• Selectors: Playwright user-facing locators

🏢 ENTERPRISE: For enterprise applications requiring cross-browser


support, mobile testing, and maximum reliability, Playwright is the clear
choice.

6
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 2: SETTING UP YOUR ENVIRONMENT

2.1 Prerequisites
• [Link] 16+ (LTS version recommended)
• Visual Studio Code (recommended)
• Basic TypeScript knowledge
• Command line familiarity

2.2 Installation Steps

Step 1: Create Project


mkdir playwright-automation
cd playwright-automation
npm init -y

Step 2: Install Playwright


npm init playwright@latest

Answer the prompts:

• TypeScript? → Yes
• Test directory? → tests
• GitHub Actions? → Yes
• Install browsers? → Yes

Step 3: Project Structure


playwright-automation/
├── node_modules/
├── tests/
│ └── [Link]
├── [Link]
├── [Link]
└── [Link]

7
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

2.3 Configuration

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

export default defineConfig({


testDir: './tests',
timeout: 30 * 1000,
fullyParallel: true,
retries: [Link] ? 2 : 0,
reporter: 'html',
use: {
baseURL: '[Link]
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
],
});

2.4 Verify Installation


npx playwright test

✅ BEST PRACTICE: Use VS Code Playwright extension for debugging,


test runner UI, and visual locator picker.

8
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 3: WRITING YOUR FIRST TEST

3.1 Basic Test Structure


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

test('basic navigation', async ({ page }) => {


await [Link]('[Link]
await expect(page).toHaveTitle(/Playwright/);
await [Link]('link', { name: 'Get started' }).click();
await expect(page).toHaveURL(/.*intro/);
});

3.2 Finding Elements

Role-Based (Recommended)
await [Link]('button', { name: 'Submit' })
await [Link]('link', { name: 'Contact' })
await [Link]('textbox', { name: 'Email' })

Text-Based
await [Link]('Sign up')
await [Link](/sign up/i) // regex

Label-Based
await [Link]('Email address')
await [Link]('Password')

Test ID
await [Link]('submit-btn')

✅ BEST PRACTICE: Prefer getByRole() and getByLabel() - they're most


resilient and promote accessibility.

9
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

3.3 Common Actions


// Click
await [Link]('button').click();

// Type text
await [Link]('Username').fill('john');

// Check/uncheck
await [Link]('checkbox').check();

// Select dropdown
await [Link]('Country').selectOption('USA');

3.4 Assertions
// Page
await expect(page).toHaveTitle('Dashboard');
await expect(page).toHaveURL(/dashboard/);

// Element
await expect([Link]('Success')).toBeVisible();
await expect([Link]('heading')).toHaveText('Welcome');
await
expect([Link]('Email')).toHaveValue('test@[Link]');

3.5 Real Login Example


[Link]('Login', () => {
[Link](async ({ page }) => {
await [Link]('/login');
});

test('successful login', async ({ page }) => {


await [Link]('Email').fill('user@[Link]');
await [Link]('Password').fill('pass123');
await [Link]('button', { name: 'Log in' }).click();

10
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

await expect(page).toHaveURL(/dashboard/);
await expect([Link]('Welcome')).toBeVisible();
});
});

11
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 4: UNDERSTANDING PLAYWRIGHT


ARCHITECTURE

4.1 Three-Tier Architecture


• Your test code runs in [Link]
• Playwright controls browsers via DevTools Protocol
• Browsers run in separate processes

4.2 Browser, Context, and Page

Browser
const browser = await [Link]({ headless: false });

Context (Isolated Session)


const context = await [Link]({
viewport: { width: 1280, height: 720 },
locale: 'en-US'
});

Page (Single Tab)


const page = await [Link]();
await [Link]('[Link]

🏢 ENTERPRISE: Browser contexts allow testing multiple users


simultaneously in one browser - perfect for enterprise applications.

4.3 Auto-Waiting Details


Before every action, Playwright waits for:

• Element attached to DOM


• Element visible
• Element stable (not animating)

12
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

• Element receives events


• Element enabled

4.4 Network Interception


await [Link]('**/api/users', route => {
[Link]({
status: 200,
body: [Link]([{ id: 1, name: 'Mock' }])
});
});

13
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

PART II: CORE CONCEPTS

14
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 5: LOCATORS AND SELECTORS

5.1 Locator Strategy Priority


1. Role-based (BEST)
[Link]('button', { name: 'Submit' })

2. Label-based (Great for forms)


[Link]('Email address')

3. Text-based
[Link]('Welcome')

4. Test ID (Stable)
[Link]('submit-btn')

5.2 Chaining Locators


await page
.locator('.user-section')
.getByRole('button', { name: 'Edit' })
.click();

5.3 Filtering
await page
.getByRole('listitem')
.filter({ hasText: 'Product 1' })
.click();

5.4 Multiple Elements


const count = await [Link]('listitem').count();
await [Link]('button').first().click();
await [Link]('button').nth(2).click();

15
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

✅ BEST PRACTICE: Use role-based locators - they reflect how users


interact with your app and promote accessibility.

16
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 6: ACTIONS AND INTERACTIONS

6.1 Clicking
// Simple click
await [Link]('button').click();

// Double click
await [Link]('File').dblclick();

// Right click
await [Link]('Item').click({ button: 'right' });

// Click with modifiers


await [Link]('Link').click({ modifiers: ['Control'] });

6.2 Typing
// Fill (clears first)
await [Link]('Username').fill('[Link]');

// Type (doesn't clear)


await [Link]('Search').type('playwright');

// Press keys
await [Link]('Enter');
await [Link]('Control+A');

6.3 Form Interactions


// Checkbox
await [Link]('checkbox', { name: 'Terms' }).check();
await [Link]('checkbox').uncheck();

// Radio button
await [Link]('radio', { name: 'Option 1' }).check();

// Dropdown

17
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

await [Link]('Country').selectOption('USA');
await [Link]('Country').selectOption({ label: 'United
States' });

6.4 File Uploads


await [Link]('Upload').setInputFiles('[Link]');
await [Link]('Upload').setInputFiles(['[Link]',
'[Link]']);

6.5 Mouse Actions


// Hover
await [Link]('link', { name: 'Products' }).hover();

// Drag and drop


await [Link]('Drag').dragTo([Link]('Drop here'));

6.6 Complete Form Example


await [Link]('First Name').fill('John');
await [Link]('Email').fill('john@[Link]');
await [Link]('Country').selectOption('USA');
await [Link]('I agree').check();
await [Link]('button', { name: 'Register' }).click();

18
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 7: ASSERTIONS AND VALIDATIONS

7.1 Auto-Retrying Assertions


Playwright assertions automatically retry until timeout:

// Will retry for 5 seconds by default


await expect([Link]('Success')).toBeVisible();

7.2 Page Assertions


await expect(page).toHaveURL('[Link]
await expect(page).toHaveURL(/dashboard/);
await expect(page).toHaveTitle('My Dashboard');
await expect(page).toHaveTitle(/Dashboard/);

7.3 Element Visibility


await expect([Link]('Welcome')).toBeVisible();
await expect([Link]('Loading')).toBeHidden();
await expect([Link]('Error')).[Link]();

7.4 Element State


await expect([Link]('button')).toBeEnabled();
await expect([Link]('button')).toBeDisabled();
await expect([Link]('Terms')).toBeChecked();
await expect([Link]('Terms')).[Link]();

7.5 Text Content


// Exact match
await expect([Link]('heading')).toHaveText('Welcome');

// Contains
await
expect([Link]('paragraph')).toContainText('Playwright');

19
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

// Array match
await expect([Link]('listitem')).toHaveText(['Item 1', 'Item
2']);

7.6 Input Values


await
expect([Link]('Email')).toHaveValue('user@[Link]');
await expect([Link]('Email')).toHaveValue(/.*@[Link]/);

7.7 Attributes
await expect([Link]('button')).toHaveAttribute('type',
'submit');
await expect([Link]('button')).toHaveClass('btn-primary');
await expect([Link]('button')).toHaveClass(/btn-/);

7.8 Count
await expect([Link]('listitem')).toHaveCount(5);
await expect([Link]('listitem')).toHaveCount(0);

7.9 Screenshots
await expect(page).toHaveScreenshot('[Link]');
await
expect([Link]('button')).toHaveScreenshot('[Link]');

7.10 Custom Timeout


await expect([Link]('Slow')).toBeVisible({ timeout: 30000 });

✅ BEST PRACTICE: Use soft assertions for non-critical checks that


shouldn't stop test execution: await [Link](element).toBeVisible();

20
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 8: PAGE OBJECT MODEL & COMPONENT


OBJECT MODEL

8.1 Traditional Page Object Pattern

Basic Page Object


// pages/[Link]
import { Page, Locator } from '@playwright/test';

export class LoginPage {


readonly page: Page;
readonly emailInput: Locator;
readonly passwordInput: Locator;
readonly submitButton: Locator;

constructor(page: Page) {
[Link] = page;
[Link] = [Link]('Email');
[Link] = [Link]('Password');
[Link] = [Link]('button', { name: 'Log in' });
}

async goto() {
await [Link]('/login');
}

async login(email: string, password: string) {


await [Link](email);
await [Link](password);
await [Link]();
}
}

Using Page Objects


test('login test', async ({ page }) => {
const loginPage = new LoginPage(page);

21
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

await [Link]();
await [Link]('user@[Link]', 'pass123');
await expect(page).toHaveURL('/dashboard');
});

8.2 Component Object Model (COM) - Enterprise Approach

🏢 ENTERPRISE: Modern apps are component-based. If your Header


appears on 15 pages and changes, you have to update 15 Page Objects.
Component Objects solve this.

Component Object Example


// components/[Link]
export class HeaderComponent {
readonly page: Page;
readonly logo: Locator;
readonly searchInput: Locator;
readonly userMenu: Locator;
readonly logoutBtn: Locator;

constructor(page: Page) {
[Link] = page;
[Link] = [Link]('logo');
[Link] = [Link]('search');
[Link] = [Link]('user-menu');
[Link] = [Link]('logout');
}

async clickLogo() {
await [Link]();
}

async search(query: string) {


await [Link](query);
await [Link]('Enter');
}

async logout() {

22
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

await [Link]();
await [Link]();
}
}

Using Component Objects in Page Objects


// pages/[Link]
import { HeaderComponent } from '../components/HeaderComponent';

export class DashboardPage {


header: HeaderComponent;

constructor(private page: Page) {


[Link] = new HeaderComponent(page);
}

async navigateToTransactions() {
await [Link]('transactions');
}
}

8.3 Reusable Component Architecture

Base Page Pattern


// pages/[Link]
export class BasePage {
readonly page: Page;
readonly header: HeaderComponent;
readonly footer: FooterComponent;

constructor(page: Page) {
[Link] = page;
[Link] = new HeaderComponent(page);
[Link] = new FooterComponent(page);
}
}

23
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

Extending Base Page


export class ProductPage extends BasePage {
readonly addToCartBtn: Locator;

constructor(page: Page) {
super(page);
[Link] = [Link]('button', { name: 'Add to
Cart' });
}

async addToCart() {
await [Link]();
}
}

🏢 ENTERPRISE: Component Object Model reduces maintenance by


80%. Update HeaderComponent once, and all 15 pages automatically get
the update.

✅ BEST PRACTICE: Use COM for Header, Footer, Navigation, Modals,


and any UI component that appears across multiple pages.

8.4 Best Practices


• Use descriptive class and method names
• Keep page objects focused - one page, one class
• Use readonly for locators
• Return page objects for method chaining
• Extract common components (header, footer)
• Use base page for shared functionality

24
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

SUMMARY

What You've Learned

PART I - Foundations:
• Testing Trophy Strategy for modern applications
• Why Playwright for enterprise applications
• Environment setup and configuration
• Writing your first tests
• Playwright architecture and auto-waiting

PART II - Core Concepts:


• Modern locator strategies (role-based)
• Actions and interactions
• Auto-retrying assertions
• Page Object Model
• Component Object Model for enterprise apps

🏢 ENTERPRISE: You now have the foundation to build enterprise-grade


test automation. Parts III & IV will cover advanced testing, performance,
CI/CD, and AI-powered patterns.

Next Steps:
• Practice writing tests with role-based locators
• Implement Component Objects in your project
• Set up CI/CD with GitHub Actions
• Move to Part III for advanced patterns

25
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

Playwright Automation with


TypeScript
The Enterprise Edition

PARTS III, IV, V: Advanced & Enterprise Patterns

Chapters 9-17 • AI-Driven Testing • Banking-Ready

26
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

Table of Contents

PART III: ADVANCED TESTING (Chapters 9-12)


• Chapter 9: Handling Complex Scenarios
• Chapter 10: API Testing - REST, GraphQL, Hybrid UI+API
• Chapter 11: Visual Testing & Accessibility
• Chapter 12: Performance Testing

PART IV: ENTERPRISE PATTERNS (Chapters 13-16)


• Chapter 13: Test Organization and Structure
• Chapter 14: CI/CD Integration & Test Sharding
• Chapter 15: Debugging and Troubleshooting
• Chapter 16: Advanced Patterns and Techniques

PART V: THE FUTURE (Chapter 17)


• Chapter 17: AI-Driven Testing

27
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

PART III: ADVANCED TESTING

28
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 9: HANDLING COMPLEX SCENARIOS

9.1 Authentication State Management

Saving Authentication State


Save login state once, reuse across all tests - massive time savings.

// [Link] - Run once before all tests


import { test as setup } from '@playwright/test';

const authFile = 'playwright/.auth/[Link]';

setup('authenticate', async ({ page }) => {


await [Link]('/login');
await [Link]('Email').fill('user@[Link]');
await [Link]('Password').fill('SecurePass123');
await [Link]('button', { name: 'Log in' }).click();

// Wait for successful login


await [Link]('/dashboard');

// Save authentication state


await [Link]().storageState({ path: authFile });
});

Reusing Authentication State


// [Link]
export default defineConfig({
projects: [
{ name: 'setup', testMatch: /.*\.setup\.ts/ },
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
storageState: 'playwright/.auth/[Link]'

29
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

},
dependencies: ['setup']
}
]
});

🏢 ENTERPRISE: This pattern saves 5-10 seconds per test. For 1000
tests, that's 1.5-3 hours saved!

9.2 File Downloads

test('download report', async ({ page }) => {


// Wait for download event
const [download] = await [Link]([
[Link]('download'),
[Link]('Download Report').click()
]);

// Get download path


const path = await [Link]();
[Link]('Downloaded to:', path);

// Save with custom name


await [Link]('/downloads/' +
[Link]());

// Verify file downloaded


expect([Link]()).toContain('[Link]');
});

9.3 File Uploads

// Single file
await [Link]('Upload
document').setInputFiles('[Link]');

30
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

// Multiple files
await [Link]('Upload photos').setInputFiles([
'[Link]',
'[Link]',
'[Link]'
]);

// Remove files
await [Link]('Upload').setInputFiles([]);

9.4 Geolocation Testing

test('location-based features', async ({ context, page }) => {


// Grant geolocation permission
await [Link](['geolocation']);

// Set location to New York


await [Link]({
latitude: 40.7128,
longitude: -74.0060
});

await [Link]('/stores');

// Verify location-based content


await expect([Link]('Stores near you')).toBeVisible();
await expect([Link]('New York, NY')).toBeVisible();
});

9.5 Network Mocking

test('mock API response', async ({ page }) => {


// Intercept API call
await [Link]('**/api/users', route => {
[Link]({
status: 200,
contentType: 'application/json',

31
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

body: [Link]([
{ id: 1, name: 'Alice', role: 'Admin' },
{ id: 2, name: 'Bob', role: 'User' }
])
});
});

await [Link]('/users');

// Verify mocked data appears


await expect([Link]('Alice')).toBeVisible();
await expect([Link]('Bob')).toBeVisible();
});

9.6 Multiple Browser Contexts

test('multi-user scenario', async ({ browser }) => {


// User 1: Admin
const adminContext = await [Link]({
storageState: '[Link]'
});
const adminPage = await [Link]();

// User 2: Regular user


const userContext = await [Link]({
storageState: '[Link]'
});
const userPage = await [Link]();

// Admin creates a resource


await [Link]('/admin/resources');
await [Link]('button', { name: 'Create' }).click();

// User sees the new resource


await [Link]('/resources');
await expect([Link]('New Resource')).toBeVisible();

// Cleanup
await [Link]();

32
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

await [Link]();
});

🏢 ENTERPRISE: Multiple contexts enable testing collaborative features,


permissions, and multi-user workflows - essential for enterprise
applications.

9.7 Handling Popups and New Tabs

test('popup window', async ({ context, page }) => {


// Wait for popup
const [popup] = await [Link]([
[Link]('page'),
[Link]('Open Terms').click()
]);

// Wait for popup to load


await [Link]();

// Interact with popup


await expect([Link]('heading')).toHaveText('Terms of
Service');
await [Link]('button', { name: 'Accept' }).click();

// Popup closes, continue on main page


await expect([Link]('Terms Accepted')).toBeVisible();
});

9.8 Working with iFrames

test('iframe interaction', async ({ page }) => {


await [Link]('/payment');

// Get frame locator


const paymentFrame = [Link]('iframe[name="payment"]');

33
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

// Interact with elements inside iframe


await [Link]('Card
Number').fill('4242424242424242');
await [Link]('Expiry').fill('12/25');
await [Link]('CVC').fill('123');
await [Link]('button', { name: 'Pay' }).click();

// Back to main page


await expect([Link]('Payment Successful')).toBeVisible();
});

🏦 BANKING: Payment gateways often use iframes. This pattern is


essential for banking and e-commerce testing.

34
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 10: API TESTING WITH PLAYWRIGHT

10.1 REST API Testing

Basic GET Request


test('GET users', async ({ request }) => {
const response = await
[Link]('[Link]

expect([Link]()).toBe(200);

const users = await [Link]();


expect(users).toBeInstanceOf(Array);
expect(users[0]).toHaveProperty('id');
expect(users[0]).toHaveProperty('email');
});

POST Request
test('POST create user', async ({ request }) => {
const response = await
[Link]('[Link] {
data: {
name: 'John Doe',
email: 'john@[Link]',
role: 'user'
}
});

expect([Link]()).toBe(201);

const user = await [Link]();


expect([Link]).toBe('John Doe');
expect(user).toHaveProperty('id');
});

Authentication Headers

35
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

test('authenticated request', async ({ request }) => {


const token = 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';

const response = await [Link]('/api/admin/users', {


headers: {
'Authorization': token,
'Content-Type': 'application/json'
}
});

expect([Link]()).toBe(200);
});

10.2 GraphQL API Testing

test('GraphQL query', async ({ request }) => {


const query = `
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
posts {
title
}
}
}
`;

const response = await [Link]('/graphql', {


data: {
query,
variables: { id: '123' }
}
});

const result = await [Link]();


expect([Link]).toBe('Alice');
expect([Link]).toHaveLength(5);

36
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

});

10.3 Hybrid UI + API Testing (The 80/20 Rule)

🏢 ENTERPRISE: The 80/20 Rule: Use APIs for 80% of test setup
(creating users, adding data, setting state). Use UI for the critical 20% you
actually want to test.

Why Hybrid Testing?


• Speed: API calls take 200ms vs UI login taking 5 seconds
• Reliability: APIs don't have animation delays or loading spinners
• Focus: Test the UI behavior, not the setup
• Efficiency: Run 1000 tests in minutes instead of hours

Banking Purchase Flow Example


test('purchase with API setup', async ({ request, page }) => {
// Step 1: Create user via API (200ms)
const userResponse = await [Link]('/api/users', {
data: {
name: 'Test User',
accountNumber: '123456789',
balance: 10000
}
});
const user = await [Link]();

// Step 2: Get auth token via API (100ms)


const authResponse = await [Link]('/api/auth/login', {
data: {
accountNumber: '123456789',
pin: '1234'
}
});
const { token } = await [Link]();

// Step 3: Set auth token in browser (instant)


await [Link]().addCookies([{

37
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

name: 'auth_token',
value: token,
domain: 'localhost',
path: '/'
}]);

// Step 4: UI Test - Focus on purchase flow (5s)


await [Link]('/shop');
await [Link]('button', { name: 'Buy Now' }).click();
await [Link]('button', { name: 'Confirm Purchase'
}).click();

// Verify
await expect([Link]('Purchase Successful')).toBeVisible();

// Verify via API


const balanceResponse = await
[Link](`/api/users/${[Link]}/balance`, {
headers: { Authorization: `Bearer ${token}` }
});
const { balance } = await [Link]();
expect(balance).toBeLessThan(10000);
});

✅ BEST PRACTICE: This test runs in 6 seconds instead of 15 seconds.


Multiply by 1000 tests = 2.5 hours saved!

10.4 API-First Data Setup Strategy

// [Link]() - Set up test data via API


[Link](async ({ request }) => {
// Create test products
await [Link]('/api/products', {
data: [
{ name: 'Laptop', price: 999 },
{ name: 'Mouse', price: 29 }
]
});

38
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

// Create test categories


await [Link]('/api/categories', {
data: [
{ name: 'Electronics' },
{ name: 'Accessories' }
]
});
});

// [Link]() - Clean up via API


[Link](async ({ request }) => {
await [Link]('/api/test-data');
});

🏦 BANKING: For banking applications, use APIs to set up account


balances, transaction history, and user preferences. Then use UI to test the
actual user journey.

39
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 11: VISUAL TESTING & ACCESSIBILITY

11.1 Visual Regression Testing

Basic Screenshot Comparison


test('homepage visual test', async ({ page }) => {
await [Link]('/');
await expect(page).toHaveScreenshot('[Link]');
});

Element Screenshot
test('header visual test', async ({ page }) => {
await [Link]('/');
const header = [Link]('header');
await expect(header).toHaveScreenshot('[Link]');
});

Full Page Screenshot


test('full page screenshot', async ({ page }) => {
await [Link]('/pricing');
await expect(page).toHaveScreenshot('[Link]', {
fullPage: true
});
});

Advanced Configuration
test('dashboard with masking', async ({ page }) => {
await [Link]('/dashboard');

await expect(page).toHaveScreenshot('[Link]', {
maxDiffPixels: 100, // Allow 100 pixels difference
threshold: 0.2, // 20% difference threshold
mask: [ // Hide dynamic content
[Link]('.timestamp'),
[Link]('.balance')

40
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

],
animations: 'disabled' // Disable animations
});
});

Responsive Visual Testing


const viewports = [
{ name: 'mobile', width: 375, height: 667 },
{ name: 'tablet', width: 768, height: 1024 },
{ name: 'desktop', width: 1920, height: 1080 }
];

for (const viewport of viewports) {


test(`visual test - ${[Link]}`, async ({ page }) => {
await [Link](viewport);
await [Link]('/');
await expect(page).toHaveScreenshot(`page-${[Link]}.png');
});
}

Updating Baselines
# Update all screenshots
npx playwright test --update-snapshots

# Update specific test


npx playwright test [Link] --update-snapshots

11.2 Accessibility Testing with axe-core

🏦 BANKING: Banks like JPMorgan and Mastercard must comply with


ADA and WCAG 2.1 Level AA standards. Failing accessibility audits can
result in lawsuits and regulatory fines.

Installation
npm install -D @axe-core/playwright

41
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

Basic Accessibility Test


import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

test('homepage accessibility', async ({ page }) => {


await [Link]('/');

const accessibilityScanResults = await new AxeBuilder({ page })


.analyze();

expect([Link]).toEqual([]);
});

Testing Specific WCAG Criteria


test('banking form accessibility', async ({ page }) => {
await [Link]('/transfer');

const results = await new AxeBuilder({ page })


.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.analyze();

if ([Link] > 0) {
[Link]('Accessibility Violations:');
[Link](violation => {
[Link](`- ${[Link]}`);
[Link](` Impact: ${[Link]}`);
[Link](` Elements: ${[Link]}`);
});
}

expect([Link]).toEqual([]);
});

11.3 Banking Compliance & WCAG Standards

42
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

Critical WCAG Success Criteria


• 1.1.1: Text Alternatives (Level A)
• 1.3.1: Info and Relationships (Level A)
• 1.4.3: Contrast (Minimum) (Level AA)
• 2.1.1: Keyboard (Level A)
• 2.4.7: Focus Visible (Level AA)
• 3.3.2: Labels or Instructions (Level A)

Exclude Known Issues


test('accessibility with exclusions', async ({ page }) => {
await [Link]('/');

const results = await new AxeBuilder({ page })


.exclude('.third-party-widget') // Exclude third-party
.disableRules(['color-contrast']) // Temporary exclusion
.analyze();

expect([Link]).toEqual([]);
});

✅ BEST PRACTICE: Run accessibility tests in CI/CD to catch violations


before production. A single lawsuit can cost millions.

43
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 12: PERFORMANCE TESTING

12.1 Core Web Vitals

test('measure Core Web Vitals', async ({ page }) => {


await [Link]('/');

const metrics = await [Link](() => {


const perfEntries = [Link]('paint');
const navigation = [Link]('navigation')[0];

return {
FCP: [Link](e => [Link] === 'first-contentful-
paint')?.startTime,
LCP: [Link]('largest-contentful-
paint')[0]?.startTime,
domContentLoaded: navigation?.domContentLoadedEventEnd -
navigation?.domContentLoadedEventStart,
loadComplete: navigation?.loadEventEnd -
navigation?.loadEventStart
};
});

// Assert thresholds
expect([Link]).toBeLessThan(1800); // < 1.8s
expect([Link]).toBeLessThan(2500); // < 2.5s
});

12.2 Page Load Time

test('page load performance', async ({ page }) => {


const startTime = [Link]();
await [Link]('[Link]
const loadTime = [Link]() - startTime;

[Link](`Page loaded in: ${loadTime}ms`);


expect(loadTime).toBeLessThan(3000); // < 3 seconds

44
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

});

12.3 Resource Timing

test('analyze resource loading', async ({ page }) => {


await [Link]('/');

const resources = await [Link](() => {


return [Link]('resource').map(r => ({
name: [Link],
duration: [Link],
size: [Link],
type: [Link]
}));
});

// Find slow resources


const slowResources = [Link](r => [Link] > 1000);
[Link]('Slow resources:', slowResources);

expect([Link]).toBe(0);
});

12.4 API Performance

test('API response time', async ({ request }) => {


const startTime = [Link]();

const response = await [Link]('/api/users');

const responseTime = [Link]() - startTime;

[Link](`API responded in: ${responseTime}ms`);


expect([Link]()).toBe(200);
expect(responseTime).toBeLessThan(500); // < 500ms
});

45
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

12.5 Performance Budget

test('performance budget', async ({ page }) => {


await [Link]('/');

const metrics = await [Link](() => {


const nav = [Link]('navigation')[0];
const resources = [Link]('resource');

return {
pageLoadTime: nav?.loadEventEnd - nav?.fetchStart,
domContentLoaded: nav?.domContentLoadedEventEnd -
nav?.fetchStart,
totalPageSize: [Link]((sum, r) => sum +
[Link], 0),
numberOfRequests: [Link]
};
});

// Performance budget
const budget = {
pageLoadTime: 3000, // 3 seconds
domContentLoaded: 2000, // 2 seconds
totalPageSize: 2 * 1024 * 1024, // 2 MB
numberOfRequests: 50 // 50 requests
};

expect([Link]).toBeLessThan([Link]);

expect([Link]).toBeLessThan([Link]
);
expect([Link]).toBeLessThan([Link]);

expect([Link]).toBeLessThan([Link]
);
});

🏦 BANKING: Performance is critical for banking applications. Slow pages


lead to abandoned transactions and lost revenue.

46
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

47
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

PART IV: ENTERPRISE PATTERNS

48
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 13: TEST ORGANIZATION AND STRUCTURE

13.1 Enterprise Directory Structure

playwright-project/
├── tests/
│ ├── e2e/ # End-to-end tests
│ │ ├── auth/
│ │ ├── transactions/
│ │ └── admin/
│ ├── api/ # API tests
│ ├── visual/ # Visual regression
│ └── performance/ # Performance tests
├── pages/ # Page Objects
│ ├── [Link]
│ └── [Link]
├── components/ # Component Objects
│ ├── [Link]
│ └── [Link]
├── fixtures/ # Custom fixtures
│ └── [Link]
├── helpers/ # Utility functions
│ ├── [Link]
│ └── [Link]
├── data/ # Test data
│ └── [Link]
├── config/ # Environment configs
│ ├── [Link]
│ └── [Link]
└── [Link]

13.2 Custom Fixtures

// fixtures/[Link]
import { test as base } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';

49
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

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

export const test = [Link]<CustomFixtures>({


loginPage: async ({ page }, use) => {
await use(new LoginPage(page));
},

authenticatedPage: async ({ page }, use) => {


// Auto-login before test
await [Link]('/login');
await [Link]('Email').fill('user@[Link]');
await [Link]('Password').fill('pass123');
await [Link]('button', { name: 'Log in' }).click();
await [Link]('/dashboard');

await use(page);
}
});

13.3 Test Hooks

[Link](async ({ page }) => {


await [Link]('/');
});

[Link](async ({ page }, testInfo) => {


if ([Link] !== 'passed') {
// Capture screenshot on failure
await [Link]({ path: `failure-${[Link]}.png` });
}
});

13.4 Tagging Tests

50
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

test('critical flow @smoke @critical', async ({ page }) => {


// Test implementation
});

test('admin feature @admin', async ({ page }) => {


// Test implementation
});

# Run only smoke tests


npx playwright test --grep @smoke

# Run everything except admin


npx playwright test --grep-invert @admin

13.5 Data-Driven Testing

const users = [
{ role: 'admin', email: 'admin@[Link]', expectedUrl: '/admin'
},
{ role: 'user', email: 'user@[Link]', expectedUrl:
'/dashboard' },
{ role: 'guest', email: 'guest@[Link]', expectedUrl:
'/welcome' }
];

for (const userData of users) {


test(`${[Link]} login flow`, async ({ page }) => {
await [Link]('/login');
await [Link]('Email').fill([Link]);
await [Link]('button', { name: 'Log in' }).click();
await expect(page).toHaveURL([Link]);
});
}

13.6 Environment Configuration

51
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

// config/[Link]
export const environments = {
local: {
baseUrl: '[Link]
apiUrl: '[Link]
},
staging: {
baseUrl: '[Link]
apiUrl: '[Link]
},
production: {
baseUrl: '[Link]
apiUrl: '[Link]
}
};

const env = [Link] || 'local';


export const config = environments[env];

✅ BEST PRACTICE: Organize tests by feature/module, not by type. Keep


related tests together for easier maintenance.

52
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 14: CI/CD INTEGRATION & TEST SHARDING

14.1 GitHub Actions Advanced Workflows

Basic Workflow
# .github/workflows/[Link]
name: Playwright Tests
on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18

- name: Install dependencies


run: npm ci

- name: Install Playwright


run: npx playwright install --with-deps

- name: Run tests


run: npx playwright test

- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30

14.2 Test Sharding: 1000 Tests in 5 Minutes

53
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

🏢 ENTERPRISE: Sharding splits your test suite across 10-20 parallel


virtual machines. Each machine runs a subset of tests simultaneously.

The Math
• 1000 tests × 5 seconds each = 5000 seconds (83 minutes)
• With 10 shards: 5000 ÷ 10 = 500 seconds (8.3 minutes)
• With 20 shards: 5000 ÷ 20 = 250 seconds (4.2 minutes)

GitHub Actions Sharding


name: Playwright Tests (Sharded)
on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
shardIndex: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
shardTotal: [10]

steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3

- name: Install dependencies


run: npm ci

- name: Install Playwright


run: npx playwright install --with-deps

- name: Run tests (Shard ${{ [Link] }}/${{


[Link] }})
run: npx playwright test --shard=${{ [Link] }}/${{
[Link] }}

- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report-${{ [Link] }}

54
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

path: playwright-report/

14.3 Parallel Execution Strategies

Configuration
// [Link]
export default defineConfig({
workers: [Link] ? 2 : undefined, // 2 in CI, max locally
fullyParallel: true, // Parallel within files
retries: [Link] ? 2 : 0, // Retry failures in CI
});

Docker Integration
# Dockerfile
FROM [Link]/playwright:v1.40.0-focal
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
CMD ["npx", "playwright", "test"]

# [Link]
version: '3'
services:
playwright:
build: .
environment:
- CI=true
volumes:
- ./playwright-report:/app/playwright-report

✅ BEST PRACTICE: Use 10 shards for most projects. Use 20+ shards for
massive test suites (2000+ tests). GitHub Actions allows up to 256 parallel
jobs.

55
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 15: DEBUGGING AND TROUBLESHOOTING

15.1 Debug Mode

# Run in debug mode


npx playwright test --debug

# Debug specific test


npx playwright test [Link] --debug

# Pause on test
await [Link](); // Add this line in your test

15.2 Headed Mode

# See browser while testing


npx playwright test --headed

# Slow down execution


npx playwright test --headed --slow-mo=1000

15.3 Trace Viewer Analysis for Flaky Tests

Configuration
// [Link]
use: {
trace: 'on-first-retry', // Capture on failure
screenshot: 'only-on-failure',
video: 'retain-on-failure'
}

View Trace

56
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

npx playwright show-trace [Link]

🏢 ENTERPRISE: Trace Viewer lets you see exactly what happened


during test execution - screenshots, DOM snapshots, network activity,
console logs - perfect for debugging flaky tests without re-running them.

15.4 Production Debugging Strategies

Network Debugging
[Link]('request', request => {
[Link]('→', [Link](), [Link]());
});

[Link]('response', response => {


[Link]('←', [Link](), [Link]());
});

[Link]('requestfailed', request => {


[Link]('✗', [Link](), [Link]()?.errorText);
});

Console Debugging
[Link]('console', msg => {
[Link]('Browser console:', [Link]());
});

[Link]('pageerror', error => {


[Link]('Page error:', [Link]);
});

Element Debugging
// Check if element exists
const count = await [Link]();
[Link](`Found ${count} elements`);

57
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

// Highlight element
await [Link]();

// Get element info


const box = await [Link]();
[Link]('Element position:', box);

15.5 Common Issues & Solutions

Timeout Issues
// Increase timeout for specific test
[Link](120000); // 2 minutes

// Increase timeout for specific action


await [Link]('button').click({ timeout: 60000 });

Flaky Tests

⚠️ WARNING: Avoid using [Link]() - it makes tests slower


and more flaky. Use proper waits instead.

// ❌ Bad - arbitrary wait


await [Link](3000);

// ✅ Good - wait for specific condition


await [Link]('networkidle');
await [Link]('.loaded');
await [Link]('Success').waitFor();

Screenshots for Debugging


// Full page screenshot
await [Link]({ path: '[Link]', fullPage: true });

// Element screenshot
await [Link]('.error').screenshot({ path: '[Link]' });

58
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

✅ BEST PRACTICE: Enable trace on first retry to get detailed information


about failures without slowing down successful tests.

59
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 16: ADVANCED PATTERNS AND TECHNIQUES

16.1 Network Mocking for Resilience Testing

🏢 ENTERPRISE: Test how your application handles server failures,


timeouts, and network errors. This is critical for banking applications where
uptime is essential.

Simulating Server Downtime


test('handles banking server down', async ({ page }) => {
// Mock API to return 503 Service Unavailable
await [Link]('**/api/transactions', route => {
[Link]({
status: 503,
contentType: 'application/json',
body: [Link]({
error: 'Service temporarily unavailable'
})
});
});

await [Link]('/transactions');

// Verify graceful error handling


await expect([Link]('Service temporarily
unavailable')).toBeVisible();
await expect([Link]('button', { name: 'Retry'
})).toBeVisible();
await expect([Link]('Please try again
later')).toBeVisible();
});

Simulating Slow Network


test('handles slow network', async ({ page }) => {
await [Link]('**/api/**', route => {
// Delay response by 5 seconds
setTimeout(() => [Link](), 5000);

60
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

});

await [Link]('/dashboard');

// Verify loading state shows


await expect([Link]('Loading...')).toBeVisible();
});

16.2 Global Teardown & Data Cleanup

Global Teardown Script


// [Link]
import { FullConfig } from '@playwright/test';

async function globalTeardown(config: FullConfig) {


[Link]('Cleaning up test data...');

// Clean up database
const response = await fetch('[Link] {
method: 'DELETE',
headers: { 'X-Test-Cleanup': 'true' }
});

if ([Link]) {
[Link]('✓ Test data cleaned up successfully');
}
}

export default globalTeardown;

Configuration
// [Link]
export default defineConfig({
globalTeardown: [Link]('./global-teardown'),
});

61
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

🏢 ENTERPRISE: After 1000 tests, ensure all test data in the database is
cleaned up via API. This prevents data pollution and ensures tests remain
independent.

16.3 Self-Healing Test Patterns

Smart Locator with Fallbacks


// utils/[Link]
export async function smartLocator(
page: Page,
primarySelector: string,
fallbackSelectors: string[]
): Promise<Locator> {
// Try primary selector
let element = [Link](primarySelector);

if (await [Link]() > 0) {


return element;
}

// Try fallback selectors


for (const selector of fallbackSelectors) {
element = [Link](selector);
if (await [Link]() > 0) {
[Link](`Self-healed: Using ${selector} instead of
${primarySelector}`);
return element;
}
}

throw new Error(`Could not locate element: ${primarySelector}`);


}

Usage
const loginButton = await smartLocator(
page,
'[Link]-btn', // Primary
[ // Fallbacks

62
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

'button#login',
'[data-testid="login-button"]',
'button:has-text("Log in")'
]
);

await [Link]();

✅ BEST PRACTICE: Self-healing patterns reduce test maintenance by


60%. When selectors break, tests auto-repair instead of failing.

63
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

PART V: THE FUTURE

64
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CHAPTER 17: AI-DRIVEN TESTING

🤖 AI-POWERED: The future of QA automation lies in AI-assisted testing.


This chapter explores how AI is transforming Playwright automation in 2025
and beyond.

17.1 Prompt Engineering for Playwright Scripts

Using AI to Generate Test Scripts


Large Language Models like GPT-4 can generate Playwright test scripts from natural
language descriptions.

Example Prompt
Prompt: "Generate a Playwright test for a banking login flow
with multi-factor authentication. The test should:
1. Navigate to /login
2. Enter account number and PIN
3. Handle OTP verification
4. Verify successful login to dashboard"

AI-Generated Output
test('banking login with MFA', async ({ page }) => {
await [Link]('/login');

await [Link]('Account Number').fill('123456789');


await [Link]('PIN').fill('1234');
await [Link]('button', { name: 'Continue' }).click();

// Wait for OTP screen


await expect([Link]('Enter OTP')).toBeVisible();

// Enter OTP (in real scenario, fetch from email/SMS)


await [Link]('OTP').fill('123456');
await [Link]('button', { name: 'Verify' }).click();

65
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

await expect(page).toHaveURL('/dashboard');
await expect([Link]('Welcome')).toBeVisible();
});

✅ BEST PRACTICE: Use AI to generate 70% of boilerplate code, then


refine manually for edge cases and business logic.

17.2 Self-Healing Selectors with AI

The Problem: Brittle Selectors


Traditional selectors break when developers change class names, IDs, or DOM
structure. This causes test maintenance nightmares.

The Solution: AI-Powered Self-Healing


AI can analyze the page context and automatically find the correct element even
when selectors change.

Self-Healing Implementation
// utils/[Link]
import { Page, Locator } from '@playwright/test';
import OpenAI from 'openai';

export async function aiSmartLocator(


page: Page,
primarySelector: string,
fallbackSelectors: string[],
aiContext?: string
): Promise<Locator> {
// Try primary selector
let element = [Link](primarySelector);

if (await [Link]() > 0) {


return element;

66
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

// Try fallback selectors


for (const selector of fallbackSelectors) {
element = [Link](selector);
if (await [Link]() > 0) {
[Link](`Self-healed: ${selector}`);
return element;
}
}

// Use AI to find element


if (aiContext) {
const aiSelector = await findElementWithAI(page, aiContext);
return [Link](aiSelector);
}

throw new Error(`Could not locate: ${primarySelector}`);


}

async function findElementWithAI(page: Page, context: string) {


const openai = new OpenAI({ apiKey: [Link].OPENAI_API_KEY });

// Get page HTML


const html = await [Link]();

// Ask AI to find selector


const response = await [Link]({
model: 'gpt-4',
messages: [{
role: 'user',
content: `Find the best selector for:
${context}\n\nHTML:\n${html}`
}]
});

return [Link][0].[Link] || '';


}

Usage
const loginButton = await aiSmartLocator(

67
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

page,
'[Link]-btn',
['button#login', '[data-testid="login"]'],
'The main login button on the login page'
);

await [Link]();

🤖 AI-POWERED: Self-healing reduces test maintenance by 60%. When


selectors break, tests auto-repair instead of failing.

17.3 Using LLMs to Generate Edge-Case Test Data

The Challenge
Testing edge cases (special characters, unicode, SQL injection, XSS) is tedious and
often incomplete.

AI-Generated Test Data


// utils/[Link]
import OpenAI from 'openai';

export async function generateEdgeCases(fieldType: string):


Promise<string[]> {
const openai = new OpenAI({ apiKey: [Link].OPENAI_API_KEY });

const prompt = `Generate 20 edge case test values for a


${fieldType}
field in a banking application. Include:
- Special characters
- Unicode
- Very long strings
- SQL injection attempts
- XSS attempts
- Empty strings
- Whitespace
- Null bytes

68
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

Return as JSON array.`;

const response = await [Link]({


model: 'gpt-4',
messages: [{ role: 'user', content: prompt }]
});

const content = [Link][0].[Link] || '[]';


return [Link](content);
}

Using AI-Generated Data


test('form handles edge cases', async ({ page }) => {
const edgeCases = await generateEdgeCases('account_name');

for (const testValue of edgeCases) {


await [Link]('/account/create');
await [Link]('Account Name').fill(testValue);
await [Link]('button', { name: 'Submit' }).click();

// Should either accept valid or show error


const hasError = await [Link]('Invalid').isVisible();
const hasSuccess = await [Link]('Success').isVisible();

expect(hasError || hasSuccess).toBeTruthy();
}
});

✅ BEST PRACTICE: AI can generate 1000+ edge cases in seconds. This


level of coverage is impossible to achieve manually.

17.4 AI-Powered Test Maintenance

Automated Flaky Test Detection


AI can analyze test execution patterns and identify flaky tests before they become
problematic.

69
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

// AI analyzes 100 test runs and identifies patterns:


// - Test "login" passes 95/100 times (flaky)
// - Test "checkout" passes 100/100 times (stable)
//
// AI suggests fixes:
// 1. Add explicit wait for login button
// 2. Increase timeout from 5s to 10s
// 3. Add retry logic for network calls

Intelligent Test Generation


// AI watches manual testing sessions
// Generates automated tests automatically
// Suggests new test cases based on code changes

17.5 The Future of QA Automation

🤖 AI-POWERED: AI will not replace QA engineers. Instead, it will elevate


them from script writers to test strategists.

The AI-Augmented QA Engineer (2025)


• AI generates 80% of test code
• Engineers focus on test strategy and business logic
• Self-healing tests reduce maintenance by 70%
• AI-generated edge cases increase coverage by 10x
• Predictive analytics identify bugs before they happen

Skills for the Future

🏢 ENTERPRISE: To thrive in AI-powered QA, master: Prompt


engineering, AI tool integration, test strategy, domain knowledge, and
Playwright architecture.

• Prompt Engineering: Write effective prompts for AI


• AI Integration: Integrate LLMs into test frameworks
• Test Strategy: Design comprehensive test approaches
70
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

• Domain Knowledge: Understand business requirements


• Playwright Expertise: Deep framework knowledge

🏦 BANKING: JPMorgan and Mastercard are already using AI for test


generation, maintenance, and analysis. Stay ahead of the curve.

71
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

CONCLUSION

You Are Now Enterprise-Ready

Congratulations! You have completed the Playwright Enterprise Edition handbook.


You now possess advanced skills in:

PART III - Advanced Testing:


• Complex scenarios (auth, files, iframes, popups)
• API testing (REST, GraphQL, Hybrid UI+API)
• Visual regression and accessibility testing
• Performance testing and optimization

PART IV - Enterprise Patterns:


• Professional test organization
• CI/CD integration and test sharding
• Debugging with Trace Viewer
• Network resilience and self-healing tests

PART V - The Future:


• AI-powered test generation
• Self-healing selectors
• LLM-generated edge cases
• AI-powered test maintenance

🏢 ENTERPRISE: This knowledge positions you for senior roles at


JPMorgan, Mastercard, and other Fortune 500 companies.

Next Steps:
• Implement test sharding in your CI/CD pipeline
• Add accessibility tests with axe-core

72
Author-Vaibhav Sahu
PLAYWRIGHT HANDBOOK

• Experiment with AI-powered test generation


• Build a portfolio showcasing enterprise patterns
• Apply for senior QA/SDET roles

🤖 AI-POWERED: The future of QA is here. Lead the transformation. 🚀

Thank you for learning with Playwright Enterprise Edition!

73

You might also like