Playwright Test is a new test runner built from scratch by the Playwright team specifically to accommodate end-to-end testing needs. Join Principal Engineer, Andrey Lushinkov as he demonstrates how to use Playwright Test to author new tests, how to migrate existing tests, how to deploy them on CI, and debug them if something goes wrong.
4. ● New product from Playwright Team
What is Playwright Test?
https:/
/github.com/microsoft/playwright
5. ● New product from Playwright Team
● Batteries-included test framework for end-to-end testing
What is Playwright Test?
https:/
/github.com/microsoft/playwright
6. ● New product from Playwright Team
● Batteries-included test framework for end-to-end testing
○ TypeScript support out-of-the-box
What is Playwright Test?
https:/
/github.com/microsoft/playwright
7. ● New product from Playwright Team
● Batteries-included test framework for end-to-end testing
○ TypeScript support out-of-the-box
○ Parallel execution out-of-the-box
What is Playwright Test?
https:/
/github.com/microsoft/playwright
8. ● New product from Playwright Team
● Batteries-included test framework for end-to-end testing
○ TypeScript support out-of-the-box
○ Parallel execution out-of-the-box
○ Cross-browser, Snapshots, Fixtures, ...
What is Playwright Test?
https:/
/github.com/microsoft/playwright
9. ● New product from Playwright Team
● Batteries-included test framework for end-to-end testing
○ TypeScript support out-of-the-box
○ Parallel execution out-of-the-box
○ Cross-browser, Snapshots, Fixtures, …
○ Time-travel debugging 🔥
What is Playwright Test?
https:/
/github.com/microsoft/playwright
10. ● New product from Playwright Team
● Batteries-included test framework for end-to-end testing
○ TypeScript support out-of-the-box
○ Parallel execution out-of-the-box
○ Cross-browser, Snapshots, Fixtures, …
○ Time-travel debugging 🔥
● Open Source, Free
What is Playwright Test?
https:/
/github.com/microsoft/playwright
15. // test.spec.ts
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.click('a:visible:has-text("Docs")');
expect(page.url()).toBe('https://playwright.dev/docs/intro');
});
T
y
p
e
S
c
r
i
p
t
17. # .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-deps
- run: npx playwright install
- run: npx playwright test
G
i
t
h
u
b
A
c
t
i
o
n
s
21. // test.spec.ts
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.click('a:visible:has-text("Docs")');
expect(page.url()).toBe('https://playwright.dev/docs/intro');
});
T
y
p
e
S
c
r
i
p
t
22. // test.spec.ts
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.click('a:visible:has-text("Docs")');
expect(page.url()).toBe('https://playwright.dev/docs/intro');
});
T
y
p
e
S
c
r
i
p
t
23. // test.spec.ts
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.click('a:visible:has-text("Docs")');
expect(page.url()).toBe('https://playwright.dev/docs/intro');
});
T
y
p
e
S
c
r
i
p
t
“Fixture”
24. // test.spec.ts
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
await page.goto('https://playwright.dev/');
await page.click('a:visible:has-text("Docs")');
expect(page.url()).toBe('https://playwright.dev/docs/intro');
});
T
y
p
e
S
c
r
i
p
t
1. Fully isolated Playwright page
“Fixture”
25. // test.spec.ts
import { test, expect } from '@playwright/test';
test('test', async ({ page, context }) => {
await page.goto('https://playwright.dev/');
await page.click('a:visible:has-text("Docs")');
expect(page.url()).toBe('https://playwright.dev/docs/intro');
});
T
y
p
e
S
c
r
i
p
t
2. Fully isolated Playwright context for multi-page tests
“Fixture”
26. // test.spec.ts
import { test, expect } from '@playwright/test';
test('test', async ({ page, context, browserName }) => {
await page.goto('https://playwright.dev/');
await page.click('a:visible:has-text("Docs")');
expect(page.url()).toBe('https://playwright.dev/docs/intro');
});
T
y
p
e
S
c
r
i
p
t
3. Browser name: ‘chromium’, ‘firefox’ or ‘webkit’
“Fixture”
27. // test.spec.ts
import { test, expect } from '@playwright/test';
test('test', async ({ page, context, browserName, isMobile }) => {
await page.goto('https://playwright.dev/');
await page.click('a:visible:has-text("Docs")');
expect(page.url()).toBe('https://playwright.dev/docs/intro');
});
T
y
p
e
S
c
r
i
p
t
4. “true” when running in mobile browser
“Fixture”
28. // test.spec.ts
import { test, expect } from '@playwright/test';
test('test', async ({ page, context, browserName, isMobile }) => {
await page.goto('https://playwright.dev/');
await page.click('a:visible:has-text("Docs")');
expect(page.url()).toBe('https://playwright.dev/docs/intro');
});
T
y
p
e
S
c
r
i
p
t
4. “true” when running in mobile browser
“Fixture”
5. channel
6. timezoneId
7. locale
8. ...
29. Concept: “Fixtures” 🦄
● Borrowed from PyTest
● Isolated configurable environment for each test
● Playwright Test comes with many ready-to-use fixtures
● Create your own fixtures instead of “beforeEach” / “afterEach” hooks
○ Organize tests semantically, not by environment
○ Fixtures are only initialized when required by test
○ Fixtures are ~20% less code
● Read more: https:/
/playwright.dev/docs/test-fixtures
34. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
};
export default config;
T
y
p
e
S
c
r
i
p
t
35. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
};
export default config;
T
y
p
e
S
c
r
i
p
t
36. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
};
export default config;
T
y
p
e
S
c
r
i
p
t
37. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
};
export default config;
T
y
p
e
S
c
r
i
p
t
38. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
// 1. Runner configuration
reporter: 'list',
};
export default config;
● globalSetup
● globalTeardown
● ...
T
y
p
e
S
c
r
i
p
t
39. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
// 1. Runner configuration
reporter: 'list',
};
export default config;
● globalSetup
● globalTeardown
● ...
T
y
p
e
S
c
r
i
p
t
First-Party:
● dot
● list
● line
● json
● junit
Third-Party:
● experimental-allure-playwright 🔥
40. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
// 1. Runner configuration
reporter: 'list',
};
export default config;
● globalSetup
● globalTeardown
● ...
T
y
p
e
S
c
r
i
p
t
41. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
// 1. Runner configuration
reporter: 'list',
projects: [{
}]};
export default config;
● globalSetup
● globalTeardown
● ...
T
y
p
e
S
c
r
i
p
t
42. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
// 1. Runner configuration
reporter: 'list',
projects: [{
// 2. Project configuration
name: 'chromium',
retries: 0,
timeout: 30000,
}]};
export default config;
● globalSetup
● globalTeardown
● ...
● outputDir
● testDir
● ...
T
y
p
e
S
c
r
i
p
t
43. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
// 1. Runner configuration
reporter: 'list',
projects: [{
// 2. Project configuration
name: 'chromium',
retries: 0,
timeout: 30000,
// 3. Fixtures configuration
use: {
browserName: 'chromium',
},
}]};
export default config;
● globalSetup
● globalTeardown
● ...
● outputDir
● testDir
● ...
T
y
p
e
S
c
r
i
p
t
● browserName
● headless
● screenshot
● trace
● ...
44. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
// 1. Runner configuration
reporter: 'list',
projects: [{
// 2. Project configuration
name: 'chromium',
retries: 0,
timeout: 30000,
// 3. Fixtures configuration
use: {
browserName: 'chromium',
},
}]};
export default config;
● globalSetup
● globalTeardown
● ...
● outputDir
● testDir
● ...
● browserName
● headless
● screenshot
● trace
● ...
T
y
p
e
S
c
r
i
p
t
45. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
// 1. Runner configuration
reporter: 'list',
projects: [{
// 2. Project configuration
name: 'chromium',
retries: 0,
timeout: 30000,
// 3. Fixtures configuration
use: {
browserName: 'chromium',
},
}]};
export default config;
T
y
p
e
S
c
r
i
p
t
// test.spec.ts
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
page; // chromium page
});
46. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
// 1. Runner configuration
reporter: 'list',
projects: [{
// 2. Project configuration
name: 'chromium',
retries: 0,
timeout: 30000,
// 3. Fixtures configuration
use: {
browserName: 'chromium',
},
}]};
export default config;
T
y
p
e
S
c
r
i
p
t
// test.spec.ts
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
page; // chromium page
});
47. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
// 1. Runner configuration
reporter: 'list',
projects: [{
// 2. Project configuration
name: 'chromium',
retries: 0,
timeout: 30000,
// 3. Fixtures configuration
use: {
browserName: 'chromium',
},
}]};
export default config;
T
y
p
e
S
c
r
i
p
t
// test.spec.ts
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
page; // chromium page
});
48. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
// 1. Runner configuration
reporter: 'list',
projects: [{
// 2. Project configuration
name: 'chromium',
retries: 0,
timeout: 30000,
// 3. Fixtures configuration
use: {
browserName: 'webkit',
},
}]};
export default config;
T
y
p
e
S
c
r
i
p
t
// test.spec.ts
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
page; // webkit page
});
53. // 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
54. // 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
55. // 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
56. // 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
57. // 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
58. // 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
63. // 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
64. // 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
65. // 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
66. // 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
68. // playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config : PlaywrightTestConfig = {
projects: [
{
name: 'Desktop Chromium',
use: { browserName: 'chromium', },
},
{
name: 'iphone',
use: { ...devices['iPhone 12 Pro'], },
},
]
};
export default config;
T
y
p
e
S
c
r
i
p
t
69. // playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config : PlaywrightTestConfig = {
projects: [
{
name: 'Desktop Chromium',
use: { browserName: 'chromium', },
},
{
name: 'iphone',
use: { ...devices['iPhone 12 Pro'], },
},
]
};
export default config;
1. Import Devices
T
y
p
e
S
c
r
i
p
t
70. // playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test';
const config : PlaywrightTestConfig = {
projects: [
{
name: 'Desktop Chromium',
use: { browserName: 'chromium', },
},
{
name: 'iphone',
use: { ...devices['iPhone 12 Pro'], },
},
]
};
export default config;
1. Import Devices
T
y
p
e
S
c
r
i
p
t
2. Run tests on iPhone
71. // test.spec.ts
import { test, expect } from '@playwright/test';
test('should work', async ({ page, isMobile }) => {
await page.goto('https://playwright.dev');
if (isMobile)
await page.click('[aria-label="Navigation bar toggle"]');
await page.click('a:visible:has-text("Docs")');
expect(page.url()).toBe('https://playwright.dev/docs/intro');
});
T
y
p
e
S
c
r
i
p
t
75. Playwright Test Artifacts 💎
Test Artifacts – any by-product of test running that helps debug test failures.
All artifacts are stored by default in test-results folder.
76. Playwright Test Artifacts 💎
Test Artifacts – any by-product of test running that helps debug test failures.
All artifacts are stored by default in test-results folder.
1. screenshots
77. Playwright Test Artifacts 💎
Test Artifacts – any by-product of test running that helps debug test failures.
All artifacts are stored by default in test-results folder.
1. screenshots 2. videos
78. Playwright Test Artifacts 💎
Test Artifacts – any by-product of test running that helps debug test failures.
All artifacts are stored by default in test-results folder.
1. screenshots
��♂
3. traces
2. videos
D
i
s
c
u
s
s
e
d
l
a
t
e
r
t
o
d
a
y
!
79. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
retries: 2,
use: {
screenshot: 'only-on-failure',
video: 'off',
trace: 'on-first-retry',
},
projects: [{
name: 'Desktop Chromium',
use: { browserName: 'chromium', },
},
]};
export default config;
T
y
p
e
S
c
r
i
p
t
80. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
retries: 2,
use: {
screenshot: 'only-on-failure',
video: 'off',
trace: 'on-first-retry',
},
projects: [{
name: 'Desktop Chromium',
use: { browserName: 'chromium', },
},
]};
export default config;
T
y
p
e
S
c
r
i
p
t
81. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
retries: 2,
use: {
screenshot: 'only-on-failure',
video: 'off',
trace: 'on-first-retry',
},
projects: [{
name: 'Desktop Chromium',
use: { browserName: 'chromium', },
},
]};
export default config;
T
y
p
e
S
c
r
i
p
t
82. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test';
const config : PlaywrightTestConfig = {
retries: 2,
use: {
screenshot: 'only-on-failure',
video: 'off',
trace: 'on-first-retry',
},
projects: [{
name: 'Desktop Chromium',
use: { browserName: 'chromium', },
},
]};
export default config;
T
y
p
e
S
c
r
i
p
t
Recommended 👍
83. Screenshot Artifacts
● “on” – always record screenshot after the test is finished
● “off” – never record screenshot after the test is finished
● “only-on-failure” – record screenshot after the test is failed
84. Screenshot Artifacts
● “on” – always record screenshot after the test is finished
● “off” – never record screenshot after the test is finished
● “only-on-failure” – record screenshot after the test is failed
Recommended 👍
85. Video Artifacts
● “on” – always record video of a test
● “off” – never record video of a test
● “retain-on-failure” – always record video of a test, but keep only videos of
failed test runs.
● “on-first-retry” – only record video when retrying test
86. Video Artifacts
● “on” – always record video of a test
● “off” – never record video of a test
● “retain-on-failure” – always record video of a test, but keep only videos of
failed test runs.
● “on-first-retry” – only record video when retrying test
Recommended 👍
87. Trace Artifacts
● “on” – always record trace of a test
● “off” – never record trace of a test
● “retain-on-failure” – always record trace of a test, but keep only traces of
failed test runs.
● “on-first-retry” – only record trace when retrying test
��
88. Trace Artifacts
● “on” – always record trace of a test
● “off” – never record trace of a test
● “retain-on-failure” – always record trace of a test, but keep only traces of
failed test runs.
● “on-first-retry” – only record trace when retrying test
��
Recommended 👍
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-deps
- run: npx playwright install
- run: npx playwright test
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-deps
- run: npx playwright install
- run: npx playwright test
- uses: actions/upload-artifact@v1
if: always()
with:
name: test-results
path: test-results
G
i
t
h
u
b
A
c
t
i
o
n
s
Uploading Artifacts