Playwright Testing Guide
Playwright Testing Guide
1. Getting Started
2. Fundamentals
3. Configuration
4. Playwright Inspector & CodeGen
5. Playwright Tracing
Chapter 1 Getting Started
Demo: Getting Started
Installation: npm init playwright
Running: npx playwright test
Test: e2e/example.spec.ts
Test: e2e/example.spec.ts
1. Test Isolation
Test: e2e/example.spec.ts
1. Test Isolation
2. Auto-waiting
Test: e2e/example.spec.ts
1. Test Isolation
● Full isolation
● Fast instantiation (~1ms)
● Low overhead Browser Context
Fundamentals: Auto-waiting
expect(locator).toBeChecked() expect(locator).toHaveClass(expected)
expect(locator).toBeDisabled() expect(locator).toHaveCount(count)
expect(locator).toBeEditable() expect(locator).toHaveCSS(name, value)
expect(locator).toBeEmpty() expect(locator).toHaveId(id)
expect(locator).toBeEnabled() expect(locator).toHaveJSProperty(name, value)
expect(locator).toBeFocused() expect(locator).toHaveText(expected)
expect(locator).toBeHidden() expect(page).toHaveTitle(title)
expect(locator).toBeVisible() expect(page).toHaveURL(url)
expect(locator).toContainText(text) expect(locator).toHaveValue(value)
expect(locator).toHaveAttribute(name)
📍 Locators API
● Locator := (page, selector)
● Create locators with page.locator(selector)
● Represents a view to the element(s) on the page
● Re-queries page on each method call
● “strict” by default
● Useful in POMs
Fundamentals: Web-First Assertions
1. Must be awaited
Fundamentals: Web-First Assertions
1. Must be awaited
1. Must be awaited
],
};
export default config;
// example.spec.ts // playwright.config.ts
import { test, expect } from '@playwright/test'; import { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
test('basic test', async ({ page }) => { projects: [
await page.goto('https://playwright.dev/'); {
await page.locator('text=Get started').click(); name: 'Desktop Chrome',
await expect(page).toHaveTitle(/Getting started/); use: { browserName: 'chromium', },
}); },
],
};
export default config;
// example.spec.ts // playwright.config.ts
import { test, expect } from '@playwright/test'; Run 1
import { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
test('basic test', async ({ page }) => { projects: [
await page.goto('https://playwright.dev/'); {
await page.locator('text=Get started').click(); name: 'Desktop Chrome',
await expect(page).toHaveTitle(/Getting started/); use: { browserName: 'chromium', },
}); },
],
};
export default config;
// example.spec.ts // playwright.config.ts
import { test, expect } from '@playwright/test'; Run 1
import { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
test('basic test', async ({ page }) => { projects: [
R
un
2
await page.goto('https://playwright.dev/'); {
await page.locator('text=Get started').click(); name: 'Desktop Chrome',
Run
await expect(page).toHaveTitle(/Getting started/); use: { browserName: 'chromium', },
3
}); },
{
name: 'Desktop Firefox',
use: { browserName: 'firefox', },
},
{
name: 'Desktop Safari',
use: { browserName: 'webkit', },
}
],
};
export default config;
// example.spec.ts // playwright.config.ts
import { test, expect } from '@playwright/test'; Run 1
import { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
test('basic test', async ({ page }) => { projects: [
R
un
2
await page.goto('https://playwright.dev/'); {
await page.locator('text=Get started').click(); name: 'Desktop Chrome',
Run
await expect(page).toHaveTitle(/Getting started/); use: { browserName: 'chromium', },
3
}); },
{
name: 'Desktop Firefox',
use: { browserName: 'firefox', },
},
{
name: 'Desktop Safari',
use: { browserName: 'webkit', },
}
],
};
export default config;
Granular Configuration: france.spec.ts
● Per-file configuration
● Per-suite configuration
// france.spec.ts
Ty
import { test, expect } from '@playwright/test';
p
eS
cr
ip
t
// france.spec.ts
Ty
import { test, expect } from '@playwright/test';
p
eS
cr
// per-file configuration
ip
t
test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' });
// france.spec.ts
Ty
import { test, expect } from '@playwright/test';
p
eS
cr
// per-file configuration
ip
t
test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' });
test('should work', async ({ page }) => { /* ... test goes here ... */ });
test('should use euro', async ({ page }) => { /* ... */ });
// france.spec.ts
Ty
import { test, expect } from '@playwright/test';
p
eS
cr
// per-file configuration
ip
t
test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' });
test('should work', async ({ page }) => { /* ... test goes here ... */ });
test('should use euro', async ({ page }) => { /* ... */ });
});
// france.spec.ts
Ty
import { test, expect } from '@playwright/test';
p
eS
cr
// per-file configuration
ip
t
test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' });
test('should work', async ({ page }) => { /* ... test goes here ... */ });
test('should use euro', async ({ page }) => { /* ... */ });
});
// france.spec.ts
Ty
import { test, expect } from '@playwright/test';
p
eS
cr
// per-file configuration
ip
t
test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' });
test('should work', async ({ page }) => { /* ... test goes here ... */ });
test('should use euro', async ({ page }) => { /* ... */ });
Ty
import { test, expect } from '@playwright/test';
p
eS
cr
// per-file configuration
ip
t
test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' });
test('should work', async ({ page }) => { /* ... test goes here ... */ });
test('should use euro', async ({ page }) => { /* ... */ });
● acceptDownloads ● javaScriptEnabled
● baseURL ● launchOptions
● browserName ● locale
● bypassCSP ● offline
● channel ● permissions
● colorScheme ● proxy
● deviceScaleFactor ● screenshot
● extraHTPHeaders ● storageState
● geolocation ● timezoneId
● hasTouch ● trace
● headless ● userAgent
● httpCredentials ● video
● ignoreHTTPSErrors ● viewport
Configuration: Data-Driven Tests
Configuration: Data-Driven Tests
Configuration: Data-Driven Tests
Ty
// check-urls.spec.ts
p
eS
cr
import { test, expect } from '@playwright/test';
ip
t
Ty
// check-urls.spec.ts
p
eS
cr
import { test, expect } from '@playwright/test';
ip
t
const urls = require('./urls.json');
Ty
// check-urls.spec.ts
p
eS
cr
import { test, expect } from '@playwright/test';
ip
t
const urls = require('./urls.json');
for (const url of urls) {
}
Ty
// check-urls.spec.ts
p
eS
cr
import { test, expect } from '@playwright/test';
ip
t
const urls = require('./urls.json');
for (const url of urls) {
test(`check ${url}`, async ({ page }) => {
await page.goto(url);
});
}
Ty
// check-urls.spec.ts
p
eS
cr
import { test, expect } from '@playwright/test';
ip
t
const urls = require('./urls.json');
for (const url of urls) {
test(`check ${url}`, async ({ page }) => {
await page.goto(url);
}); NOTE: Make sure to have
} different test titles
Configuration: Reporters
Configuration: Reporters
// playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
projects: [
{
name: 'Desktop Chrome',
use: { browserName: 'chromium', },
},
{
name: 'Desktop Firefox',
use: { browserName: 'firefox', },
},
{
name: 'Desktop Safari',
use: { browserName: 'webkit', },
}
],
};
export default config;
// playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: 'dot',
projects: [
{
name: 'Desktop Chrome',
use: { browserName: 'chromium', },
},
{
name: 'Desktop Firefox',
use: { browserName: 'firefox', },
},
{
name: 'Desktop Safari',
use: { browserName: 'webkit', },
}
],
};
export default config;
// playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: process.env.CI ? 'dot' : 'line',
projects: [
{
name: 'Desktop Chrome',
use: { browserName: 'chromium', },
},
{
name: 'Desktop Firefox',
use: { browserName: 'firefox', },
},
{
name: 'Desktop Safari',
use: { browserName: 'webkit', },
}
],
};
export default config;
// playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: [
process.env.CI ? ['dot'] : ['list'],
['json', { outputFile: 'test-results.json' }],
],
projects: [
{
name: 'Desktop Chrome',
use: { browserName: 'chromium', },
},
{
name: 'Desktop Firefox',
use: { browserName: 'firefox', },
},
{
name: 'Desktop Safari',
use: { browserName: 'webkit', },
}
],
};
export default config;
// playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: [
process.env.CI ? ['dot'] : ['list'],
['json', { outputFile: 'test-results.json' }],
Built-in reporters
],
projects: [ ➔ dot
{ ➔ list
name: 'Desktop Chrome', ➔ line
use: { browserName: 'chromium', }, ➔ json
}, ➔ junit
{
name: 'Desktop Firefox', Third-party reporters
use: { browserName: 'firefox', },
➔ allure-playwright
},
{
name: 'Desktop Safari',
https://aka.ms/playwright/reporters
use: { browserName: 'webkit', },
}
],
};
export default config;
Configuration: Devices
// playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: [
process.env.CI ? ['dot'] : ['list'],
['json', { outputFile: 'test-results.json' }],
],
projects: [
{
name: 'Desktop Chrome',
use: { browserName: 'chromium', },
},
{
name: 'Desktop Firefox',
use: { browserName: 'firefox', },
},
{
name: 'Desktop Safari',
use: { browserName: 'webkit', },
}
],
};
export default config;
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: [
process.env.CI ? ['dot'] : ['list'],
['json', { outputFile: 'test-results.json' }],
],
projects: [
{
name: 'Desktop Chrome',
use: { browserName: 'chromium', },
},
{
name: 'Desktop Firefox',
use: { browserName: 'firefox', },
},
{
name: 'Desktop Safari',
use: { browserName: 'webkit', },
}
],
};
export default config;
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: [
process.env.CI ? ['dot'] : ['list'],
['json', { outputFile: 'test-results.json' }],
],
projects: [
{
name: 'Desktop Chrome',
use: { browserName: 'chromium', },
},
{
name: 'Desktop Firefox',
use: { browserName: 'firefox', },
},
{
name: 'Desktop Safari',
use: { browserName: 'webkit', },
}
],
};
export default config;
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: [
process.env.CI ? ['dot'] : ['list'],
['json', { outputFile: 'test-results.json' }],
],
projects: [
{
name: 'Desktop Chrome',
use: devices['Desktop Chrome'],
},
{
name: 'Desktop Firefox',
use: { browserName: 'firefox', },
},
{
name: 'Desktop Safari',
use: { browserName: 'webkit', },
}
],
};
export default config;
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: [
process.env.CI ? ['dot'] : ['list'],
['json', { outputFile: 'test-results.json' }],
],
projects: [
{
name: 'Desktop Chrome',
use: devices['Desktop Chrome'],
},
{
name: 'Desktop Firefox',
use: { browserName: 'firefox', },
},
{
name: 'Desktop Safari',
use: { browserName: 'webkit', },
}
],
};
export default config;
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: [
process.env.CI ? ['dot'] : ['list'],
['json', { outputFile: 'test-results.json' }],
],
projects: [
{
name: 'Desktop Chrome',
use: devices['Desktop Chrome'],
},
{
name: 'Desktop Firefox',
use: devices['Desktop Firefox'],
},
{
name: 'Desktop Safari',
use: { browserName: 'webkit', },
}
],
};
export default config;
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: [
process.env.CI ? ['dot'] : ['list'],
['json', { outputFile: 'test-results.json' }],
],
projects: [
{
name: 'Desktop Chrome',
use: devices['Desktop Chrome'],
},
{
name: 'Desktop Firefox',
use: devices['Desktop Firefox'],
},
{
name: 'Desktop Safari',
use: { browserName: 'webkit', },
}
],
};
export default config;
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: [
process.env.CI ? ['dot'] : ['list'],
['json', { outputFile: 'test-results.json' }],
],
projects: [
{
name: 'Desktop Chrome',
use: devices['Desktop Chrome'],
},
{
name: 'Desktop Firefox',
use: devices['Desktop Firefox'],
},
{
name: 'Mobile Safari',
use: { browserName: 'webkit', },
}
],
};
export default config;
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: [
process.env.CI ? ['dot'] : ['list'],
['json', { outputFile: 'test-results.json' }],
],
projects: [
{
name: 'Desktop Chrome',
use: devices['Desktop Chrome'],
},
{
name: 'Desktop Firefox',
use: devices['Desktop Firefox'],
},
{
name: 'Mobile Safari',
use: devices['iPhone 12 Pro'],
}
],
};
export default config;
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: [
process.env.CI ? ['dot'] : ['list'],
['json', { outputFile: 'test-results.json' }],
],
projects: [
{
name: 'Desktop Chrome',
use: devices['Desktop Chrome'],
},
{
name: 'Desktop Firefox',
use: devices['Desktop Firefox'],
},
{
name: 'Mobile Safari',
use: devices['iPhone 12 Pro'],
}
],
};
export default config;
Playwright
Chapter 4 Inspector & Codegen
Demo: Inspector & CodeGen
Playwright Inspector
Playwright Code Generation
Control Panel
Playwright Inspector
Source Code
Selectors Playground
Actions
Post-mortem Debugging
Chapter 5 Playwright Tracing
Post-Mortem Debugging
videos screenshots
Playwright
Tracing
Playwright Tracing
Playwright Tracing
trace.zip files
● Playwright actions
● Playwright events
● Screencast
● Network log
● Console log
● DOM snapshots 🔥
Playwright Tracing
Ty
const config: PlaywrightTestConfig = {
p
eS
reporter: process.env.CI ? 'dot' : 'line',
cr
ip
t
projects: [{
name: 'Desktop Chrome',
use: devices['Desktop Chrome'],
}, {
name: 'Desktop Firefox',
use: devices['Desktop Firefox'],
}, {
name: 'Mobile Safari',
use: devices['iPhone 12 Pro'],
}],
};
export default config;
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
Ty
const config: PlaywrightTestConfig = {
p
eS
reporter: process.env.CI ? 'dot' : 'line',
cr
ip
retries: 2,
t
projects: [{
name: 'Desktop Chrome',
use: devices['Desktop Chrome'],
}, {
name: 'Desktop Firefox',
use: devices['Desktop Firefox'],
}, {
name: 'Mobile Safari',
use: devices['iPhone 12 Pro'],
}],
};
export default config;
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
Ty
const config: PlaywrightTestConfig = {
p
eS
reporter: process.env.CI ? 'dot' : 'line',
cr
ip
retries: 2,
t
use: {
trace: 'on-first-retry',
},
projects: [{
name: 'Desktop Chrome',
use: devices['Desktop Chrome'],
}, {
name: 'Desktop Firefox',
use: devices['Desktop Firefox'],
}, {
name: 'Mobile Safari',
use: devices['iPhone 12 Pro'],
}],
};
export default config;
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
Ty
const config: PlaywrightTestConfig = {
p
eS
reporter: process.env.CI ? 'dot' : 'line',
cr
ip
retries: 2,
t
use: {
trace: 'on-first-retry',
},
projects: [{
name: 'Desktop Chrome',
Enabling Trace Collection
use: devices['Desktop Chrome'],
}, {
name: 'Desktop Firefox',
use: devices['Desktop Firefox'],
}, {
name: 'Mobile Safari',
use: devices['iPhone 12 Pro'],
}],
};
export default config;
# .github/workflows/tests.yml
on: [push]
jobs:
G
ith
ub
run_tests:
Ac
runs-on: ubuntu-latest
tio
ns
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run test:e2e
# .github/workflows/tests.yml
on: [push]
jobs:
G
ith
ub
run_tests:
Ac
runs-on: ubuntu-latest
tio
ns
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- run: npm ci
- run: npx playwright install --with-deps Uploading Artifacts
- run: npm run test:e2e
- uses: actions/upload-artifact@v2
if: always()
with:
name: test-results
path: test-results
# .github/workflows/tests.yml
on: [push]
jobs:
G
ith
ub
run_tests:
Ac
runs-on: ubuntu-latest
tio
ns
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- run: npm ci
- run: npx playwright install --with-deps Uploading Artifacts
- run: npm run test:e2e
- uses: actions/upload-artifact@v2
if: always()
with:
name: test-results
path: test-results ← default folder with all artifacts
Demo: Playwright Trace Viewer
Opening Playwright Trace Viewer
Timeline
Action Details
Actions List
DOM Snapshot 🔥
Playwright Test: Playful Testing Framework
@aslushnikov @playwrightweb
aslushnikov@gmail.com https://aka.ms/playwright-slack
microsoft/playwright