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
Ad

More Related Content

Similar to Playwright Test automation frameworktest (20)

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

Recently uploaded (20)

Buckeye Dreamin 2024: Assessing and Resolving Technical Debt
Buckeye Dreamin 2024: Assessing and Resolving Technical DebtBuckeye Dreamin 2024: Assessing and Resolving Technical Debt
Buckeye Dreamin 2024: Assessing and Resolving Technical Debt
Lynda Kane
 
AI EngineHost Review: Revolutionary USA Datacenter-Based Hosting with NVIDIA ...
AI EngineHost Review: Revolutionary USA Datacenter-Based Hosting with NVIDIA ...AI EngineHost Review: Revolutionary USA Datacenter-Based Hosting with NVIDIA ...
AI EngineHost Review: Revolutionary USA Datacenter-Based Hosting with NVIDIA ...
SOFTTECHHUB
 
Semantic Cultivators : The Critical Future Role to Enable AI
Semantic Cultivators : The Critical Future Role to Enable AISemantic Cultivators : The Critical Future Role to Enable AI
Semantic Cultivators : The Critical Future Role to Enable AI
artmondano
 
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
Alan Dix
 
2025-05-Q4-2024-Investor-Presentation.pptx
2025-05-Q4-2024-Investor-Presentation.pptx2025-05-Q4-2024-Investor-Presentation.pptx
2025-05-Q4-2024-Investor-Presentation.pptx
Samuele Fogagnolo
 
Rock, Paper, Scissors: An Apex Map Learning Journey
Rock, Paper, Scissors: An Apex Map Learning JourneyRock, Paper, Scissors: An Apex Map Learning Journey
Rock, Paper, Scissors: An Apex Map Learning Journey
Lynda Kane
 
AI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global TrendsAI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global Trends
InData Labs
 
Complete Guide to Advanced Logistics Management Software in Riyadh.pdf
Complete Guide to Advanced Logistics Management Software in Riyadh.pdfComplete Guide to Advanced Logistics Management Software in Riyadh.pdf
Complete Guide to Advanced Logistics Management Software in Riyadh.pdf
Software Company
 
Build Your Own Copilot & Agents For Devs
Build Your Own Copilot & Agents For DevsBuild Your Own Copilot & Agents For Devs
Build Your Own Copilot & Agents For Devs
Brian McKeiver
 
Big Data Analytics Quick Research Guide by Arthur Morgan
Big Data Analytics Quick Research Guide by Arthur MorganBig Data Analytics Quick Research Guide by Arthur Morgan
Big Data Analytics Quick Research Guide by Arthur Morgan
Arthur Morgan
 
Splunk Security Update | Public Sector Summit Germany 2025
Splunk Security Update | Public Sector Summit Germany 2025Splunk Security Update | Public Sector Summit Germany 2025
Splunk Security Update | Public Sector Summit Germany 2025
Splunk
 
Hands On: Create a Lightning Aura Component with force:RecordData
Hands On: Create a Lightning Aura Component with force:RecordDataHands On: Create a Lightning Aura Component with force:RecordData
Hands On: Create a Lightning Aura Component with force:RecordData
Lynda Kane
 
Asthma presentación en inglés abril 2025 pdf
Asthma presentación en inglés abril 2025 pdfAsthma presentación en inglés abril 2025 pdf
Asthma presentación en inglés abril 2025 pdf
VanessaRaudez
 
"Client Partnership — the Path to Exponential Growth for Companies Sized 50-5...
"Client Partnership — the Path to Exponential Growth for Companies Sized 50-5..."Client Partnership — the Path to Exponential Growth for Companies Sized 50-5...
"Client Partnership — the Path to Exponential Growth for Companies Sized 50-5...
Fwdays
 
Automation Dreamin': Capture User Feedback From Anywhere
Automation Dreamin': Capture User Feedback From AnywhereAutomation Dreamin': Capture User Feedback From Anywhere
Automation Dreamin': Capture User Feedback From Anywhere
Lynda Kane
 
Electronic_Mail_Attacks-1-35.pdf by xploit
Electronic_Mail_Attacks-1-35.pdf by xploitElectronic_Mail_Attacks-1-35.pdf by xploit
Electronic_Mail_Attacks-1-35.pdf by xploit
niftliyevhuseyn
 
Learn the Basics of Agile Development: Your Step-by-Step Guide
Learn the Basics of Agile Development: Your Step-by-Step GuideLearn the Basics of Agile Development: Your Step-by-Step Guide
Learn the Basics of Agile Development: Your Step-by-Step Guide
Marcel David
 
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdfSAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
Precisely
 
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Impelsys Inc.
 
Linux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdfLinux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdf
RHCSA Guru
 
Buckeye Dreamin 2024: Assessing and Resolving Technical Debt
Buckeye Dreamin 2024: Assessing and Resolving Technical DebtBuckeye Dreamin 2024: Assessing and Resolving Technical Debt
Buckeye Dreamin 2024: Assessing and Resolving Technical Debt
Lynda Kane
 
AI EngineHost Review: Revolutionary USA Datacenter-Based Hosting with NVIDIA ...
AI EngineHost Review: Revolutionary USA Datacenter-Based Hosting with NVIDIA ...AI EngineHost Review: Revolutionary USA Datacenter-Based Hosting with NVIDIA ...
AI EngineHost Review: Revolutionary USA Datacenter-Based Hosting with NVIDIA ...
SOFTTECHHUB
 
Semantic Cultivators : The Critical Future Role to Enable AI
Semantic Cultivators : The Critical Future Role to Enable AISemantic Cultivators : The Critical Future Role to Enable AI
Semantic Cultivators : The Critical Future Role to Enable AI
artmondano
 
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
Alan Dix
 
2025-05-Q4-2024-Investor-Presentation.pptx
2025-05-Q4-2024-Investor-Presentation.pptx2025-05-Q4-2024-Investor-Presentation.pptx
2025-05-Q4-2024-Investor-Presentation.pptx
Samuele Fogagnolo
 
Rock, Paper, Scissors: An Apex Map Learning Journey
Rock, Paper, Scissors: An Apex Map Learning JourneyRock, Paper, Scissors: An Apex Map Learning Journey
Rock, Paper, Scissors: An Apex Map Learning Journey
Lynda Kane
 
AI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global TrendsAI and Data Privacy in 2025: Global Trends
AI and Data Privacy in 2025: Global Trends
InData Labs
 
Complete Guide to Advanced Logistics Management Software in Riyadh.pdf
Complete Guide to Advanced Logistics Management Software in Riyadh.pdfComplete Guide to Advanced Logistics Management Software in Riyadh.pdf
Complete Guide to Advanced Logistics Management Software in Riyadh.pdf
Software Company
 
Build Your Own Copilot & Agents For Devs
Build Your Own Copilot & Agents For DevsBuild Your Own Copilot & Agents For Devs
Build Your Own Copilot & Agents For Devs
Brian McKeiver
 
Big Data Analytics Quick Research Guide by Arthur Morgan
Big Data Analytics Quick Research Guide by Arthur MorganBig Data Analytics Quick Research Guide by Arthur Morgan
Big Data Analytics Quick Research Guide by Arthur Morgan
Arthur Morgan
 
Splunk Security Update | Public Sector Summit Germany 2025
Splunk Security Update | Public Sector Summit Germany 2025Splunk Security Update | Public Sector Summit Germany 2025
Splunk Security Update | Public Sector Summit Germany 2025
Splunk
 
Hands On: Create a Lightning Aura Component with force:RecordData
Hands On: Create a Lightning Aura Component with force:RecordDataHands On: Create a Lightning Aura Component with force:RecordData
Hands On: Create a Lightning Aura Component with force:RecordData
Lynda Kane
 
Asthma presentación en inglés abril 2025 pdf
Asthma presentación en inglés abril 2025 pdfAsthma presentación en inglés abril 2025 pdf
Asthma presentación en inglés abril 2025 pdf
VanessaRaudez
 
"Client Partnership — the Path to Exponential Growth for Companies Sized 50-5...
"Client Partnership — the Path to Exponential Growth for Companies Sized 50-5..."Client Partnership — the Path to Exponential Growth for Companies Sized 50-5...
"Client Partnership — the Path to Exponential Growth for Companies Sized 50-5...
Fwdays
 
Automation Dreamin': Capture User Feedback From Anywhere
Automation Dreamin': Capture User Feedback From AnywhereAutomation Dreamin': Capture User Feedback From Anywhere
Automation Dreamin': Capture User Feedback From Anywhere
Lynda Kane
 
Electronic_Mail_Attacks-1-35.pdf by xploit
Electronic_Mail_Attacks-1-35.pdf by xploitElectronic_Mail_Attacks-1-35.pdf by xploit
Electronic_Mail_Attacks-1-35.pdf by xploit
niftliyevhuseyn
 
Learn the Basics of Agile Development: Your Step-by-Step Guide
Learn the Basics of Agile Development: Your Step-by-Step GuideLearn the Basics of Agile Development: Your Step-by-Step Guide
Learn the Basics of Agile Development: Your Step-by-Step Guide
Marcel David
 
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdfSAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
SAP Modernization: Maximizing the Value of Your SAP S/4HANA Migration.pdf
Precisely
 
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Enhancing ICU Intelligence: How Our Functional Testing Enabled a Healthcare I...
Impelsys Inc.
 
Linux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdfLinux Professional Institute LPIC-1 Exam.pdf
Linux Professional Institute LPIC-1 Exam.pdf
RHCSA Guru
 
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