SlideShare a Scribd company logo
Playwright Test
Playful testing framework
● Cross-browser Web Testing Framework
● Node.js: JavaScript / TypeScript
● Free, Open Source, Sponsored by Microsoft
● Extensively used in the industry
What is Playwright Test?
● Historically, JavaScript test frameworks are built for unit tests
● Playwright Test is built for end-to-end tests:
○ Cross-browser — Chrome, Firefox & Safari
○ Parallelisation — tests are fast
○ Isolation — zero-overhead test isolation
○ Flexibility — pytest-like fixture configuration
Why yet another test runner?
Agenda
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
2. Auto-waiting 3. Web-First Assertions
Chapter 2 Fundamentals
❌ Old-School: Browser Restart
● Slow instantiation (>100ms)
● Huge memory overhead
✅ Playwright Test: Browser Contexts
● Full isolation
● Fast instantiation (~1ms)
● Low overhead Browser Context
Fundamentals: Test Isolation
❌ Old-School: timeouts to await elements
● Time does not exist in the cloud
● Timeouts are inefficient
✅ Playwright Test: built-in auto-waiting
● Just Works!
● Happens for all actions (e.g. click, fill, press)
● No need for `setTimeout` calls
Loading...
Fundamentals: Auto-waiting
Fundamentals: Auto-waiting ✨
Fundamentals: Auto-waiting ✨
Fundamentals: Auto-waiting ✨
Fundamentals: Auto-waiting ✨
Fundamentals: Auto-waiting ✨
Fundamentals: Auto-waiting ✨
Fundamentals: Auto-waiting ✨
❌ Old-School: assert current state
● Web Applications are highly dynamic
● State is always in flux
✅ Playwright Test: declare expected state
● Wait until the declared state is reached
● Web-First assertions
Fundamentals: Web-First Assertions
expect(locator).toHaveClass(expected)
expect(locator).toHaveCount(count)
expect(locator).toHaveCSS(name, value)
expect(locator).toHaveId(id)
expect(locator).toHaveJSProperty(name, value)
expect(locator).toHaveText(expected)
expect(page).toHaveTitle(title)
expect(page).toHaveURL(url)
expect(locator).toHaveValue(value)
expect(locator).toBeChecked()
expect(locator).toBeDisabled()
expect(locator).toBeEditable()
expect(locator).toBeEmpty()
expect(locator).toBeEnabled()
expect(locator).toBeFocused()
expect(locator).toBeHidden()
expect(locator).toBeVisible()
expect(locator).toContainText(text)
expect(locator).toHaveAttribute(name)
Fundamentals: Web-First Assertions
📍 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
await expect(page.locator('.products .item')).toHaveText(['soap', 'rope']);
Fundamentals: Web-First Assertions
await expect(page.locator('.products .item')).toHaveText(['soap', 'rope']);
1. Must be awaited
Fundamentals: Web-First Assertions
await expect(page.locator('.products .item')).toHaveText(['soap', 'rope']);
1. Must be awaited
2. Re-queries given locator
Fundamentals: Web-First Assertions
await expect(page.locator('.products .item')).toHaveText(['soap', 'rope']);
1. Must be awaited
2. Re-queries given locator
3. Waiting until it has two elements
with given texts
Fundamentals: Web-First Assertions
Chapter 3 Configuration
// example.spec.ts
import { test, expect } from '@playwright/test';
test('basic test', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.locator('text=Get started').click();
await expect(page).toHaveTitle(/Getting started/);
});
// example.spec.ts
import { test, expect } from '@playwright/test';
test('basic test', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.locator('text=Get started').click();
await expect(page).toHaveTitle(/Getting started/);
});
// example.spec.ts
import { test, expect } from '@playwright/test';
test('basic test', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.locator('text=Get started').click();
await expect(page).toHaveTitle(/Getting started/);
});
// playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
projects: [
],
};
export default config;
// example.spec.ts
import { test, expect } from '@playwright/test';
test('basic test', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.locator('text=Get started').click();
await expect(page).toHaveTitle(/Getting started/);
});
// playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
projects: [
{
name: 'Desktop Chrome',
use: { browserName: 'chromium', },
},
],
};
export default config;
// example.spec.ts
import { test, expect } from '@playwright/test';
test('basic test', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.locator('text=Get started').click();
await expect(page).toHaveTitle(/Getting started/);
});
// playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
projects: [
{
name: 'Desktop Chrome',
use: { browserName: 'chromium', },
},
],
};
export default config;
Run 1
// 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;
// example.spec.ts
import { test, expect } from '@playwright/test';
test('basic test', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.locator('text=Get started').click();
await expect(page).toHaveTitle(/Getting started/);
});
Run 1
R
u
n
2
R
u
n
3
// 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;
// example.spec.ts
import { test, expect } from '@playwright/test';
test('basic test', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.locator('text=Get started').click();
await expect(page).toHaveTitle(/Getting started/);
});
Run 1
R
u
n
2
R
u
n
3
Granular Configuration: france.spec.ts
● Per-file configuration
● Per-suite configuration
// france.spec.ts
import { test, expect } from '@playwright/test';
T
y
p
e
S
c
r
i
p
t
// france.spec.ts
import { test, expect } from '@playwright/test';
// per-file configuration
test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' });
T
y
p
e
S
c
r
i
p
t
// france.spec.ts
import { test, expect } from '@playwright/test';
// per-file configuration
test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' });
test('should work', async ({ page }) => { /* ... test goes here ... */ });
test('should use euro', async ({ page }) => { /* ... */ });
T
y
p
e
S
c
r
i
p
t
// france.spec.ts
import { test, expect } from '@playwright/test';
// per-file configuration
test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' });
test('should work', async ({ page }) => { /* ... test goes here ... */ });
test('should use euro', async ({ page }) => { /* ... */ });
test.describe('light theme', () => {
});
T
y
p
e
S
c
r
i
p
t
// france.spec.ts
import { test, expect } from '@playwright/test';
// per-file configuration
test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' });
test('should work', async ({ page }) => { /* ... test goes here ... */ });
test('should use euro', async ({ page }) => { /* ... */ });
test.describe('light theme', () => {
test.use({ colorScheme: 'light' }); // per-suite configuration
});
T
y
p
e
S
c
r
i
p
t
// france.spec.ts
import { test, expect } from '@playwright/test';
// per-file configuration
test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' });
test('should work', async ({ page }) => { /* ... test goes here ... */ });
test('should use euro', async ({ page }) => { /* ... */ });
test.describe('light theme', () => {
test.use({ colorScheme: 'light' }); // per-suite configuration
test('should be light', async ({ page }) => { /* ... */ });
});
T
y
p
e
S
c
r
i
p
t
// france.spec.ts
import { test, expect } from '@playwright/test';
// per-file configuration
test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' });
test('should work', async ({ page }) => { /* ... test goes here ... */ });
test('should use euro', async ({ page }) => { /* ... */ });
test.describe('light theme', () => {
test.use({ colorScheme: 'light' }); // per-suite configuration
test('should be light', async ({ page }) => { /* ... */ });
});
test.describe('dark theme', () => {
test.use({ colorScheme: 'dark' }); // per-suite configuration
test('should be dark', async ({ page }) => { /* ... */ });
});
T
y
p
e
S
c
r
i
p
t
● javaScriptEnabled
● launchOptions
● locale
● offline
● permissions
● proxy
● screenshot
● storageState
● timezoneId
● trace
● userAgent
● video
● viewport
● acceptDownloads
● baseURL
● browserName
● bypassCSP
● channel
● colorScheme
● deviceScaleFactor
● extraHTPHeaders
● geolocation
● hasTouch
● headless
● httpCredentials
● ignoreHTTPSErrors
Configuration Options
https:/
/aka.ms/playwright/fixtures
Configuration: Data-Driven Tests
Configuration: Data-Driven Tests
Configuration: Data-Driven Tests
// check-urls.spec.ts
import { test, expect } from '@playwright/test';
T
y
p
e
S
c
r
i
p
t
// check-urls.spec.ts
import { test, expect } from '@playwright/test';
const urls = require('./urls.json');
T
y
p
e
S
c
r
i
p
t
// check-urls.spec.ts
import { test, expect } from '@playwright/test';
const urls = require('./urls.json');
for (const url of urls) {
}
T
y
p
e
S
c
r
i
p
t
// check-urls.spec.ts
import { test, expect } from '@playwright/test';
const urls = require('./urls.json');
for (const url of urls) {
test(`check ${url}`, async ({ page }) => {
await page.goto(url);
});
}
T
y
p
e
S
c
r
i
p
t
// check-urls.spec.ts
import { test, expect } from '@playwright/test';
const urls = require('./urls.json');
for (const url of urls) {
test(`check ${url}`, async ({ page }) => {
await page.goto(url);
});
}
T
y
p
e
S
c
r
i
p
t
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' }],
],
projects: [
{
name: 'Desktop Chrome',
use: { browserName: 'chromium', },
},
{
name: 'Desktop Firefox',
use: { browserName: 'firefox', },
},
{
name: 'Desktop Safari',
use: { browserName: 'webkit', },
}
],
};
export default config;
Built-in reporters
➔ dot
➔ list
➔ line
➔ json
➔ junit
Third-party reporters
➔ allure-playwright
https://aka.ms/playwright/reporters
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;
Chapter 4 Playwright
Inspector & Codegen
Demo: Inspector & CodeGen
Playwright Inspector
Playwright Code Generation
Playwright
Inspector
Control Panel
Actions
Selectors Playground
Source Code
Chapter 5 Post-mortem Debugging
Playwright Tracing
Post-Mortem Debugging
● Post-Mortem Debugging – debugging test failures on CI without being
able to debug locally.
● Test Artifacts – any by-product of test running that helps debug test
failures.
○ Logs
○ Screenshots
○ Videos
Unique Artifact: Tracing
screenshots
Playwright
Tracing
videos
Playwright Tracing
● Playwright actions
● Playwright events
● Screencast
● Network log
● Console log
● DOM snapshots 🔥
Playwright Tracing
trace.zip files
● Playwright actions
● Playwright events
● Screencast
● Network log
● Console log
● DOM snapshots 🔥
Playwright Tracing
● GUI tool to explore trace
files
● Bundled with Playwright
trace.zip files Trace Viewer
Playwright Tracing: Workflow
Enable trace collection in playwright.config.ts
Setup CI to upload trace files
Download & Inspect trace files with Playwright Trace Viewer
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: process.env.CI ? 'dot' : 'line',
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;
T
y
p
e
S
c
r
i
p
t
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: process.env.CI ? 'dot' : 'line',
retries: 2,
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;
T
y
p
e
S
c
r
i
p
t
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: process.env.CI ? 'dot' : 'line',
retries: 2,
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;
T
y
p
e
S
c
r
i
p
t
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config: PlaywrightTestConfig = {
reporter: process.env.CI ? 'dot' : 'line',
retries: 2,
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;
Enabling Trace Collection
T
y
p
e
S
c
r
i
p
t
# .github/workflows/tests.yml
on: [push]
jobs:
run_tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run test:e2e
G
i
t
h
u
b
A
c
t
i
o
n
s
# .github/workflows/tests.yml
on: [push]
jobs:
run_tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run test:e2e
- uses: actions/upload-artifact@v2
if: always()
with:
name: test-results
path: test-results
G
i
t
h
u
b
A
c
t
i
o
n
s
Uploading Artifacts
# .github/workflows/tests.yml
on: [push]
jobs:
run_tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run test:e2e
- uses: actions/upload-artifact@v2
if: always()
with:
name: test-results
path: test-results
G
i
t
h
u
b
A
c
t
i
o
n
s
Uploading Artifacts
← default folder with all artifacts
Demo: Playwright Trace Viewer
Opening Playwright Trace Viewer
Timeline
Actions
List
Action
Details
DOM Snapshot 🔥
Playwright Test: Playful Testing Framework
1. Get started with npm init playwright
2. Configure everything at playwright.config.ts
3. Test iPhone, customize reporters, generate tests
4. Debug tests with Playwright Inspector
5. Author tests with Playwright CodeGen
6. Post-mortem with Playwright Tracing
Playwright
● Cross-browser Web Testing and Automation Framework
● Documentation: https:/
/playwright.dev
● Source / Issues: https:/
/github.com/microsoft/playwright
● Social:
○ https:/
/aka.ms/playwright/slack
○ https:/
/aka.ms/playwright/twitter
○ https:/
/aka.ms/playwright/youtube
Andrey Lushnikov
@playwrightweb
Q
u
e
s
t
i
o
n
s
?
Playwright Test
https:/
/aka.ms/playwright-slack
microsoft/playwright
@aslushnikov
aslushnikov@gmail.com

More Related Content

Similar to Playwright Test automation frameworktest (20)

PDF
ITB 2023 cbPlaywright End-to-end Tests with Playwright and TestBox.pdf
Ortus Solutions, Corp
 
KEY
Node.js basics
Ben Lin
 
ODP
RichFaces - Testing on Mobile Devices
Pavol Pitoňák
 
PDF
A re introduction to webpack - reactfoo - mumbai
Praveen Puglia
 
PDF
Nodejs in Production
William Bruno Moraes
 
PDF
Why Every Tester Should Learn Ruby
Raimonds Simanovskis
 
PPT
Demystifying Maven
Mike Desjardins
 
PPTX
Taking Jenkins Pipeline to the Extreme
yinonavraham
 
PPT
Beyond Unit Testing
Steve Loughran
 
PPTX
Das kannste schon so machen
André Goliath
 
PDF
Getting started with puppet and vagrant (1)
Puppet
 
PDF
20160905 - BrisJS - nightwatch testing
Vladimir Roudakov
 
PDF
TypeScript for Java Developers
Yakov Fain
 
PPTX
A few good JavaScript development tools
Simon Kim
 
PDF
Writing and Publishing Puppet Modules - PuppetConf 2014
Puppet
 
PDF
Groovy for java developers
Puneet Behl
 
PDF
Orchestrated Functional Testing with Puppet-spec and Mspectator - PuppetConf ...
Puppet
 
PDF
Orchestrated Functional Testing with Puppet-spec and Mspectator
Raphaël PINSON
 
PDF
Construire une application JavaFX 8 avec gradle
Thierry Wasylczenko
 
PDF
Node.js vs Play Framework (with Japanese subtitles)
Yevgeniy Brikman
 
ITB 2023 cbPlaywright End-to-end Tests with Playwright and TestBox.pdf
Ortus Solutions, Corp
 
Node.js basics
Ben Lin
 
RichFaces - Testing on Mobile Devices
Pavol Pitoňák
 
A re introduction to webpack - reactfoo - mumbai
Praveen Puglia
 
Nodejs in Production
William Bruno Moraes
 
Why Every Tester Should Learn Ruby
Raimonds Simanovskis
 
Demystifying Maven
Mike Desjardins
 
Taking Jenkins Pipeline to the Extreme
yinonavraham
 
Beyond Unit Testing
Steve Loughran
 
Das kannste schon so machen
André Goliath
 
Getting started with puppet and vagrant (1)
Puppet
 
20160905 - BrisJS - nightwatch testing
Vladimir Roudakov
 
TypeScript for Java Developers
Yakov Fain
 
A few good JavaScript development tools
Simon Kim
 
Writing and Publishing Puppet Modules - PuppetConf 2014
Puppet
 
Groovy for java developers
Puneet Behl
 
Orchestrated Functional Testing with Puppet-spec and Mspectator - PuppetConf ...
Puppet
 
Orchestrated Functional Testing with Puppet-spec and Mspectator
Raphaël PINSON
 
Construire une application JavaFX 8 avec gradle
Thierry Wasylczenko
 
Node.js vs Play Framework (with Japanese subtitles)
Yevgeniy Brikman
 

Recently uploaded (20)

PDF
NLJUG Speaker academy 2025 - first session
Bert Jan Schrijver
 
DOCX
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
PPTX
Mastering ODC + Okta Configuration - Chennai OSUG
HathiMaryA
 
PDF
“Computer Vision at Sea: Automated Fish Tracking for Sustainable Fishing,” a ...
Edge AI and Vision Alliance
 
PDF
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
PDF
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
PDF
UiPath DevConnect 2025: Agentic Automation Community User Group Meeting
DianaGray10
 
PDF
Go Concurrency Real-World Patterns, Pitfalls, and Playground Battles.pdf
Emily Achieng
 
PPT
Ericsson LTE presentation SEMINAR 2010.ppt
npat3
 
PDF
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
DOCX
Python coding for beginners !! Start now!#
Rajni Bhardwaj Grover
 
PDF
AI Agents in the Cloud: The Rise of Agentic Cloud Architecture
Lilly Gracia
 
PDF
Future-Proof or Fall Behind? 10 Tech Trends You Can’t Afford to Ignore in 2025
DIGITALCONFEX
 
PDF
NASA A Researcher’s Guide to International Space Station : Physical Sciences ...
Dr. PANKAJ DHUSSA
 
PDF
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
PDF
“Voice Interfaces on a Budget: Building Real-time Speech Recognition on Low-c...
Edge AI and Vision Alliance
 
PPTX
AI Penetration Testing Essentials: A Cybersecurity Guide for 2025
defencerabbit Team
 
PDF
What’s my job again? Slides from Mark Simos talk at 2025 Tampa BSides
Mark Simos
 
PDF
“NPU IP Hardware Shaped Through Software and Use-case Analysis,” a Presentati...
Edge AI and Vision Alliance
 
PPTX
New ThousandEyes Product Innovations: Cisco Live June 2025
ThousandEyes
 
NLJUG Speaker academy 2025 - first session
Bert Jan Schrijver
 
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
Mastering ODC + Okta Configuration - Chennai OSUG
HathiMaryA
 
“Computer Vision at Sea: Automated Fish Tracking for Sustainable Fishing,” a ...
Edge AI and Vision Alliance
 
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
UiPath DevConnect 2025: Agentic Automation Community User Group Meeting
DianaGray10
 
Go Concurrency Real-World Patterns, Pitfalls, and Playground Battles.pdf
Emily Achieng
 
Ericsson LTE presentation SEMINAR 2010.ppt
npat3
 
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
Python coding for beginners !! Start now!#
Rajni Bhardwaj Grover
 
AI Agents in the Cloud: The Rise of Agentic Cloud Architecture
Lilly Gracia
 
Future-Proof or Fall Behind? 10 Tech Trends You Can’t Afford to Ignore in 2025
DIGITALCONFEX
 
NASA A Researcher’s Guide to International Space Station : Physical Sciences ...
Dr. PANKAJ DHUSSA
 
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
“Voice Interfaces on a Budget: Building Real-time Speech Recognition on Low-c...
Edge AI and Vision Alliance
 
AI Penetration Testing Essentials: A Cybersecurity Guide for 2025
defencerabbit Team
 
What’s my job again? Slides from Mark Simos talk at 2025 Tampa BSides
Mark Simos
 
“NPU IP Hardware Shaped Through Software and Use-case Analysis,” a Presentati...
Edge AI and Vision Alliance
 
New ThousandEyes Product Innovations: Cisco Live June 2025
ThousandEyes
 
Ad

Playwright Test automation frameworktest

  • 2. ● Cross-browser Web Testing Framework ● Node.js: JavaScript / TypeScript ● Free, Open Source, Sponsored by Microsoft ● Extensively used in the industry What is Playwright Test?
  • 3. ● Historically, JavaScript test frameworks are built for unit tests ● Playwright Test is built for end-to-end tests: ○ Cross-browser — Chrome, Firefox & Safari ○ Parallelisation — tests are fast ○ Isolation — zero-overhead test isolation ○ Flexibility — pytest-like fixture configuration Why yet another test runner?
  • 4. Agenda 1. Getting Started 2. Fundamentals 3. Configuration 4. Playwright Inspector & CodeGen 5. Playwright Tracing
  • 11. Test: e2e/example.spec.ts 1. Test Isolation 2. Auto-waiting
  • 12. Test: e2e/example.spec.ts 1. Test Isolation 2. Auto-waiting 3. Web-First Assertions
  • 14. ❌ Old-School: Browser Restart ● Slow instantiation (>100ms) ● Huge memory overhead ✅ Playwright Test: Browser Contexts ● Full isolation ● Fast instantiation (~1ms) ● Low overhead Browser Context Fundamentals: Test Isolation
  • 15. ❌ Old-School: timeouts to await elements ● Time does not exist in the cloud ● Timeouts are inefficient ✅ Playwright Test: built-in auto-waiting ● Just Works! ● Happens for all actions (e.g. click, fill, press) ● No need for `setTimeout` calls Loading... Fundamentals: Auto-waiting
  • 23. ❌ Old-School: assert current state ● Web Applications are highly dynamic ● State is always in flux ✅ Playwright Test: declare expected state ● Wait until the declared state is reached ● Web-First assertions Fundamentals: Web-First Assertions
  • 25. 📍 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
  • 26. await expect(page.locator('.products .item')).toHaveText(['soap', 'rope']); Fundamentals: Web-First Assertions
  • 27. await expect(page.locator('.products .item')).toHaveText(['soap', 'rope']); 1. Must be awaited Fundamentals: Web-First Assertions
  • 28. await expect(page.locator('.products .item')).toHaveText(['soap', 'rope']); 1. Must be awaited 2. Re-queries given locator Fundamentals: Web-First Assertions
  • 29. await expect(page.locator('.products .item')).toHaveText(['soap', 'rope']); 1. Must be awaited 2. Re-queries given locator 3. Waiting until it has two elements with given texts Fundamentals: Web-First Assertions
  • 31. // example.spec.ts import { test, expect } from '@playwright/test'; test('basic test', async ({ page }) => { await page.goto('https://playwright.dev/'); await page.locator('text=Get started').click(); await expect(page).toHaveTitle(/Getting started/); });
  • 32. // example.spec.ts import { test, expect } from '@playwright/test'; test('basic test', async ({ page }) => { await page.goto('https://playwright.dev/'); await page.locator('text=Get started').click(); await expect(page).toHaveTitle(/Getting started/); });
  • 33. // example.spec.ts import { test, expect } from '@playwright/test'; test('basic test', async ({ page }) => { await page.goto('https://playwright.dev/'); await page.locator('text=Get started').click(); await expect(page).toHaveTitle(/Getting started/); }); // playwright.config.ts import { PlaywrightTestConfig } from '@playwright/test'; const config: PlaywrightTestConfig = { projects: [ ], }; export default config;
  • 34. // example.spec.ts import { test, expect } from '@playwright/test'; test('basic test', async ({ page }) => { await page.goto('https://playwright.dev/'); await page.locator('text=Get started').click(); await expect(page).toHaveTitle(/Getting started/); }); // playwright.config.ts import { PlaywrightTestConfig } from '@playwright/test'; const config: PlaywrightTestConfig = { projects: [ { name: 'Desktop Chrome', use: { browserName: 'chromium', }, }, ], }; export default config;
  • 35. // example.spec.ts import { test, expect } from '@playwright/test'; test('basic test', async ({ page }) => { await page.goto('https://playwright.dev/'); await page.locator('text=Get started').click(); await expect(page).toHaveTitle(/Getting started/); }); // playwright.config.ts import { PlaywrightTestConfig } from '@playwright/test'; const config: PlaywrightTestConfig = { projects: [ { name: 'Desktop Chrome', use: { browserName: 'chromium', }, }, ], }; export default config; Run 1
  • 36. // 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; // example.spec.ts import { test, expect } from '@playwright/test'; test('basic test', async ({ page }) => { await page.goto('https://playwright.dev/'); await page.locator('text=Get started').click(); await expect(page).toHaveTitle(/Getting started/); }); Run 1 R u n 2 R u n 3
  • 37. // 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; // example.spec.ts import { test, expect } from '@playwright/test'; test('basic test', async ({ page }) => { await page.goto('https://playwright.dev/'); await page.locator('text=Get started').click(); await expect(page).toHaveTitle(/Getting started/); }); Run 1 R u n 2 R u n 3
  • 38. Granular Configuration: france.spec.ts ● Per-file configuration ● Per-suite configuration
  • 39. // france.spec.ts import { test, expect } from '@playwright/test'; T y p e S c r i p t
  • 40. // france.spec.ts import { test, expect } from '@playwright/test'; // per-file configuration test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' }); T y p e S c r i p t
  • 41. // france.spec.ts import { test, expect } from '@playwright/test'; // per-file configuration test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' }); test('should work', async ({ page }) => { /* ... test goes here ... */ }); test('should use euro', async ({ page }) => { /* ... */ }); T y p e S c r i p t
  • 42. // france.spec.ts import { test, expect } from '@playwright/test'; // per-file configuration test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' }); test('should work', async ({ page }) => { /* ... test goes here ... */ }); test('should use euro', async ({ page }) => { /* ... */ }); test.describe('light theme', () => { }); T y p e S c r i p t
  • 43. // france.spec.ts import { test, expect } from '@playwright/test'; // per-file configuration test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' }); test('should work', async ({ page }) => { /* ... test goes here ... */ }); test('should use euro', async ({ page }) => { /* ... */ }); test.describe('light theme', () => { test.use({ colorScheme: 'light' }); // per-suite configuration }); T y p e S c r i p t
  • 44. // france.spec.ts import { test, expect } from '@playwright/test'; // per-file configuration test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' }); test('should work', async ({ page }) => { /* ... test goes here ... */ }); test('should use euro', async ({ page }) => { /* ... */ }); test.describe('light theme', () => { test.use({ colorScheme: 'light' }); // per-suite configuration test('should be light', async ({ page }) => { /* ... */ }); }); T y p e S c r i p t
  • 45. // france.spec.ts import { test, expect } from '@playwright/test'; // per-file configuration test.use({ locale: 'fr-FR', timezoneId: 'Europe/Paris' }); test('should work', async ({ page }) => { /* ... test goes here ... */ }); test('should use euro', async ({ page }) => { /* ... */ }); test.describe('light theme', () => { test.use({ colorScheme: 'light' }); // per-suite configuration test('should be light', async ({ page }) => { /* ... */ }); }); test.describe('dark theme', () => { test.use({ colorScheme: 'dark' }); // per-suite configuration test('should be dark', async ({ page }) => { /* ... */ }); }); T y p e S c r i p t
  • 46. ● javaScriptEnabled ● launchOptions ● locale ● offline ● permissions ● proxy ● screenshot ● storageState ● timezoneId ● trace ● userAgent ● video ● viewport ● acceptDownloads ● baseURL ● browserName ● bypassCSP ● channel ● colorScheme ● deviceScaleFactor ● extraHTPHeaders ● geolocation ● hasTouch ● headless ● httpCredentials ● ignoreHTTPSErrors Configuration Options https:/ /aka.ms/playwright/fixtures
  • 50. // check-urls.spec.ts import { test, expect } from '@playwright/test'; T y p e S c r i p t
  • 51. // check-urls.spec.ts import { test, expect } from '@playwright/test'; const urls = require('./urls.json'); T y p e S c r i p t
  • 52. // check-urls.spec.ts import { test, expect } from '@playwright/test'; const urls = require('./urls.json'); for (const url of urls) { } T y p e S c r i p t
  • 53. // check-urls.spec.ts import { test, expect } from '@playwright/test'; const urls = require('./urls.json'); for (const url of urls) { test(`check ${url}`, async ({ page }) => { await page.goto(url); }); } T y p e S c r i p t
  • 54. // check-urls.spec.ts import { test, expect } from '@playwright/test'; const urls = require('./urls.json'); for (const url of urls) { test(`check ${url}`, async ({ page }) => { await page.goto(url); }); } T y p e S c r i p t NOTE: Make sure to have different test titles
  • 57. // 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;
  • 58. // 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;
  • 59. // 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;
  • 60. // 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;
  • 61. // 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; Built-in reporters ➔ dot ➔ list ➔ line ➔ json ➔ junit Third-party reporters ➔ allure-playwright https://aka.ms/playwright/reporters
  • 63. // 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;
  • 64. // 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;
  • 65. // 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;
  • 66. // 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;
  • 67. // 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;
  • 68. // 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;
  • 69. // 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;
  • 70. // 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;
  • 71. // 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;
  • 72. // 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;
  • 74. Demo: Inspector & CodeGen
  • 78. Chapter 5 Post-mortem Debugging Playwright Tracing
  • 79. Post-Mortem Debugging ● Post-Mortem Debugging – debugging test failures on CI without being able to debug locally. ● Test Artifacts – any by-product of test running that helps debug test failures. ○ Logs ○ Screenshots ○ Videos
  • 82. ● Playwright actions ● Playwright events ● Screencast ● Network log ● Console log ● DOM snapshots 🔥 Playwright Tracing trace.zip files
  • 83. ● Playwright actions ● Playwright events ● Screencast ● Network log ● Console log ● DOM snapshots 🔥 Playwright Tracing ● GUI tool to explore trace files ● Bundled with Playwright trace.zip files Trace Viewer
  • 84. Playwright Tracing: Workflow Enable trace collection in playwright.config.ts Setup CI to upload trace files Download & Inspect trace files with Playwright Trace Viewer
  • 85. // playwright.config.ts import { PlaywrightTestConfig, devices } from '@playwright/test'; const config: PlaywrightTestConfig = { reporter: process.env.CI ? 'dot' : 'line', 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; T y p e S c r i p t
  • 86. // playwright.config.ts import { PlaywrightTestConfig, devices } from '@playwright/test'; const config: PlaywrightTestConfig = { reporter: process.env.CI ? 'dot' : 'line', retries: 2, 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; T y p e S c r i p t
  • 87. // playwright.config.ts import { PlaywrightTestConfig, devices } from '@playwright/test'; const config: PlaywrightTestConfig = { reporter: process.env.CI ? 'dot' : 'line', retries: 2, 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; T y p e S c r i p t
  • 88. // playwright.config.ts import { PlaywrightTestConfig, devices } from '@playwright/test'; const config: PlaywrightTestConfig = { reporter: process.env.CI ? 'dot' : 'line', retries: 2, 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; Enabling Trace Collection T y p e S c r i p t
  • 89. # .github/workflows/tests.yml on: [push] jobs: run_tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 - run: npm ci - run: npx playwright install --with-deps - run: npm run test:e2e G i t h u b A c t i o n s
  • 90. # .github/workflows/tests.yml on: [push] jobs: run_tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 - run: npm ci - run: npx playwright install --with-deps - run: npm run test:e2e - uses: actions/upload-artifact@v2 if: always() with: name: test-results path: test-results G i t h u b A c t i o n s Uploading Artifacts
  • 91. # .github/workflows/tests.yml on: [push] jobs: run_tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 - run: npm ci - run: npx playwright install --with-deps - run: npm run test:e2e - uses: actions/upload-artifact@v2 if: always() with: name: test-results path: test-results G i t h u b A c t i o n s Uploading Artifacts ← default folder with all artifacts
  • 95. Playwright Test: Playful Testing Framework 1. Get started with npm init playwright 2. Configure everything at playwright.config.ts 3. Test iPhone, customize reporters, generate tests 4. Debug tests with Playwright Inspector 5. Author tests with Playwright CodeGen 6. Post-mortem with Playwright Tracing
  • 96. Playwright ● Cross-browser Web Testing and Automation Framework ● Documentation: https:/ /playwright.dev ● Source / Issues: https:/ /github.com/microsoft/playwright ● Social: ○ https:/ /aka.ms/playwright/slack ○ https:/ /aka.ms/playwright/twitter ○ https:/ /aka.ms/playwright/youtube