🎭 Playwright
Who We Are
● 2009-2019
WebKit Web Inspector
Chrome Developer Tools
Who We Are
● 2009-2019
WebKit Web Inspector
Chrome Developer Tools
Who We Are
● 2009-2019
WebKit Web Inspector
Chrome Developer Tools
Who We Are
● 2009-2019
WebKit Web Inspector
Chrome Developer Tools
● 2011-...
Chrome DevTools Protocol
Who We Are
● 2009-2019
WebKit Web Inspector
Chrome Developer Tools
● 2011-...
Chrome DevTools Protocol
● 2015-2016
Node.js Debugger
Who We Are
● 2009-2019
WebKit Web Inspector
Chrome Developer Tools
● 2011-...
Chrome DevTools Protocol
● 2015-2016
Node.js Debugger
● 2017-2019
Puppeteer
Who We Are
● 2009-2019
WebKit Web Inspector
Chrome Developer Tools
● 2011-...
Chrome DevTools Protocol
● 2015-2016
Node.js Debugger
● 2017-2019
Puppeteer
Who We Are
● 2009-2019
WebKit Web Inspector
Chrome Developer Tools
● 2011-...
Chrome DevTools Protocol
● 2015-2016
Node.js Debugger
● 2017-2019
Puppeteer
● 2019-...
Playwright
Who We Are
Playwright
github.com/microsoft/playwright
🍿 Demo Time! 🍿
import {chromium, firefox, webkit} from 'playwright';
for (const browserType of [chromium, firefox, webkit]) {
const browser = await browserType.launch();
const page = await browser.newPage();
await page.goto('https://playwright.dev');
await page.screenshot({path: `image-${browserType.name()}.png`});
await browser.close();
}
import {chromium, firefox, webkit} from 'playwright';
for (const browserType of [chromium, firefox, webkit]) {
const browser = await browserType.launch();
const page = await browser.newPage();
await page.goto('https://playwright.dev');
await page.screenshot({path: `image-${browserType.name()}.png`});
await browser.close();
}
J
a
v
a
S
c
r
i
p
t
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
for browser_type in [p.chromium, p.firefox, p.webkit]:
browser = browser_type.launch()
page = browser.new_page()
page.goto("https://playwright.dev")
page.screenshot(path="image-" + browser_type.name + ".png")
browser.close()
P
y
t
h
o
n
public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) {
List<BrowserType> browserTypes = Arrays.asList(
playwright.chromium(), playwright.webkit(), playwright.firefox()
);
for (BrowserType browserType : browserTypes) {
try (Browser browser = browserType.launch()) {
Page page = browser.newPage();
page.navigate("http://playwright.dev");
page.screenshot(new Page.ScreenshotOptions()
.setPath(Paths.get("image-" + browserType.name() + ".png")));
}
}
}
}
J
a
v
a
Why Playwright?
Playwright:
✅ dependable
✅ efficient
✅ capable
✅ ubiquitous
✅ delightful
✅ lively
Act I
Dependable
Dependable
Efficient
Capable
Ubiquitous
Delightful
Lively
Dependable
👌 We Triage Issues in <48 hours
📚 We Fix Issues (>1600 fixed)
✈ We Take Full Responsibility
github.com/microsoft/playwright
Downloads API
Downloads API
Level 1: Browser Engineering
C++
- Chromium Development
- Improving Chrome DevTools Protocol
- Google Chrome, Microsoft Edge, Opera, etc
Level 1: Browser Engineering
C++
- Chromium Development
- Improving Chrome DevTools Protocol
- Google Chrome, Microsoft Edge, Opera, etc
- WebKit Development
- Improving Web Inspector Protocol
- Safari, Mobile Safari, Epiphany, etc
Level 1: Browser Engineering
C++
- Chromium Development
- Improving Chrome DevTools Protocol
- Google Chrome, Microsoft Edge, Opera, etc
- WebKit Development
- Improving Web Inspector Protocol
- Safari, Mobile Safari, Epiphany, etc
- Firefox Development
- Improving “Juggler” Protocol
- Mozilla Firefox
Level 1: Browser Engineering
C++
- Chromium Development
- Improving Chrome DevTools Protocol
- Google Chrome, Microsoft Edge, Opera, etc
- WebKit Development
- Improving Web Inspector Protocol
- Safari, Mobile Safari, Epiphany, etc
- Firefox Development
- Improving “Juggler” Protocol
- Mozilla Firefox
Level 1: Browser Engineering
C++
Level 2: Playwright Driver
Level 1: Browser Engineering
C++
TypeScript
- Single web automation protocol
- Unify all remote debugging protocols
- Expose “Downloads API” in driver
Level 2: Playwright Driver
🎭 Playwright Driver
Level 1: Browser Engineering
C++
TypeScript
Level 3: Language Bindings
🎭 Playwright Driver
Level 1: Browser Engineering
C++
Level 2: Playwright Driver
TypeScript
Level 3: Language Bindings
🎭 Playwright Driver
Level 1: Browser Engineering
C++
Level 2: Playwright Driver
Playwright
Playwright
for
Java
Playwright
for
Python
Playwright
for
C#
Expose language idiomatic API to
control downloads
TypeScript
Level 3: Language Bindings
🎭 Playwright Driver
Level 1: Browser Engineering
C++
Level 2: Playwright Driver
Playwright
Playwright
for
Java
Playwright
for
Python
Playwright
for
C#
Expose language idiomatic API to
control downloads
TypeScript
Level 3: Language Bindings
🎭 Playwright Driver
Level 1: Browser Engineering
C++
Level 2: Playwright Driver
Playwright
Playwright
for
Java
Playwright
for
Python
Playwright
for
C#
Expose language idiomatic API to
control downloads
TypeScript
Being Dependable
🎭 Playwright Driver
C++
Playwright
Playwright
for
Java
Playwright
for
Python
Playwright
for
C#
Cross-Browser Testing Tool
● 👀 8 teams from multiple companies
● 🐌 Slow feedback loop (months /
years)
Playwright
● 💫 1 team from a single company
● 🐆 Fast feedback loop (days / weeks)
More Like This
● Downloads API
● Screencast API
● Drag & Drop API
● Clipboard API
● Browser-Level cookies
● Browser Contexts
● Per-context HTTP proxy
● ...
Summary: Playwright is Dependable
👌 We Triage Issues in <48 hours
📚 We Fix Issues (>1600 fixed)
✈ We Take Full Responsibility
Act II
Efficient
Dependable
Efficient
Capable
Ubiquitous
Delightful
Lively
What is “Efficient”?
Efficient = Fast
?
Automation Costs
● Throughput = spend less CPU cycles
● Latency = run tests fast
● Flakiness = reliable runs
Playwright Being Efficient
● Trick 1: Browser Contexts
● Trick 2: Auto-waiting
Trick 1: Browser Contexts
❌ Never Restart a Browser
● Slow instantiation (>100ms)
● Huge memory overhead
✅ Always create Browser Contexts
● Full isolation
● Fast instantiation (~1ms)
● Low overhead Browser Context
Trick 1: Browser Contexts
Context 1
Trick 1: Browser Contexts
Context 1 Context 2
Trick 1: Browser Contexts
Context 1 Context 2
import {webkit, chromium, firefox} from 'playwright';
const browser = await chromium.launch();
for (let i = 0; i < 10; ++i) {
// Fast, simple, configurable!
const context = await browser.newContext()
const page = await context.newPage();
await page.goto('https://playwright.dev');
await context.close();
}
import {webkit, chromium, firefox} from 'playwright';
const browser = await chromium.launch();
for (let i = 0; i < 10; ++i) {
// Fast, simple, configurable!
const context = await browser.newContext()
const page = await context.newPage();
await page.goto('https://playwright.dev');
await context.close();
}
import {webkit, chromium, firefox} from 'playwright';
const browser = await chromium.launch();
for (let i = 0; i < 10; ++i) {
// Fast, simple, configurable!
const context = await browser.newContext()
const page = await context.newPage();
await page.goto('https://playwright.dev');
await context.close();
}
Form Filling: No Autowait
Form Filling: No Autowait
e-mail..
Form Filling: No Autowait
e-mail..
Form Filling: No Autowait
e-mail..
Form Filling: No Autowait
passwd...
Form Filling: No Autowait
passwd...
Form Filling: No Autowait
passwd...
Form Filling: No Autowait
submitting...
Form Filling: No Autowait
submitting...
Form Filling: No Autowait
MISSED
Form Filling: No Autowait
MISSED
Disabled button!
await page.goto('https://form.example.com');
// Shall we wait for fields to be enabled?..
await page.fill('#email', 'aslushnikov@gmail.com');
await page.fill('#password', 'mypassword');
// Wait for button to get enabled and click submit
setTimeout(async () => {
await page.click('#submit');
}, 1000);
N
o
n
-
P
l
a
y
w
r
i
g
h
t
P
s
e
u
d
o
-
C
o
d
e
await page.goto('https://form.example.com');
// Shall we wait for fields to be enabled?..
await page.fill('#email', 'aslushnikov@gmail.com');
await page.fill('#password', 'mypassword');
// Wait for button to get enabled and click submit
setTimeout(async () => {
await page.click('#submit');
}, 1000);
N
o
n
-
P
l
a
y
w
r
i
g
h
t
P
s
e
u
d
o
-
C
o
d
e
await page.goto('https://form.example.com');
// Shall we wait for fields to be enabled?..
await page.fill('#email', 'aslushnikov@gmail.com');
await page.fill('#password', 'mypassword');
// Wait for button to get enabled and click submit
setTimeout(async () => {
await page.click('#submit');
}, 1000);
N
o
n
-
P
l
a
y
w
r
i
g
h
t
P
s
e
u
d
o
-
C
o
d
e
Time Does Not Exist in the Cloud!
Trick 2: Auto-waiting ✨
Auto-Waiting..
Trick 2: Auto-waiting ✨
Successful!
Enabled!
Trick 2: Auto-waiting ✨
Trick 2: Auto-waiting ✨
// ✨ Playwright auto-waiting by default!
await page.goto('https://form.example.com');
await page.fill('#email', 'aslushnikov@gmail.com');
await page.fill('#password', 'mypassword');
await page.click('#submit');
P
l
a
y
w
r
i
g
h
t
// ✨ Playwright auto-waiting by default!
await page.goto('https://form.example.com');
await page.fill('#email', 'aslushnikov@gmail.com');
await page.fill('#password', 'mypassword');
await page.click('#submit');
P
l
a
y
w
r
i
g
h
t
// ✨ Playwright auto-waiting by default!
await page.goto('https://form.example.com');
await page.fill('#email', 'aslushnikov@gmail.com');
await page.fill('#password', 'mypassword');
await page.click('#submit');
P
l
a
y
w
r
i
g
h
t
// ✨ Playwright auto-waiting by default!
await page.goto('https://form.example.com');
await page.fill('#email', 'aslushnikov@gmail.com');
await page.fill('#password', 'mypassword');
await page.click('#submit');
P
l
a
y
w
r
i
g
h
t
Trick 2: Auto-waiting ✨
Trick 2: Auto-waiting ✨
Trick 2: Auto-waiting ✨
Trick 2: Auto-waiting ✨
Trick 2: Auto-waiting ✨
Trick 2: Auto-waiting ✨
Trick 2: Auto-waiting ✨
Summary: Playwright is Efficient
● Trick 1: Browser Contexts
● Trick 2: Auto-waiting
Act III
Capable
Dependable
Efficient
Capable
Ubiquitous
Delightful
Lively
import {webkit, devices} from 'playwright';
const browser = await webkit.launch();
const context = await browser.newContext({
...devices['iPhone 12 Pro'],
});
const page = await context.newPage();
await page.goto('https://playwright.dev');
await browser.close();
📱
D
e
v
i
c
e
E
m
u
l
a
t
i
o
n
import {webkit, devices} from 'playwright';
const browser = await webkit.launch();
const context = await browser.newContext({
...devices['iPhone 12 Pro'],
});
const page = await context.newPage();
await page.goto('https://playwright.dev');
await browser.close();
📱
D
e
v
i
c
e
E
m
u
l
a
t
i
o
n
import {webkit, devices} from 'playwright';
const browser = await webkit.launch();
const context = await browser.newContext({
...devices['iPhone 12 Pro'],
});
const page = await context.newPage();
await page.goto('https://playwright.dev');
await browser.close();
📱
D
e
v
i
c
e
E
m
u
l
a
t
i
o
n
import {chromium, firefox, webkit} from 'playwright';
const browser = await firefox.launch();
const context = await browser.newContext({
geolocation: { longitude: 48.858455, latitude: 2.294474 },
permissions: ['geolocation']
});
const page = await context.newPage();
await page.goto('https://playwright.dev');
await context.setGeolocation({
longitude: 29.97, latitude: 31.13
});
G
e
o
l
o
c
a
t
i
o
n
&
P
e
r
m
i
s
s
i
o
n
s
import {chromium, firefox, webkit} from 'playwright';
const browser = await firefox.launch();
const context = await browser.newContext({
geolocation: { longitude: 48.858455, latitude: 2.294474 },
permissions: ['geolocation']
});
const page = await context.newPage();
await page.goto('https://playwright.dev');
await context.setGeolocation({
longitude: 29.97, latitude: 31.13
});
G
e
o
l
o
c
a
t
i
o
n
&
P
e
r
m
i
s
s
i
o
n
s
import {chromium, firefox, webkit} from 'playwright';
const browser = await firefox.launch();
const context = await browser.newContext({
geolocation: { longitude: 48.858455, latitude: 2.294474 },
permissions: ['geolocation']
});
const page = await context.newPage();
await page.goto('https://playwright.dev');
await context.setGeolocation({
longitude: 29.97, latitude: 31.13
});
G
e
o
l
o
c
a
t
i
o
n
&
P
e
r
m
i
s
s
i
o
n
s
import {chromium, firefox, webkit} from 'playwright';
const browser = await firefox.launch();
const context = await browser.newContext({
geolocation: { longitude: 48.858455, latitude: 2.294474 },
permissions: ['geolocation']
});
const page = await context.newPage();
await page.goto('https://playwright.dev');
await context.setGeolocation({
longitude: 29.97, latitude: 31.13
});
G
e
o
l
o
c
a
t
i
o
n
&
P
e
r
m
i
s
s
i
o
n
s
import {webkit, chromium, firefox} from 'playwright';
const browser = await chromium.launch();
const context = await browser.newContext({
recordVideo: {
dir: 'videos/',
size: { width: 800, height: 600 },
},
});
const page = await context.newPage();
await page.goto('https://playwright.dev');
await page.close();
🎥
V
i
d
e
o
s
import {webkit, chromium, firefox} from 'playwright';
const browser = await chromium.launch();
const context = await browser.newContext({
recordVideo: {
dir: 'videos/',
size: { width: 800, height: 600 },
},
});
const page = await context.newPage();
await page.goto('https://playwright.dev');
await page.close();
🎥
V
i
d
e
o
s
import {webkit, chromium, firefox} from 'playwright';
const browser = await chromium.launch();
const context = await browser.newContext({
recordVideo: {
dir: 'videos/',
size: { width: 800, height: 600 },
},
});
const page = await context.newPage();
await page.goto('https://playwright.dev');
await page.close();
🎥
V
i
d
e
o
s
import {webkit, chromium, firefox} from 'playwright';
const browser = await webkit.launch();
const page = await browser.newPage();
page.on('request', r => console.log(r.method(), r.url()));
page.on('websocket', ws => console.log(ws.url()));
await page.goto('https://playwright.dev');
await browser.close();
🌎
N
e
t
w
o
r
k
&
W
e
b
S
o
c
k
e
t
s
import {webkit, chromium, firefox} from 'playwright';
const browser = await webkit.launch();
const page = await browser.newPage();
page.on('request', r => console.log(r.method(), r.url()));
page.on('websocket', ws => console.log(ws.url()));
await page.goto('https://playwright.dev');
await browser.close();
🌎
N
e
t
w
o
r
k
&
W
e
b
S
o
c
k
e
t
s
import {webkit, chromium, firefox} from 'playwright';
const browser = await firefox.launch();
const page = await browser.newPage();
await page.route('**/*.{png,jpg,jpeg}', route => route.abort());
await page.goto('https://playwright.dev');
await browser.close();
P
a
g
e
R
e
q
u
e
s
t
I
n
t
e
r
c
e
p
t
i
o
n
import {webkit, chromium, firefox} from 'playwright';
const browser = await firefox.launch();
const context = await browser.newContext();
await context.route('**/*.{png,jpg,jpeg}', route => route.abort());
const page = await page.newPage();
await page.goto('https://playwright.dev');
await browser.close();
C
o
n
t
e
x
t
R
e
q
u
e
s
t
I
n
t
e
r
c
e
p
t
i
o
n
import {webkit, chromium, firefox} from 'playwright';
const browser = await webkit.launch();
const page = await browser.newPage();
await page.goto('https://playwright.dev');
const [ download ] = await Promise.all([
page.waitForEvent('download'),
page.click('button#delayed-download')
]);
console.log(await download.path());
await browser.close();
D
o
w
n
l
o
a
d
s
import {webkit, chromium, firefox} from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://playwright.dev');
await page.click('button'); // CSS selector
await page.click('xpath=//button'); // XPath selector
await page.click('text=Log in'); // Text selector
await page.click(':nth-match(:text("Buy"), 3)'); // N-th match
await page.click('div:right-of(:text("Name"))'); // Layout 😱
await page.click(`xpath=//form >> text=Submit`); // Composite
S
m
a
r
t
S
e
l
e
c
t
o
r
s
import {webkit, chromium, firefox} from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://playwright.dev');
await page.click('button'); // CSS selector
await page.click('xpath=//button'); // XPath selector
await page.click('text=Log in'); // Text selector
await page.click(':nth-match(:text("Buy"), 3)'); // N-th match
await page.click('div:right-of(:text("Name"))'); // Layout 😱
await page.click(`xpath=//form >> text=Submit`); // Composite
S
m
a
r
t
S
e
l
e
c
t
o
r
s
import {webkit, chromium, firefox} from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://playwright.dev');
await page.click('button'); // CSS selector
await page.click('xpath=//button'); // XPath selector
await page.click('text=Log in'); // Text selector
await page.click(':nth-match(:text("Buy"), 3)'); // N-th match
await page.click('div:right-of(:text("Name"))'); // Layout 😱
await page.click(`xpath=//form >> text=Submit`); // Composite
S
m
a
r
t
S
e
l
e
c
t
o
r
s
import {webkit, chromium, firefox} from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://playwright.dev');
await page.click('button'); // CSS selector
await page.click('xpath=//button'); // XPath selector
await page.click('text=Log in'); // Text selector
await page.click(':nth-match(:text("Buy"), 3)'); // N-th match
await page.click('div:right-of(:text("Name"))'); // Layout 😱
await page.click(`xpath=//form >> text=Submit`); // Composite
S
m
a
r
t
S
e
l
e
c
t
o
r
s
import {webkit, chromium, firefox} from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://playwright.dev');
await page.click('button'); // CSS selector
await page.click('xpath=//button'); // XPath selector
await page.click('text=Log in'); // Text selector
await page.click(':nth-match(:text("Buy"), 3)'); // N-th match
await page.click('div:right-of(:text("Name"))'); // Layout 😱
await page.click(`xpath=//form >> text=Submit`); // Composite
S
m
a
r
t
S
e
l
e
c
t
o
r
s
import {webkit, chromium, firefox} from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://playwright.dev');
await page.click('button'); // CSS selector
await page.click('xpath=//button'); // XPath selector
await page.click('text=Log in'); // Text selector
await page.click(':nth-match(:text("Buy"), 3)'); // N-th match
await page.click('div:right-of(:text("Name"))'); // Layout 😱
await page.click(`xpath=//form >> text=Submit`); // Composite
S
m
a
r
t
S
e
l
e
c
t
o
r
s
import {webkit, chromium, firefox} from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://playwright.dev');
await page.click('button'); // CSS selector
await page.click('xpath=//button'); // XPath selector
await page.click('text=Log in'); // Text selector
await page.click(':nth-match(:text("Buy"), 3)'); // N-th match
await page.click('div:right-of(:text("Name"))'); // Layout 😱
await page.click(`xpath=//form >> text=Submit`); // Composite
S
m
a
r
t
S
e
l
e
c
t
o
r
s
// text content
const content = await page.textContent('nav:first-child');
expect(content).toBe('home');
// Attributes
const alt = await page.getAttribute('input', 'alt');
expect(alt).toBe('Text');
// Checkbox state
const checked = await page.isChecked('input');
expect(checked).toBeTruthy();
// Visibility
const visible = await page.isVisible('input');
expect(visible).toBeTruthy();
A
s
s
e
r
t
i
o
n
s
// text content
const content = await page.textContent('nav:first-child');
expect(content).toBe('home');
// Attributes
const alt = await page.getAttribute('input', 'alt');
expect(alt).toBe('Text');
// Checkbox state
const checked = await page.isChecked('input');
expect(checked).toBeTruthy();
// Visibility
const visible = await page.isVisible('input');
expect(visible).toBeTruthy();
A
s
s
e
r
t
i
o
n
s
// text content
const content = await page.textContent('nav:first-child');
expect(content).toBe('home');
// Attributes
const alt = await page.getAttribute('input', 'alt');
expect(alt).toBe('Text');
// Checkbox state
const checked = await page.isChecked('input');
expect(checked).toBeTruthy();
// Visibility
const visible = await page.isVisible('input');
expect(visible).toBeTruthy();
A
s
s
e
r
t
i
o
n
s
// text content
const content = await page.textContent('nav:first-child');
expect(content).toBe('home');
// Attributes
const alt = await page.getAttribute('input', 'alt');
expect(alt).toBe('Text');
// Checkbox state
const checked = await page.isChecked('input');
expect(checked).toBeTruthy();
// Visibility
const visible = await page.isVisible('input');
expect(visible).toBeTruthy();
A
s
s
e
r
t
i
o
n
s
import {chromium} from 'playwright';
const browser = await chromium.launch({
// Can be 'chrome', 'chrome-beta', 'msedge-beta', 'msedge-dev'
channel: 'msedge',
});
const page = await context.newPage();
await page.goto('https://playwright.dev');
await browser.close();
M
i
c
r
o
s
o
f
t
E
d
g
e
import {chromium} from 'playwright';
const browser = await chromium.launch({
// Can be 'chrome', 'chrome-beta', 'msedge-beta', 'msedge-dev'
channel: 'msedge',
});
const page = await context.newPage();
await page.goto('https://playwright.dev');
await browser.close();
M
i
c
r
o
s
o
f
t
E
d
g
e
More Features
● Built-in Shadow DOM piercing
● Idiomatic frames API
● Custom Selector Engines
● TimeZone / Locale / Color Scheme emulation
● Per-Context HTTP Proxy
● Workers / Service Workers
● Console sniffing
● Dialogs
● File Uploads
● Element Handles
● ...
● ubuntu-18.04 (chromium)
● ubuntu-18.04 (firefox)
● ubuntu-18.04 (webkit)
● ubuntu-20.04 (chromium)
● ubuntu-20.04 (firefox)
● ubuntu-20.04 (webkit)
● macos-10.14 (chromium)
● macos-10.14 (firefox)
● macos-10.14 (webkit)
● macos-10.15 (chromium)
● macos-10.15 (firefox)
● macos-10.15 (webkit)
● macos-11.0 (chromium)
● macos-11.0 (firefox)
● macos-11.0 (webkit)
● macos-11.0 arm64 (chromium)
● macos-11.0 arm64 (firefox)
● macos-11.0 arm64 (webkit)
Rigorous Testing ● Windows (chromium)
● Windows (firefox)
● Windows (webkit)
● test-package-installations (^10.17.0)
● test-package-installations (^12.0.0)
● test-package-installations (^14.1.0)
● Headful Linux (chromium)
● Headful Linux (firefox)
● Headful Linux (webkit)
● Transport (driver)
● Transport (service)
● Video Linux (chromium)
● Video Linux (firefox)
● Video Linux (webkit)
● Android Emulator (shard 1)
● Android Emulator (shard 2)
● Chrome Stable (Linux)
● Chrome Stable (Win)
● Chrome Stable (Mac)
● Edge Stable (Win)
● Electron Linux
Act IV
Ubiquitous
Dependable
Efficient
Capable
Ubiquitous
Delightful
Lively
WebKit Availability
Headed
Headless
❌
❌ ❌ ❌
✅
WebKit Availability
Headed
Headless
❌
❌ ❌ ❌
✅
🎭 Playwright WebKit Availability
Headed
Headless
✅
✅
✅
✅
✅
✅
● All Browsers
○ Chrome, Safari, Firefox
🎭 Playwright is Ubiquitous
● All Browsers
○ Chrome, Safari, Firefox
● All OS
○ Linux, Mac, Windows
🎭 Playwright is Ubiquitous
🎭 Playwright is Ubiquitous
● All Browsers
○ Chrome, Safari, Firefox
● All OS
○ Linux, Mac, Windows
● All Popular Languages
○ JavaScript / TypeScript
○ Java
○ Python
○ C# (alpha)
CI/CD, Services, Clients
● Any CI/CD
○ Github Actions, Travis CI, Azure Pipelines, Jenkins, Circle CI,
AWS, GCP, …
● Any CI/CD
○ Github Actions, Travis CI, Azure Pipelines, Jenkins, Circle CI,
AWS, GCP, …
● Docker Containers
○ docker pull mcr.microsoft.com/playwright
○ docker pull mcr.microsoft.com/playwright-java
CI/CD, Services, Clients
CI/CD, Services, Clients
● Any CI/CD
○ Github Actions, Travis CI, Azure Pipelines, Jenkins, Circle CI,
AWS, GCP, …
● Docker Containers
○ docker pull mcr.microsoft.com/playwright
○ docker pull mcr.microsoft.com/playwright-java
● Third-Party integrations
○ applitools.com
○ saucelabs.com
○ Checklyhq.com
○ www.testim.io
○ github.com/aerokube/moon
○ seleniumbox.com
CI/CD, Services, Clients
● Any CI/CD
○ Github Actions, Travis CI, Azure Pipelines, Jenkins, Circle CI,
AWS, GCP, …
● Docker Containers
○ docker pull mcr.microsoft.com/playwright
○ docker pull mcr.microsoft.com/playwright-java
● Third-Party integrations
○ applitools.com
○ saucelabs.com
○ Checklyhq.com
○ www.testim.io
○ github.com/aerokube/moon
○ seleniumbox.com
Playwright ❤ WebDriver
Chrome
DevTools
Protocol
Historical Context
Chrome
DevTools
Protocol
ChromeDriver
2013
Historical Context
Chrome
DevTools
Protocol
Playwright
2013
Historical Context
ChromeDriver
WebDriver Integration
WebDriver-Grid
WebDriver-Grid
WebDriver-Grid
WebDriver Integration
Chrome
DevTools
Protocol
WebDriver-Grid
WebDriver-Grid
WebDriver-Grid
WebDriver Integration
chromium.connectOverCDP()
Chrome
DevTools
Protocol
🎭 Playwright
WebDriver-Grid
WebDriver-Grid
WebDriver-Grid
Act V
Delightful Authoring & Debugging
Dependable
Efficient
Capable
Ubiquitous
Delightful
Lively
🍿 Demo Time 🍿
Authoring & Debugging
- Code Generation
- npx playwright codegen
- Built-In Inspector
- PWDEBUG=1 node snippet.js
- Devtools Console integration
- playwright.$
- Simple Node.js Debugging
- Logging
- DEBUG=pw:api node snippet.js
- Storage State re-use
Act VI
Lively
Dependable
Efficient
Capable
Ubiquitous
Delightful
Lively
Lively 🐈
● Active Development
○ 2500+ commits in the last year
○ 1600+ closed issues
● Growing Team
○ We’re hiring!
● Expanding Horizons
const { _electron } = require('playwright');
(async () => {
const electronApp = await _electron.launch({ args: ['main.js'] });
const window = await electronApp.firstWindow();
console.log(await window.title());
await window.screenshot({ path: 'intro.png' });
await window.click('text=Click me');
await electronApp.close();
})();
🧪
E
x
p
e
r
i
m
e
n
t
a
l
:
E
l
e
c
t
r
o
n
.
j
s
const { _android } = require('playwright');
(async () => {
const [device] = await _android.devices();
await device.shell('am force-stop com.android.chrome');
const context = await device.launchBrowser();
const page = await context.newPage();
await page.goto('https://playwright.dev/');
await page.screenshot({ path: 'page.png' });
await context.close();
await device.close();
})();
🧪
E
x
p
e
r
i
m
e
n
t
a
l
:
A
n
d
r
o
i
d
Conclusion?
Dependable
Efficient
Capable
Ubiquitous
Delightful
Lively
Web Automation
can be
Fun
🎭 playwright.dev
@aslushnikov
aslushnikov@gmail.com
Andrey Lushnikov
@playwrightweb
https:/
/aka.ms/playwright-slack
microsoft/playwright
Playwright
@aslushnikov
aslushnikov@gmail.com
Andrey Lushnikov
@playwrightweb
https:/
/aka.ms/playwright-slack
microsoft/playwright
Q
u
e
s
t
i
o
n
s
?
Playwright
How-To: Authentication Re-use
Context 1
1. npx playwright codegen --save-storage=auth.json
How-To: Authentication Re-use
Context 1
Storage State
2. Extract & Save Storage State
1. npx playwright codegen --save-storage=auth.json
How-To: Authentication Re-use
Context 1 Context N
Storage State
2. Extract & Save Storage State
3. Re-Use Storage State
1. npx playwright codegen --save-storage=auth.json
🎁
Authentication Re-Use
Authentication is Slow
Authentication is Slow
Authentication is Slow
Authentication is Slow Too Slow!
🍿 Demo Time 🍿
const context = await browser.newContext({
storageState: require('auth.json'),
});
A
u
t
h
e
n
t
i
c
a
t
i
o
n
R
e
-
U
s
e
const context = await browser.newContext({
storageState: require('auth.json'),
});
A
u
t
h
e
n
t
i
c
a
t
i
o
n
R
e
-
U
s
e

Playwright: A New Test Automation Framework for the Modern Web

  • 1.
  • 2.
  • 3.
    ● 2009-2019 WebKit WebInspector Chrome Developer Tools Who We Are
  • 4.
    ● 2009-2019 WebKit WebInspector Chrome Developer Tools Who We Are
  • 5.
    ● 2009-2019 WebKit WebInspector Chrome Developer Tools Who We Are
  • 6.
    ● 2009-2019 WebKit WebInspector Chrome Developer Tools ● 2011-... Chrome DevTools Protocol Who We Are
  • 7.
    ● 2009-2019 WebKit WebInspector Chrome Developer Tools ● 2011-... Chrome DevTools Protocol ● 2015-2016 Node.js Debugger Who We Are
  • 8.
    ● 2009-2019 WebKit WebInspector Chrome Developer Tools ● 2011-... Chrome DevTools Protocol ● 2015-2016 Node.js Debugger ● 2017-2019 Puppeteer Who We Are
  • 9.
    ● 2009-2019 WebKit WebInspector Chrome Developer Tools ● 2011-... Chrome DevTools Protocol ● 2015-2016 Node.js Debugger ● 2017-2019 Puppeteer Who We Are
  • 10.
    ● 2009-2019 WebKit WebInspector Chrome Developer Tools ● 2011-... Chrome DevTools Protocol ● 2015-2016 Node.js Debugger ● 2017-2019 Puppeteer ● 2019-... Playwright Who We Are
  • 11.
  • 12.
  • 13.
    import {chromium, firefox,webkit} from 'playwright'; for (const browserType of [chromium, firefox, webkit]) { const browser = await browserType.launch(); const page = await browser.newPage(); await page.goto('https://playwright.dev'); await page.screenshot({path: `image-${browserType.name()}.png`}); await browser.close(); }
  • 14.
    import {chromium, firefox,webkit} from 'playwright'; for (const browserType of [chromium, firefox, webkit]) { const browser = await browserType.launch(); const page = await browser.newPage(); await page.goto('https://playwright.dev'); await page.screenshot({path: `image-${browserType.name()}.png`}); await browser.close(); } J a v a S c r i p t
  • 15.
    from playwright.sync_api importsync_playwright with sync_playwright() as p: for browser_type in [p.chromium, p.firefox, p.webkit]: browser = browser_type.launch() page = browser.new_page() page.goto("https://playwright.dev") page.screenshot(path="image-" + browser_type.name + ".png") browser.close() P y t h o n
  • 16.
    public static voidmain(String[] args) { try (Playwright playwright = Playwright.create()) { List<BrowserType> browserTypes = Arrays.asList( playwright.chromium(), playwright.webkit(), playwright.firefox() ); for (BrowserType browserType : browserTypes) { try (Browser browser = browserType.launch()) { Page page = browser.newPage(); page.navigate("http://playwright.dev"); page.screenshot(new Page.ScreenshotOptions() .setPath(Paths.get("image-" + browserType.name() + ".png"))); } } } } J a v a
  • 17.
  • 18.
    Playwright: ✅ dependable ✅ efficient ✅capable ✅ ubiquitous ✅ delightful ✅ lively
  • 19.
  • 20.
    Dependable 👌 We TriageIssues in <48 hours 📚 We Fix Issues (>1600 fixed) ✈ We Take Full Responsibility github.com/microsoft/playwright
  • 21.
  • 22.
  • 23.
    Level 1: BrowserEngineering C++
  • 24.
    - Chromium Development -Improving Chrome DevTools Protocol - Google Chrome, Microsoft Edge, Opera, etc Level 1: Browser Engineering C++
  • 25.
    - Chromium Development -Improving Chrome DevTools Protocol - Google Chrome, Microsoft Edge, Opera, etc - WebKit Development - Improving Web Inspector Protocol - Safari, Mobile Safari, Epiphany, etc Level 1: Browser Engineering C++
  • 26.
    - Chromium Development -Improving Chrome DevTools Protocol - Google Chrome, Microsoft Edge, Opera, etc - WebKit Development - Improving Web Inspector Protocol - Safari, Mobile Safari, Epiphany, etc - Firefox Development - Improving “Juggler” Protocol - Mozilla Firefox Level 1: Browser Engineering C++
  • 27.
    - Chromium Development -Improving Chrome DevTools Protocol - Google Chrome, Microsoft Edge, Opera, etc - WebKit Development - Improving Web Inspector Protocol - Safari, Mobile Safari, Epiphany, etc - Firefox Development - Improving “Juggler” Protocol - Mozilla Firefox Level 1: Browser Engineering C++
  • 28.
    Level 2: PlaywrightDriver Level 1: Browser Engineering C++
  • 29.
    TypeScript - Single webautomation protocol - Unify all remote debugging protocols - Expose “Downloads API” in driver Level 2: Playwright Driver 🎭 Playwright Driver Level 1: Browser Engineering C++
  • 30.
    TypeScript Level 3: LanguageBindings 🎭 Playwright Driver Level 1: Browser Engineering C++ Level 2: Playwright Driver
  • 31.
    TypeScript Level 3: LanguageBindings 🎭 Playwright Driver Level 1: Browser Engineering C++ Level 2: Playwright Driver Playwright Playwright for Java Playwright for Python Playwright for C# Expose language idiomatic API to control downloads
  • 32.
    TypeScript Level 3: LanguageBindings 🎭 Playwright Driver Level 1: Browser Engineering C++ Level 2: Playwright Driver Playwright Playwright for Java Playwright for Python Playwright for C# Expose language idiomatic API to control downloads
  • 33.
    TypeScript Level 3: LanguageBindings 🎭 Playwright Driver Level 1: Browser Engineering C++ Level 2: Playwright Driver Playwright Playwright for Java Playwright for Python Playwright for C# Expose language idiomatic API to control downloads
  • 34.
    TypeScript Being Dependable 🎭 PlaywrightDriver C++ Playwright Playwright for Java Playwright for Python Playwright for C# Cross-Browser Testing Tool ● 👀 8 teams from multiple companies ● 🐌 Slow feedback loop (months / years) Playwright ● 💫 1 team from a single company ● 🐆 Fast feedback loop (days / weeks)
  • 35.
    More Like This ●Downloads API ● Screencast API ● Drag & Drop API ● Clipboard API ● Browser-Level cookies ● Browser Contexts ● Per-context HTTP proxy ● ...
  • 36.
    Summary: Playwright isDependable 👌 We Triage Issues in <48 hours 📚 We Fix Issues (>1600 fixed) ✈ We Take Full Responsibility
  • 37.
  • 38.
  • 39.
    Automation Costs ● Throughput= spend less CPU cycles ● Latency = run tests fast ● Flakiness = reliable runs
  • 40.
    Playwright Being Efficient ●Trick 1: Browser Contexts ● Trick 2: Auto-waiting
  • 41.
    Trick 1: BrowserContexts ❌ Never Restart a Browser ● Slow instantiation (>100ms) ● Huge memory overhead ✅ Always create Browser Contexts ● Full isolation ● Fast instantiation (~1ms) ● Low overhead Browser Context
  • 42.
    Trick 1: BrowserContexts Context 1
  • 43.
    Trick 1: BrowserContexts Context 1 Context 2
  • 44.
    Trick 1: BrowserContexts Context 1 Context 2
  • 45.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await chromium.launch(); for (let i = 0; i < 10; ++i) { // Fast, simple, configurable! const context = await browser.newContext() const page = await context.newPage(); await page.goto('https://playwright.dev'); await context.close(); }
  • 46.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await chromium.launch(); for (let i = 0; i < 10; ++i) { // Fast, simple, configurable! const context = await browser.newContext() const page = await context.newPage(); await page.goto('https://playwright.dev'); await context.close(); }
  • 47.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await chromium.launch(); for (let i = 0; i < 10; ++i) { // Fast, simple, configurable! const context = await browser.newContext() const page = await context.newPage(); await page.goto('https://playwright.dev'); await context.close(); }
  • 48.
  • 49.
    Form Filling: NoAutowait e-mail..
  • 50.
    Form Filling: NoAutowait e-mail..
  • 51.
    Form Filling: NoAutowait e-mail..
  • 52.
    Form Filling: NoAutowait passwd...
  • 53.
    Form Filling: NoAutowait passwd...
  • 54.
    Form Filling: NoAutowait passwd...
  • 55.
    Form Filling: NoAutowait submitting...
  • 56.
    Form Filling: NoAutowait submitting...
  • 57.
    Form Filling: NoAutowait MISSED
  • 58.
    Form Filling: NoAutowait MISSED Disabled button!
  • 59.
    await page.goto('https://form.example.com'); // Shallwe wait for fields to be enabled?.. await page.fill('#email', 'aslushnikov@gmail.com'); await page.fill('#password', 'mypassword'); // Wait for button to get enabled and click submit setTimeout(async () => { await page.click('#submit'); }, 1000); N o n - P l a y w r i g h t P s e u d o - C o d e
  • 60.
    await page.goto('https://form.example.com'); // Shallwe wait for fields to be enabled?.. await page.fill('#email', 'aslushnikov@gmail.com'); await page.fill('#password', 'mypassword'); // Wait for button to get enabled and click submit setTimeout(async () => { await page.click('#submit'); }, 1000); N o n - P l a y w r i g h t P s e u d o - C o d e
  • 61.
    await page.goto('https://form.example.com'); // Shallwe wait for fields to be enabled?.. await page.fill('#email', 'aslushnikov@gmail.com'); await page.fill('#password', 'mypassword'); // Wait for button to get enabled and click submit setTimeout(async () => { await page.click('#submit'); }, 1000); N o n - P l a y w r i g h t P s e u d o - C o d e Time Does Not Exist in the Cloud!
  • 62.
    Trick 2: Auto-waiting✨ Auto-Waiting..
  • 63.
    Trick 2: Auto-waiting✨ Successful! Enabled!
  • 64.
  • 65.
  • 66.
    // ✨ Playwrightauto-waiting by default! await page.goto('https://form.example.com'); await page.fill('#email', 'aslushnikov@gmail.com'); await page.fill('#password', 'mypassword'); await page.click('#submit'); P l a y w r i g h t
  • 67.
    // ✨ Playwrightauto-waiting by default! await page.goto('https://form.example.com'); await page.fill('#email', 'aslushnikov@gmail.com'); await page.fill('#password', 'mypassword'); await page.click('#submit'); P l a y w r i g h t
  • 68.
    // ✨ Playwrightauto-waiting by default! await page.goto('https://form.example.com'); await page.fill('#email', 'aslushnikov@gmail.com'); await page.fill('#password', 'mypassword'); await page.click('#submit'); P l a y w r i g h t
  • 69.
    // ✨ Playwrightauto-waiting by default! await page.goto('https://form.example.com'); await page.fill('#email', 'aslushnikov@gmail.com'); await page.fill('#password', 'mypassword'); await page.click('#submit'); P l a y w r i g h t
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
    Summary: Playwright isEfficient ● Trick 1: Browser Contexts ● Trick 2: Auto-waiting
  • 78.
  • 79.
    import {webkit, devices}from 'playwright'; const browser = await webkit.launch(); const context = await browser.newContext({ ...devices['iPhone 12 Pro'], }); const page = await context.newPage(); await page.goto('https://playwright.dev'); await browser.close(); 📱 D e v i c e E m u l a t i o n
  • 80.
    import {webkit, devices}from 'playwright'; const browser = await webkit.launch(); const context = await browser.newContext({ ...devices['iPhone 12 Pro'], }); const page = await context.newPage(); await page.goto('https://playwright.dev'); await browser.close(); 📱 D e v i c e E m u l a t i o n
  • 81.
    import {webkit, devices}from 'playwright'; const browser = await webkit.launch(); const context = await browser.newContext({ ...devices['iPhone 12 Pro'], }); const page = await context.newPage(); await page.goto('https://playwright.dev'); await browser.close(); 📱 D e v i c e E m u l a t i o n
  • 82.
    import {chromium, firefox,webkit} from 'playwright'; const browser = await firefox.launch(); const context = await browser.newContext({ geolocation: { longitude: 48.858455, latitude: 2.294474 }, permissions: ['geolocation'] }); const page = await context.newPage(); await page.goto('https://playwright.dev'); await context.setGeolocation({ longitude: 29.97, latitude: 31.13 }); G e o l o c a t i o n & P e r m i s s i o n s
  • 83.
    import {chromium, firefox,webkit} from 'playwright'; const browser = await firefox.launch(); const context = await browser.newContext({ geolocation: { longitude: 48.858455, latitude: 2.294474 }, permissions: ['geolocation'] }); const page = await context.newPage(); await page.goto('https://playwright.dev'); await context.setGeolocation({ longitude: 29.97, latitude: 31.13 }); G e o l o c a t i o n & P e r m i s s i o n s
  • 84.
    import {chromium, firefox,webkit} from 'playwright'; const browser = await firefox.launch(); const context = await browser.newContext({ geolocation: { longitude: 48.858455, latitude: 2.294474 }, permissions: ['geolocation'] }); const page = await context.newPage(); await page.goto('https://playwright.dev'); await context.setGeolocation({ longitude: 29.97, latitude: 31.13 }); G e o l o c a t i o n & P e r m i s s i o n s
  • 85.
    import {chromium, firefox,webkit} from 'playwright'; const browser = await firefox.launch(); const context = await browser.newContext({ geolocation: { longitude: 48.858455, latitude: 2.294474 }, permissions: ['geolocation'] }); const page = await context.newPage(); await page.goto('https://playwright.dev'); await context.setGeolocation({ longitude: 29.97, latitude: 31.13 }); G e o l o c a t i o n & P e r m i s s i o n s
  • 86.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await chromium.launch(); const context = await browser.newContext({ recordVideo: { dir: 'videos/', size: { width: 800, height: 600 }, }, }); const page = await context.newPage(); await page.goto('https://playwright.dev'); await page.close(); 🎥 V i d e o s
  • 87.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await chromium.launch(); const context = await browser.newContext({ recordVideo: { dir: 'videos/', size: { width: 800, height: 600 }, }, }); const page = await context.newPage(); await page.goto('https://playwright.dev'); await page.close(); 🎥 V i d e o s
  • 88.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await chromium.launch(); const context = await browser.newContext({ recordVideo: { dir: 'videos/', size: { width: 800, height: 600 }, }, }); const page = await context.newPage(); await page.goto('https://playwright.dev'); await page.close(); 🎥 V i d e o s
  • 89.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await webkit.launch(); const page = await browser.newPage(); page.on('request', r => console.log(r.method(), r.url())); page.on('websocket', ws => console.log(ws.url())); await page.goto('https://playwright.dev'); await browser.close(); 🌎 N e t w o r k & W e b S o c k e t s
  • 90.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await webkit.launch(); const page = await browser.newPage(); page.on('request', r => console.log(r.method(), r.url())); page.on('websocket', ws => console.log(ws.url())); await page.goto('https://playwright.dev'); await browser.close(); 🌎 N e t w o r k & W e b S o c k e t s
  • 91.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await firefox.launch(); const page = await browser.newPage(); await page.route('**/*.{png,jpg,jpeg}', route => route.abort()); await page.goto('https://playwright.dev'); await browser.close(); P a g e R e q u e s t I n t e r c e p t i o n
  • 92.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await firefox.launch(); const context = await browser.newContext(); await context.route('**/*.{png,jpg,jpeg}', route => route.abort()); const page = await page.newPage(); await page.goto('https://playwright.dev'); await browser.close(); C o n t e x t R e q u e s t I n t e r c e p t i o n
  • 93.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await webkit.launch(); const page = await browser.newPage(); await page.goto('https://playwright.dev'); const [ download ] = await Promise.all([ page.waitForEvent('download'), page.click('button#delayed-download') ]); console.log(await download.path()); await browser.close(); D o w n l o a d s
  • 94.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await chromium.launch(); const page = await browser.newPage(); await page.goto('https://playwright.dev'); await page.click('button'); // CSS selector await page.click('xpath=//button'); // XPath selector await page.click('text=Log in'); // Text selector await page.click(':nth-match(:text("Buy"), 3)'); // N-th match await page.click('div:right-of(:text("Name"))'); // Layout 😱 await page.click(`xpath=//form >> text=Submit`); // Composite S m a r t S e l e c t o r s
  • 95.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await chromium.launch(); const page = await browser.newPage(); await page.goto('https://playwright.dev'); await page.click('button'); // CSS selector await page.click('xpath=//button'); // XPath selector await page.click('text=Log in'); // Text selector await page.click(':nth-match(:text("Buy"), 3)'); // N-th match await page.click('div:right-of(:text("Name"))'); // Layout 😱 await page.click(`xpath=//form >> text=Submit`); // Composite S m a r t S e l e c t o r s
  • 96.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await chromium.launch(); const page = await browser.newPage(); await page.goto('https://playwright.dev'); await page.click('button'); // CSS selector await page.click('xpath=//button'); // XPath selector await page.click('text=Log in'); // Text selector await page.click(':nth-match(:text("Buy"), 3)'); // N-th match await page.click('div:right-of(:text("Name"))'); // Layout 😱 await page.click(`xpath=//form >> text=Submit`); // Composite S m a r t S e l e c t o r s
  • 97.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await chromium.launch(); const page = await browser.newPage(); await page.goto('https://playwright.dev'); await page.click('button'); // CSS selector await page.click('xpath=//button'); // XPath selector await page.click('text=Log in'); // Text selector await page.click(':nth-match(:text("Buy"), 3)'); // N-th match await page.click('div:right-of(:text("Name"))'); // Layout 😱 await page.click(`xpath=//form >> text=Submit`); // Composite S m a r t S e l e c t o r s
  • 98.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await chromium.launch(); const page = await browser.newPage(); await page.goto('https://playwright.dev'); await page.click('button'); // CSS selector await page.click('xpath=//button'); // XPath selector await page.click('text=Log in'); // Text selector await page.click(':nth-match(:text("Buy"), 3)'); // N-th match await page.click('div:right-of(:text("Name"))'); // Layout 😱 await page.click(`xpath=//form >> text=Submit`); // Composite S m a r t S e l e c t o r s
  • 99.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await chromium.launch(); const page = await browser.newPage(); await page.goto('https://playwright.dev'); await page.click('button'); // CSS selector await page.click('xpath=//button'); // XPath selector await page.click('text=Log in'); // Text selector await page.click(':nth-match(:text("Buy"), 3)'); // N-th match await page.click('div:right-of(:text("Name"))'); // Layout 😱 await page.click(`xpath=//form >> text=Submit`); // Composite S m a r t S e l e c t o r s
  • 100.
    import {webkit, chromium,firefox} from 'playwright'; const browser = await chromium.launch(); const page = await browser.newPage(); await page.goto('https://playwright.dev'); await page.click('button'); // CSS selector await page.click('xpath=//button'); // XPath selector await page.click('text=Log in'); // Text selector await page.click(':nth-match(:text("Buy"), 3)'); // N-th match await page.click('div:right-of(:text("Name"))'); // Layout 😱 await page.click(`xpath=//form >> text=Submit`); // Composite S m a r t S e l e c t o r s
  • 101.
    // text content constcontent = await page.textContent('nav:first-child'); expect(content).toBe('home'); // Attributes const alt = await page.getAttribute('input', 'alt'); expect(alt).toBe('Text'); // Checkbox state const checked = await page.isChecked('input'); expect(checked).toBeTruthy(); // Visibility const visible = await page.isVisible('input'); expect(visible).toBeTruthy(); A s s e r t i o n s
  • 102.
    // text content constcontent = await page.textContent('nav:first-child'); expect(content).toBe('home'); // Attributes const alt = await page.getAttribute('input', 'alt'); expect(alt).toBe('Text'); // Checkbox state const checked = await page.isChecked('input'); expect(checked).toBeTruthy(); // Visibility const visible = await page.isVisible('input'); expect(visible).toBeTruthy(); A s s e r t i o n s
  • 103.
    // text content constcontent = await page.textContent('nav:first-child'); expect(content).toBe('home'); // Attributes const alt = await page.getAttribute('input', 'alt'); expect(alt).toBe('Text'); // Checkbox state const checked = await page.isChecked('input'); expect(checked).toBeTruthy(); // Visibility const visible = await page.isVisible('input'); expect(visible).toBeTruthy(); A s s e r t i o n s
  • 104.
    // text content constcontent = await page.textContent('nav:first-child'); expect(content).toBe('home'); // Attributes const alt = await page.getAttribute('input', 'alt'); expect(alt).toBe('Text'); // Checkbox state const checked = await page.isChecked('input'); expect(checked).toBeTruthy(); // Visibility const visible = await page.isVisible('input'); expect(visible).toBeTruthy(); A s s e r t i o n s
  • 105.
    import {chromium} from'playwright'; const browser = await chromium.launch({ // Can be 'chrome', 'chrome-beta', 'msedge-beta', 'msedge-dev' channel: 'msedge', }); const page = await context.newPage(); await page.goto('https://playwright.dev'); await browser.close(); M i c r o s o f t E d g e
  • 106.
    import {chromium} from'playwright'; const browser = await chromium.launch({ // Can be 'chrome', 'chrome-beta', 'msedge-beta', 'msedge-dev' channel: 'msedge', }); const page = await context.newPage(); await page.goto('https://playwright.dev'); await browser.close(); M i c r o s o f t E d g e
  • 107.
    More Features ● Built-inShadow DOM piercing ● Idiomatic frames API ● Custom Selector Engines ● TimeZone / Locale / Color Scheme emulation ● Per-Context HTTP Proxy ● Workers / Service Workers ● Console sniffing ● Dialogs ● File Uploads ● Element Handles ● ...
  • 108.
    ● ubuntu-18.04 (chromium) ●ubuntu-18.04 (firefox) ● ubuntu-18.04 (webkit) ● ubuntu-20.04 (chromium) ● ubuntu-20.04 (firefox) ● ubuntu-20.04 (webkit) ● macos-10.14 (chromium) ● macos-10.14 (firefox) ● macos-10.14 (webkit) ● macos-10.15 (chromium) ● macos-10.15 (firefox) ● macos-10.15 (webkit) ● macos-11.0 (chromium) ● macos-11.0 (firefox) ● macos-11.0 (webkit) ● macos-11.0 arm64 (chromium) ● macos-11.0 arm64 (firefox) ● macos-11.0 arm64 (webkit) Rigorous Testing ● Windows (chromium) ● Windows (firefox) ● Windows (webkit) ● test-package-installations (^10.17.0) ● test-package-installations (^12.0.0) ● test-package-installations (^14.1.0) ● Headful Linux (chromium) ● Headful Linux (firefox) ● Headful Linux (webkit) ● Transport (driver) ● Transport (service) ● Video Linux (chromium) ● Video Linux (firefox) ● Video Linux (webkit) ● Android Emulator (shard 1) ● Android Emulator (shard 2) ● Chrome Stable (Linux) ● Chrome Stable (Win) ● Chrome Stable (Mac) ● Edge Stable (Win) ● Electron Linux
  • 109.
  • 110.
  • 111.
  • 112.
    🎭 Playwright WebKitAvailability Headed Headless ✅ ✅ ✅ ✅ ✅ ✅
  • 113.
    ● All Browsers ○Chrome, Safari, Firefox 🎭 Playwright is Ubiquitous
  • 114.
    ● All Browsers ○Chrome, Safari, Firefox ● All OS ○ Linux, Mac, Windows 🎭 Playwright is Ubiquitous
  • 115.
    🎭 Playwright isUbiquitous ● All Browsers ○ Chrome, Safari, Firefox ● All OS ○ Linux, Mac, Windows ● All Popular Languages ○ JavaScript / TypeScript ○ Java ○ Python ○ C# (alpha)
  • 116.
    CI/CD, Services, Clients ●Any CI/CD ○ Github Actions, Travis CI, Azure Pipelines, Jenkins, Circle CI, AWS, GCP, …
  • 117.
    ● Any CI/CD ○Github Actions, Travis CI, Azure Pipelines, Jenkins, Circle CI, AWS, GCP, … ● Docker Containers ○ docker pull mcr.microsoft.com/playwright ○ docker pull mcr.microsoft.com/playwright-java CI/CD, Services, Clients
  • 118.
    CI/CD, Services, Clients ●Any CI/CD ○ Github Actions, Travis CI, Azure Pipelines, Jenkins, Circle CI, AWS, GCP, … ● Docker Containers ○ docker pull mcr.microsoft.com/playwright ○ docker pull mcr.microsoft.com/playwright-java ● Third-Party integrations ○ applitools.com ○ saucelabs.com ○ Checklyhq.com ○ www.testim.io ○ github.com/aerokube/moon ○ seleniumbox.com
  • 119.
    CI/CD, Services, Clients ●Any CI/CD ○ Github Actions, Travis CI, Azure Pipelines, Jenkins, Circle CI, AWS, GCP, … ● Docker Containers ○ docker pull mcr.microsoft.com/playwright ○ docker pull mcr.microsoft.com/playwright-java ● Third-Party integrations ○ applitools.com ○ saucelabs.com ○ Checklyhq.com ○ www.testim.io ○ github.com/aerokube/moon ○ seleniumbox.com
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
    Act V Delightful Authoring& Debugging Dependable Efficient Capable Ubiquitous Delightful Lively
  • 128.
  • 129.
    Authoring & Debugging -Code Generation - npx playwright codegen - Built-In Inspector - PWDEBUG=1 node snippet.js - Devtools Console integration - playwright.$ - Simple Node.js Debugging - Logging - DEBUG=pw:api node snippet.js - Storage State re-use
  • 130.
  • 131.
    Lively 🐈 ● ActiveDevelopment ○ 2500+ commits in the last year ○ 1600+ closed issues ● Growing Team ○ We’re hiring! ● Expanding Horizons
  • 132.
    const { _electron} = require('playwright'); (async () => { const electronApp = await _electron.launch({ args: ['main.js'] }); const window = await electronApp.firstWindow(); console.log(await window.title()); await window.screenshot({ path: 'intro.png' }); await window.click('text=Click me'); await electronApp.close(); })(); 🧪 E x p e r i m e n t a l : E l e c t r o n . j s
  • 133.
    const { _android} = require('playwright'); (async () => { const [device] = await _android.devices(); await device.shell('am force-stop com.android.chrome'); const context = await device.launchBrowser(); const page = await context.newPage(); await page.goto('https://playwright.dev/'); await page.screenshot({ path: 'page.png' }); await context.close(); await device.close(); })(); 🧪 E x p e r i m e n t a l : A n d r o i d
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 140.
    How-To: Authentication Re-use Context1 1. npx playwright codegen --save-storage=auth.json
  • 141.
    How-To: Authentication Re-use Context1 Storage State 2. Extract & Save Storage State 1. npx playwright codegen --save-storage=auth.json
  • 142.
    How-To: Authentication Re-use Context1 Context N Storage State 2. Extract & Save Storage State 3. Re-Use Storage State 1. npx playwright codegen --save-storage=auth.json
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
    const context =await browser.newContext({ storageState: require('auth.json'), }); A u t h e n t i c a t i o n R e - U s e
  • 150.
    const context =await browser.newContext({ storageState: require('auth.json'), }); A u t h e n t i c a t i o n R e - U s e