Skip to content

Commit c50e7a5

Browse files
committed
Adds Playwright documentation
1 parent 8b89f1e commit c50e7a5

File tree

1 file changed

+288
-0
lines changed

1 file changed

+288
-0
lines changed
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
---
2+
pcx_content_type: concept
3+
title: Playwright
4+
description: Learn how to use Playwright with Cloudflare Workers for browser automation. Access Playwright API, manage sessions, and optimize browser rendering.
5+
sidebar:
6+
order: 5
7+
badge: Beta
8+
---
9+
10+
import { Render, WranglerConfig, TabItem, Tabs } from "~/components";
11+
12+
[Playwright](https://playwright.dev/) is an open-source package developer by Microsoft that can do browser automation tasks; it's commonly used to write frontend tests, create screenshots, or crawl pages.
13+
14+
The Workers team forked a [version of Playwright](https://github.com/cloudflare/playwright) that was modified to be compatible with [Cloudflare Workers](https://developers.cloudflare.com/workers/) and [Browser Rendering](https://developers.cloudflare.com/browser-rendering/).
15+
16+
Our version is open sourced and can be found in [Cloudflare's fork of Playwright](https://github.com/cloudflare/playwright). The npm can be installed from [npmjs](https://www.npmjs.com/) as [@cloudflare/playwright](https://www.npmjs.com/package/@cloudflare/playwright):
17+
18+
```bash
19+
npm install @cloudflare/playwright --save-dev
20+
```
21+
22+
## Use Playwright in a Worker
23+
24+
Make sure you have the [browser binding](/browser-rendering/platform/wrangler/#bindings) configured in your `wrangler.toml` file:
25+
26+
<WranglerConfig>
27+
28+
```toml
29+
name = "cloudflare-playwright-example"
30+
main = "src/index.ts"
31+
workers_dev = true
32+
compatibility_flags = ["nodejs_compat_v2"]
33+
compatibility_date = "2025-03-05"
34+
upload_source_maps = true
35+
36+
[dev]
37+
port = 9000
38+
39+
[browser]
40+
binding = "MYBROWSER"
41+
```
42+
43+
</WranglerConfig>
44+
45+
Install the npm package:
46+
47+
```bash
48+
npm install --save-dev @cloudflare/playwright
49+
```
50+
51+
Let's look at some examples of how to use Playwright:
52+
53+
### Take a screenshot
54+
55+
Using browser automation to take screenshots of web pages is a common use case. This script tells the browser to navigate to https://demo.playwright.dev/todomvc, create some items, take a screenshot of the page, and return the image in the response.
56+
57+
```ts
58+
import { launch } from '@cloudflare/playwright';
59+
60+
const todos = searchParams.getAll('todo');
61+
62+
const browser = await launch(env.MYBROWSER);
63+
const page = await browser.newPage();
64+
65+
await page.goto('https://demo.playwright.dev/todomvc');
66+
67+
const TODO_ITEMS = todos.length > 0 ? todos : [
68+
'buy some cheese',
69+
'feed the cat',
70+
'book a doctors appointment'
71+
];
72+
73+
const newTodo = page.getByPlaceholder('What needs to be done?');
74+
for (const item of TODO_ITEMS) {
75+
await newTodo.fill(item);
76+
await newTodo.press('Enter');
77+
}
78+
79+
const img = await page.screenshot();
80+
await browser.close();
81+
82+
return new Response(img, {
83+
headers: {
84+
'Content-Type': 'image/png',
85+
},
86+
});
87+
```
88+
89+
### Trace
90+
91+
A Playwright trace is a detailed log of your workflow execution that captures information like user clicks and navigation actions, screenshots of the page, and any console messages generated and used for debugging. Developers can take a `trace.zip` file and either open it [locally](https://playwright.dev/docs/trace-viewer#opening-the-trace) or upload it to the [Playwright Trace Viewer](https://trace.playwright.dev/), a GUI tool that helps you explore the data.
92+
93+
Here's an example of a worker generating a trace file:
94+
95+
```ts
96+
import type { Fetcher } from '@cloudflare/workers-types';
97+
import { launch, fs } from "@cloudflare/playwright";
98+
import { expect } from "@cloudflare/playwright/test";
99+
100+
interface Env {
101+
MYBROWSER: Fetcher;
102+
}
103+
104+
export default {
105+
async fetch(request: Request, env: Env) {
106+
const { searchParams } = new URL(request.url);
107+
const todos = searchParams.getAll('todo');
108+
const trace = searchParams.has('trace');
109+
110+
const browser = await launch(env.MYBROWSER);
111+
const page = await browser.newPage();
112+
113+
if (trace) await page.context().tracing.start({ screenshots: true, snapshots: true });
114+
115+
await page.goto('https://demo.playwright.dev/todomvc');
116+
117+
const TODO_ITEMS = todos.length > 0 ? todos : [
118+
'buy some cheese',
119+
'feed the cat',
120+
'book a doctors appointment'
121+
];
122+
123+
const newTodo = page.getByPlaceholder('What needs to be done?');
124+
for (const item of TODO_ITEMS) {
125+
await newTodo.fill(item);
126+
await newTodo.press('Enter');
127+
}
128+
129+
await expect(page.getByTestId('todo-title')).toHaveCount(TODO_ITEMS.length);
130+
131+
await Promise.all(TODO_ITEMS.map(
132+
(value, index) => expect(page.getByTestId('todo-title').nth(index)).toHaveText(value)
133+
));
134+
135+
if (trace) {
136+
await page.context().tracing.stop({ path: 'trace.zip' });
137+
await browser.close();
138+
const file = await fs.promises.readFile('trace.zip');
139+
140+
return new Response(file, {
141+
status: 200,
142+
headers: {
143+
'Content-Type': 'application/zip',
144+
},
145+
});
146+
} else {
147+
const img = await page.screenshot();
148+
await browser.close();
149+
150+
return new Response(img, {
151+
headers: {
152+
'Content-Type': 'image/png',
153+
},
154+
});
155+
}
156+
},
157+
};
158+
```
159+
160+
### Assertions
161+
162+
One of the most common use cases for using Playwright is software testing. Playwright includes test assertion features in its APIs; see [this page](https://playwright.dev/docs/test-assertions) for detailed documentation. Here's an example of a Worker doing `expect()` test assertions of the [todomvc](https://demo.playwright.dev/todomvc) demo page:
163+
164+
```ts
165+
import { launch } from '@cloudflare/playwright';
166+
import { expect } from '@cloudflare/playwright/test';
167+
168+
const browser = await launch(env.MYBROWSER);
169+
const page = await browser.newPage();
170+
171+
await page.goto('https://demo.playwright.dev/todomvc');
172+
173+
const TODO_ITEMS = todos.length > 0 ? todos : [
174+
'buy some cheese',
175+
'feed the cat',
176+
'book a doctors appointment'
177+
];
178+
179+
const newTodo = page.getByPlaceholder('What needs to be done?');
180+
for (const item of TODO_ITEMS) {
181+
await newTodo.fill(item);
182+
await newTodo.press('Enter');
183+
}
184+
185+
await expect(page.getByTestId('todo-title')).toHaveCount(TODO_ITEMS.length);
186+
187+
await Promise.all(TODO_ITEMS.map(
188+
(value, index) => expect(page.getByTestId('todo-title').nth(index)).toHaveText(value)
189+
));
190+
```
191+
192+
### Keep Alive
193+
194+
If users omit the `browser.close()` statement, the browser instance will stay open, ready to be connected to again and [re-used](/browser-rendering/workers-binding-api/reuse-sessions/) but it will, by default, close automatically after 1 minute of inactivity. Users can optionally extend this idle time up to 10 minutes, by using the `keep_alive` option, set in milliseconds:
195+
196+
```js
197+
const browser = await puppeteer.launch(env.MYBROWSER, { keep_alive: 600000 });
198+
```
199+
200+
Using the above, the browser will stay open for up to 10 minutes, even if inactive.
201+
202+
## Session management
203+
204+
In order to facilitate browser session management, we've extended the Playwright API with new methods:
205+
206+
### List open sessions
207+
208+
`playwright.sessions()` lists the current running sessions. It will return an output similar to this:
209+
210+
```json
211+
[
212+
{
213+
"connectionId": "2a2246fa-e234-4dc1-8433-87e6cee80145",
214+
"connectionStartTime": 1711621704607,
215+
"sessionId": "478f4d7d-e943-40f6-a414-837d3736a1dc",
216+
"startTime": 1711621703708
217+
},
218+
{
219+
"sessionId": "565e05fb-4d2a-402b-869b-5b65b1381db7",
220+
"startTime": 1711621703808
221+
}
222+
]
223+
```
224+
225+
Notice that the session `478f4d7d-e943-40f6-a414-837d3736a1dc` has an active worker connection (`connectionId=2a2246fa-e234-4dc1-8433-87e6cee80145`), while session `565e05fb-4d2a-402b-869b-5b65b1381db7` is free. While a connection is active, no other workers may connect to that session.
226+
227+
### List recent sessions
228+
229+
`playwright.history()` lists recent sessions, both open and closed. It's useful to get a sense of your current usage.
230+
231+
```json
232+
[
233+
{
234+
"closeReason": 2,
235+
"closeReasonText": "BrowserIdle",
236+
"endTime": 1711621769485,
237+
"sessionId": "478f4d7d-e943-40f6-a414-837d3736a1dc",
238+
"startTime": 1711621703708
239+
},
240+
{
241+
"closeReason": 1,
242+
"closeReasonText": "NormalClosure",
243+
"endTime": 1711123501771,
244+
"sessionId": "2be00a21-9fb6-4bb2-9861-8cd48e40e771",
245+
"startTime": 1711123430918
246+
}
247+
]
248+
```
249+
250+
Session `2be00a21-9fb6-4bb2-9861-8cd48e40e771` was closed explicitly with `browser.close()` by the client, while session `478f4d7d-e943-40f6-a414-837d3736a1dc` was closed due to reaching the maximum idle time (check [limits](/browser-rendering/platform/limits/)).
251+
252+
You should also be able to access this information in the dashboard, albeit with a slight delay.
253+
254+
### Active limits
255+
256+
`playwright.limits()` lists your active limits:
257+
258+
```json
259+
{
260+
"activeSessions": [
261+
"478f4d7d-e943-40f6-a414-837d3736a1dc",
262+
"565e05fb-4d2a-402b-869b-5b65b1381db7"
263+
],
264+
"allowedBrowserAcquisitions": 1,
265+
"maxConcurrentSessions": 2,
266+
"timeUntilNextAllowedBrowserAcquisition": 0
267+
}
268+
```
269+
270+
- `activeSessions` lists the IDs of the current open sessions
271+
- `maxConcurrentSessions` defines how many browsers can be open at the same time
272+
- `allowedBrowserAcquisitions` specifies if a new browser session can be opened according to the rate [limits](/browser-rendering/platform/limits/) in place
273+
- `timeUntilNextAllowedBrowserAcquisition` defines the waiting period before a new browser can be launched.
274+
275+
## Playwright API
276+
277+
The full Playwright API can be found [here](https://playwright.dev/docs/api/class-playwright).
278+
279+
Note that `@cloudflare/playwright` is in beta. The following capabilities are not yet fully supported, but we’re actively working on them:
280+
281+
- [API Testing](https://playwright.dev/docs/api-testing)
282+
- [Playwright Test](https://playwright.dev/docs/test-configuration) except [Assertions](https://playwright.dev/docs/test-assertions)
283+
- [Components](https://playwright.dev/docs/test-components)
284+
- [Firefox](https://playwright.dev/docs/api/class-playwright#playwright-firefox), [Android](https://playwright.dev/docs/api/class-android) and [Electron](https://playwright.dev/docs/api/class-electron), as well as different versions of Chrome
285+
- [Network](https://playwright.dev/docs/next/network#network)
286+
- [Videos](https://playwright.dev/docs/next/videos)
287+
288+
This is **not an exhaustive list** — expect rapid changes as we work toward broader parity with the original feature set. You can also check [latest test results](https://playwright-full-test-report.pages.dev/) for a granular up to date list of the features that are fully supported.

0 commit comments

Comments
 (0)