INTEGRITY Документация

Playwright

Playwright ↗ это open source пакет, разработанный Microsoft, который умеет выполнять задачи автоматизации браузера. Его обычно используют для написания фронтенд-тестов, создания скриншотов или обхода страниц.

Команда Workers создала форк версия Playwright ↗ который был изменён для совместимости с Cloudflare Workers и Browser Run.

Наша версия имеет открытый исходный код, её можно найти в форк Playwright от Cloudflare ↗. Пакет npm можно установить из npmjs ↗ как @cloudflare/playwright ↗:

npm i -D @cloudflare/playwright

Используйте Playwright в Worker

В этом пример ↗, вы будете запускать тесты Playwright в Cloudflare Worker с помощью todomvc ↗ приложение.

Если вы хотите пропустить шаги и быстро начать, выберите Deploy to Cloudflare ниже.

Deploy to Cloudflare

Убедитесь, что у вас есть привязка browser настроенный в конфигурационном файле Wrangler:

{
	"$schema": "./node_modules/wrangler/config-schema.json",
	"name": "cloudflare-playwright-example",
	"main": "src/index.ts",
	"workers_dev": true,
	"compatibility_flags": ["nodejs_compat"],
	// Set this to today's date
	"compatibility_date": "2026-08-28",
	"upload_source_maps": true,
	"browser": {
		"binding": "MYBROWSER",
	},
}
"$schema" = "./node_modules/wrangler/config-schema.json"
name = "cloudflare-playwright-example"
main = "src/index.ts"
workers_dev = true
compatibility_flags = [ "nodejs_compat" ]
# Set this to today's date
compatibility_date = "2026-08-28"
upload_source_maps = true

[browser]
binding = "MYBROWSER"

Установите npm-пакет:

npm i -D @cloudflare/playwright

Рассмотрим несколько примеров использования Playwright:

Сделайте скриншот

Автоматизация браузера часто применяется для создания снимков веб-страниц. Этот скрипт указывает браузеру перейти на https://demo.playwright.dev/todomvc ↗, создайте несколько элементов, сделайте снимок страницы и верните изображение в ответе.

import { launch } from "@cloudflare/playwright";

export default {
	async fetch(request: Request, env: Env) {
		const browser = await launch(env.MYBROWSER);
		const page = await browser.newPage();

		await page.goto("https://demo.playwright.dev/todomvc");

		const TODO_ITEMS = [
			"buy some cheese",
			"feed the cat",
			"book a doctors appointment",
		];

		const newTodo = page.getByPlaceholder("What needs to be done?");
		for (const item of TODO_ITEMS) {
			await newTodo.fill(item);
			await newTodo.press("Enter");
		}

		const img = await page.screenshot();
		await browser.close();

		return new Response(img, {
			headers: {
				"Content-Type": "image/png",
			},
		});
	},
};

Trace

Трассировка Playwright представляет собой подробный журнал выполнения вашего сценария: она фиксирует такие данные, как клики пользователя и переходы по страницам, скриншоты страницы, а также все сообщения консоли, используемые для отладки. Разработчики могут снять trace.zip файл и либо откройте его локально ↗ или загрузите его в Playwright Trace Viewer ↗, GUI-инструмент, который помогает изучать данные.

Пример worker, который генерирует файл трассировки:

import fs from "fs";
import { launch } from "@cloudflare/playwright";

export default {
	async fetch(request: Request, env: Env) {
		const browser = await launch(env.MYBROWSER);
		const page = await browser.newPage();

		// Start tracing before navigating to the page
		await page.context().tracing.start({ screenshots: true, snapshots: true });

		await page.goto("https://demo.playwright.dev/todomvc");

		const TODO_ITEMS = [
			"buy some cheese",
			"feed the cat",
			"book a doctors appointment",
		];

		const newTodo = page.getByPlaceholder("What needs to be done?");
		for (const item of TODO_ITEMS) {
			await newTodo.fill(item);
			await newTodo.press("Enter");
		}

		// Stop tracing and save the trace to a zip file
		await page.context().tracing.stop({ path: "trace.zip" });
		await browser.close();
		const file = await fs.promises.readFile("trace.zip");

		return new Response(file, {
			status: 200,
			headers: {
				"Content-Type": "application/zip",
			},
		});
	},
};

Утверждения

Один из самых распространённых сценариев использования Playwright: тестирование программного обеспечения. API Playwright включает функции проверки утверждений (assertions), подробнее см. Утверждения ↗ в документации Playwright. Вот пример Worker, который выполняет expect() тестовые утверждения todomvc ↗ демонстрационную страницу:

import { launch } from "@cloudflare/playwright";
import { expect } from "@cloudflare/playwright/test";

export default {
	async fetch(request: Request, env: Env) {
		const browser = await launch(env.MYBROWSER);
		const page = await browser.newPage();

		await page.goto("https://demo.playwright.dev/todomvc");

		const TODO_ITEMS = [
			"buy some cheese",
			"feed the cat",
			"book a doctors appointment",
		];

		const newTodo = page.getByPlaceholder("What needs to be done?");
		for (const item of TODO_ITEMS) {
			await newTodo.fill(item);
			await newTodo.press("Enter");
		}

		await expect(page.getByTestId("todo-title")).toHaveCount(TODO_ITEMS.length);

		await Promise.all(
			TODO_ITEMS.map((value, index) =>
				expect(page.getByTestId("todo-title").nth(index)).toHaveText(value),
			),
		);
	},
};

Storage state

Playwright поддерживает состояние хранилища ↗ чтобы получить и сохранить cookie-файлы и другие данные хранилища. В этом примере вы будете использовать состояние хранилища, чтобы сохранять cookie-файлы и другие данные хранилища в Workers KV.

Сначала убедитесь, что у вас есть пространство имён KV. Создать новое можно с помощью команды:

npx wrangler kv namespace create KV

Затем добавьте пространство имён KV в файл конфигурации Wrangler:

{
	"name": "storage-state-examples",
	"main": "src/index.ts",
	"compatibility_flags": ["nodejs_compat"],
	// Set this to today's date
	"compatibility_date": "2026-08-28",
	"browser": {
		"binding": "MYBROWSER",
	},
	"kv_namespaces": [
		{
			"binding": "KV",
			"id": "<YOUR-KV-NAMESPACE-ID>",
		},
	],
}
name = "storage-state-examples"
main = "src/index.ts"
compatibility_flags = [ "nodejs_compat" ]
# Set this to today's date
compatibility_date = "2026-08-28"

[browser]
binding = "MYBROWSER"

[[kv_namespaces]]
binding = "KV"
id = "<YOUR-KV-NAMESPACE-ID>"

Теперь вы можете использовать storage state для сохранения файлов cookie и других данных хранилища в KV:

src/index.ts
// gets persisted storage state from KV or undefined if it does not exist
const storageStateJson = await env.KV.get("storageState");
const storageState = storageStateJson
	? ((await JSON.parse(
			storageStateJson,
		)) as BrowserContextOptions["storageState"])
	: undefined;

await using browser = await launch(env.MYBROWSER);
// creates a new context with storage state persisted in KV
await using context = await browser.newContext({ storageState });

await using page = await context.newPage();

// do some actions on the page that may update client-side storage

// gets updated storage state: cookies, localStorage, and IndexedDB
const updatedStorageState = await context.storageState({ indexedDB: true });

// persists updated storage state in KV
await env.KV.put("storageState", JSON.stringify(updatedStorageState));

Keep Alive

Если пользователи не указывают browser.close() то экземпляр браузера останется открытым, готовым к повторному подключению, а также повторно используется но по умолчанию она автоматически закрывается через 1 минуту бездействия. Пользователи могут по желанию продлить время простоя до 10 минут с помощью keep_alive параметр, заданный в миллисекундах:

const browser = await playwright.launch(env.MYBROWSER, { keep_alive: 600000 });

При таком подходе браузер остаётся открытым до 10 минут, даже если он неактивен.

Повторное использование сессии

Чтобы повысить производительность Worker для Browser Run, лучше всего повторно использовать сессии: не закрывайте браузер после завершения работы, а подключайтесь к той же сессии при каждом новом запросе. Playwright обрабатывает browser.close ↗ иначе, чем Puppeteer. В Playwright, если браузер был получен с помощью connect сессии, сессия отключится. Если браузер был получен с помощью launch сессии, сессия закроется.

import { env } from "cloudflare:workers";
import { acquire, connect } from "@cloudflare/playwright";

async function reuseSameSession() {
	// acquires a new session
	const { sessionId } = await acquire(env.BROWSER);

	for (let i = 0; i < 5; i++) {
		// connects to the session that was previously acquired
		const browser = await connect(env.BROWSER, sessionId);

		// ...

		// this will disconnect the browser from the session, but the session will be kept alive
		await browser.close();
	}
}

Задайте собственный User-Agent

Чтобы указать собственный user agent в Playwright, задайте его в опциях при создании нового browser context с помощью browser.newContext(). Все страницы, созданные впоследствии в этом контексте, будут использовать новый user agent. Это полезно, если целевой сайт показывает разное содержимое в зависимости от user agent.

const context = await browser.newContext({
	userAgent:
		"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
});

Локальная отладка в headful-режиме (экспериментально)

При локальной разработке с wrangler dev или vite dev, Chrome по умолчанию запускается в режиме headless. Чтобы запустить Chrome в видимом режиме (headful), задайте X_BROWSER_HEADFUL переменную окружения:

X_BROWSER_HEADFUL=true npx wrangler dev

Или с помощью Cloudflare Vite plugin:

X_BROWSER_HEADFUL=true npx vite dev

Это открывает окно браузера, чтобы вы могли наблюдать за работой автоматизации Playwright в режиме реального времени, что упрощает отладку навигации, выбора элементов и взаимодействия со страницей.

Управление сессиями

Чтобы упростить управление сессиями браузера, мы расширили Playwright API новыми методами:

Список открытых сеансов

playwright.sessions() выводит список текущих выполняющихся сессий. Результат будет выглядеть примерно так:

[
	{
		"connectionId": "2a2246fa-e234-4dc1-8433-87e6cee80145",
		"connectionStartTime": 1711621704607,
		"sessionId": "478f4d7d-e943-40f6-a414-837d3736a1dc",
		"startTime": 1711621703708
	},
	{
		"sessionId": "565e05fb-4d2a-402b-869b-5b65b1381db7",
		"startTime": 1711621703808
	}
]

Обратите внимание, что сессия 478f4d7d-e943-40f6-a414-837d3736a1dc есть активное подключение Worker (connectionId=2a2246fa-e234-4dc1-8433-87e6cee80145), в то время как сеанс 565e05fb-4d2a-402b-869b-5b65b1381db7 бесплатен. Пока соединение активно, другие Worker не смогут подключиться к этой сессии.

Список последних сеансов

playwright.history() выводит список недавних сессий, как открытых, так и закрытых. Это удобно, чтобы оценить текущий объём использования.

[
	{
		"closeReason": 2,
		"closeReasonText": "BrowserIdle",
		"endTime": 1711621769485,
		"sessionId": "478f4d7d-e943-40f6-a414-837d3736a1dc",
		"startTime": 1711621703708
	},
	{
		"closeReason": 1,
		"closeReasonText": "NormalClosure",
		"endTime": 1711123501771,
		"sessionId": "2be00a21-9fb6-4bb2-9861-8cd48e40e771",
		"startTime": 1711123430918
	}
]

Сессия 2be00a21-9fb6-4bb2-9861-8cd48e40e771 была закрыта явным образом с помощью browser.close() клиентом, тогда как сессия 478f4d7d-e943-40f6-a414-837d3736a1dc была закрыта из-за достижения максимального времени простоя (см. ограничения).

Эту информацию можно также посмотреть в панели управления, хотя и с небольшой задержкой.

Действующие лимиты

playwright.limits() выводит ваши текущие лимиты:

{
	"activeSessions": [
		{ "id": "478f4d7d-e943-40f6-a414-837d3736a1dc" },
		{ "id": "565e05fb-4d2a-402b-869b-5b65b1381db7" }
	],
	"allowedBrowserAcquisitions": 1,
	"maxConcurrentSessions": 2,
	"timeUntilNextAllowedBrowserAcquisition": 0
}

Playwright API

Полный API Playwright можно найти по адресу Документация Playwright API ↗.

Перечисленные ниже возможности пока поддерживаются не полностью, но мы активно работаем над ними:

Это неполный список : ожидайте быстрых изменений по мере того, как мы стремимся к более полному паритету с оригинальным набором функций. Также вы можете посмотреть последние результаты тестов ↗ для получения подробного и актуального списка полностью поддерживаемых функций.