Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
title: Run Vitest Browser Mode tests on Browser Run
description: Use the Browser Run Vitest provider to run Vitest Browser Mode test suites in hosted Chromium over CDP.
products:
- browser-run
date: 2026-07-03
---

You can now run [Vitest Browser Mode](https://vitest.dev/guide/browser/) test suites on [Browser Run](/browser-run/) hosted Chromium using `@cloudflare/vitest-browser-run-provider`. The provider connects Vitest's Playwright browser provider to Browser Run over the [Chrome DevTools Protocol (CDP)](/browser-run/cdp/), so browser tests can run against a managed Chromium environment from local development or CI.

Use this for tests that need browser APIs, user interactions, screenshots, or visual regression. For tests that need Workers runtime APIs or bindings, continue to use the [Workers Vitest integration](/workers/testing/vitest-integration/).

Here is a minimal Browser Run provider configuration:

```ts title="vitest.browser-run.config.ts"
import { cloudflare } from "@cloudflare/vite-plugin";
import { browserRunCdp } from "@cloudflare/vitest-browser-run-provider";
import { defineConfig } from "vitest/config";

export default defineConfig({
plugins: [cloudflare({ tunnel: { autoStart: true } })],
test: {
browser: {
enabled: true,
headless: true,
provider: browserRunCdp(),
instances: [{ browser: "chromium" }],
},
},
});
```

Before running tests, set `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN`. The token needs `Browser Rendering - Edit` permission.

To get started, refer to [Using with Vitest Browser Mode (CDP)](/browser-run/cdp/vitest-browser-mode/).
6 changes: 3 additions & 3 deletions src/content/docs/browser-run/cdp/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Render } from "~/components";

The `/devtools` endpoints provide session management capabilities that follow the [Chrome DevTools Protocol (CDP)](https://chromedevtools.github.io/devtools-protocol/). These endpoints allow you to create persistent browser sessions, manage multiple tabs, and interact with browsers using CDP commands. This is useful for advanced automation, debugging, and remote browser control.

CDP endpoints can be accessed from any environment that supports WebSocket connections, including local development machines, external servers, and CI/CD pipelines. This means you can connect to Browser Run from Node.js scripts, Puppeteer, Playwright, or any CDP-compatible client.
CDP endpoints can be accessed from any environment that supports WebSocket connections, including local development machines, external servers, and CI/CD pipelines. This means you can connect to Browser Run from Node.js scripts, Puppeteer, Playwright, Vitest Browser Mode, or any CDP-compatible client.

<Render file="cdp-api-token-permissions" product="browser-run" />

Expand All @@ -29,15 +29,15 @@ The browser sessions endpoints enable you to:
- **Open, close, and list browser tabs (targets)** — Manage multiple debuggable targets (pages, iframes, etc.) within a single browser instance
- **Connect via WebSocket to send CDP commands** — Automate browser actions programmatically
- **View live browser sessions using Chrome DevTools UI** — Debug and inspect remote browser sessions visually
- **Integrate with existing CDP clients** — Use standard CDP clients like Puppeteer or custom WebSocket implementations
- **Integrate with existing CDP clients** — Use standard CDP clients like Puppeteer, Vitest Browser Mode, or custom WebSocket implementations

## How it works

Once you acquire a browser session, you can interact with it in two ways:

### CDP over WebSocket

Connect to the WebSocket endpoint `/devtools/browser` to acquire a session and send [CDP commands](https://chromedevtools.github.io/devtools-protocol/) directly over the connection. This is the standard way to use CDP and works with any CDP client, including [Puppeteer](/browser-run/cdp/puppeteer/), [Playwright](/browser-run/cdp/playwright/), and [MCP clients](/browser-run/cdp/mcp-clients/).
Connect to the WebSocket endpoint `/devtools/browser` to acquire a session and send [CDP commands](https://chromedevtools.github.io/devtools-protocol/) directly over the connection. This is the standard way to use CDP and works with any CDP client, including [Puppeteer](/browser-run/cdp/puppeteer/), [Playwright](/browser-run/cdp/playwright/), [Vitest Browser Mode](/browser-run/cdp/vitest-browser-mode/), and [MCP clients](/browser-run/cdp/mcp-clients/).

### HTTP API

Expand Down
257 changes: 257 additions & 0 deletions src/content/docs/browser-run/cdp/vitest-browser-mode.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
---
pcx_content_type: how-to
title: Using with Vitest Browser Mode (CDP)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Page titles must use imperative mood, not gerund phrases (style guide: 'Install Wrangler' not 'Installing Wrangler').

description: Run Vitest Browser Mode tests on Browser Run using hosted Chromium over the Chrome DevTools Protocol.
sidebar:
order: 5
label: Vitest Browser Mode
products:
- browser-run
---

import { PackageManagers, Render, TypeScriptExample } from "~/components";

Use Vitest Browser Mode with Browser Run to run browser tests, user interaction tests, and visual regression tests in a hosted Chromium browser. The `@cloudflare/vitest-browser-run-provider` package connects Vitest to Browser Run through the Chrome DevTools Protocol (CDP).

<Render file="cdp-api-token-permissions" product="browser-run" />

Browser Run tests run in a real browser. If you need to test Worker handlers, bindings, Durable Objects, or Workers runtime APIs, use the [Workers Vitest integration](/workers/testing/vitest-integration/) instead.

## Prerequisites

- A Vite project that uses [Vitest Browser Mode](https://vitest.dev/guide/browser/)
- A Cloudflare account with Browser Run enabled
- A Browser Run API token with `Browser Rendering - Edit` permission
- The [Cloudflare Vite plugin](/workers/vite-plugin/), if Browser Run needs to reach a local Vite dev server

## Install packages

Install Vitest Browser Mode, the Playwright browser provider, the Browser Run provider, and the Cloudflare Vite plugin:

<PackageManagers
pkg="vitest @vitest/browser @vitest/browser-playwright @cloudflare/vitest-browser-run-provider @cloudflare/vite-plugin playwright@npm:playwright-core"
dev
/>

The `playwright@npm:playwright-core` alias satisfies the Playwright provider without downloading local browser binaries. If your project already uses the full `playwright` package, you can use that package instead.

## Configure credentials

Set the Browser Run credentials in your shell, CI environment, or an uncommitted `.env` file. By default, `browserRunCdp()` reads `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN`:

```txt title=".env"
CLOUDFLARE_ACCOUNT_ID="<ACCOUNT_ID>"
CLOUDFLARE_API_TOKEN="<API_TOKEN>"
CLOUDFLARE_BROWSER_RUN_CONCURRENCY="8"
```

`CLOUDFLARE_BROWSER_RUN_CONCURRENCY` controls how many Vitest browser test files can run at the same time. It does not create that many Browser Run browser instances.

## Configure Vitest

Create a Vitest config for Browser Run tests:

<TypeScriptExample filename="vitest.browser-run.config.ts">

```ts
import { cloudflare } from "@cloudflare/vite-plugin";
import { browserRunCdp } from "@cloudflare/vitest-browser-run-provider";
import { defineConfig } from "vitest/config";

const browserApiHost = process.env.VITEST_BROWSER_API_HOST ?? "0.0.0.0";
const browserApiPort = Number(process.env.VITEST_BROWSER_API_PORT ?? "63315");
const browserRunConcurrency = Number(
process.env.CLOUDFLARE_BROWSER_RUN_CONCURRENCY ?? "8",
);

export default defineConfig({
plugins: [
cloudflare({
tunnel: { autoStart: true },
}),
],
server: {
host: browserApiHost,
port: browserApiPort,
strictPort: true,
allowedHosts: true,
},
test: {
include: ["test/browser/**/*.browser.test.ts"],
fileParallelism: true,
maxWorkers: browserRunConcurrency,
browser: {
enabled: true,
headless: true,
fileParallelism: true,
provider: browserRunCdp(),
api: {
host: browserApiHost,
port: browserApiPort,
allowExec: true,
allowWrite: true,
},
expect: {
toMatchScreenshot: {
comparatorName: "pixelmatch",
comparatorOptions: {
threshold: 0.2,
allowedMismatchedPixelRatio: 0.005,
},
},
},
instances: [
{
browser: "chromium",
viewport: { width: 1280, height: 800 },
},
],
},
},
});
```

</TypeScriptExample>

The Cloudflare Vite plugin starts a tunnel and sets `CLOUDFLARE_TUNNEL_URL`. The Browser Run provider uses that public origin so hosted Chromium can reach the local Vitest browser API.

If you expose the Vitest browser API with a different tunnel, set `VITEST_BROWSER_PUBLIC_ORIGIN` to its public origin.

This configuration sets `test.browser.api.allowExec` and `test.browser.api.allowWrite` to `true` so browser tests can use APIs such as `cdp()` and write screenshot artifacts. If your tests do not use `cdp()` or write artifacts, remove the corresponding option. Anyone with the tunnel URL can reach your dev server while the tunnel is active. For more information, refer to [Share a local dev server](/workers/local-development/local-dev-tunnels/#security-considerations).

The `test.browser.expect.toMatchScreenshot` block configures shared defaults for visual regression assertions. The fixed viewport keeps screenshots the same size across local and CI runs.

## Write a browser test

Create a browser test file under the configured `test/browser/` directory:

```ts title="test/browser/greeting.browser.test.ts"
import { describe, expect, it } from "vitest";
import { cdp, userEvent } from "vitest/browser";

describe("Browser Run Vitest test", () => {
it("runs in hosted Chromium", async () => {
const userAgent = (await cdp().send("Runtime.evaluate", {
expression: "navigator.userAgent",
returnByValue: true,
})) as { result: { value: string } };

expect(userAgent.result.value).toContain("Chrome");

document.body.innerHTML = `
<main>
<label for="name">Name</label>
<input id="name" />
<button type="button">Greet</button>
<p data-testid="greeting"></p>
</main>
`;

const input = document.querySelector<HTMLInputElement>("#name");
const button = document.querySelector<HTMLButtonElement>("button");
const greeting = document.querySelector<HTMLElement>(
"[data-testid='greeting']",
);

expect(input).not.toBeNull();
expect(button).not.toBeNull();
expect(greeting).not.toBeNull();

button!.addEventListener("click", () => {
greeting!.textContent = `Hello, ${input!.value}!`;
});

await userEvent.type(input!, "Cloudflare");
await userEvent.click(button!);

expect(greeting!.textContent).toBe("Hello, Cloudflare!");
});
});
```

## Add visual regression tests

Use `expect.element(...).toMatchScreenshot()` to compare a page element against a reference screenshot:

```ts title="test/browser/hero.browser.test.ts"
import { expect, test } from "vitest";
import { page } from "vitest/browser";

test("hero section", async () => {
document.body.innerHTML = `
<main>
<section data-testid="hero">
<h1>Build with Cloudflare</h1>
<p>Run browser tests in hosted Chromium.</p>
</section>
</main>
`;

await expect
.element(page.getByTestId("hero"))
.toMatchScreenshot("hero-section");
});
```

When you run a visual test for the first time, Vitest creates a reference screenshot in a `__screenshots__` directory and fails the test. Review the screenshot, commit it, and run the test again.

To intentionally update reference screenshots, run Vitest with `--update`:

<PackageManagers
type="exec"
pkg="vitest"
args="run --config vitest.browser-run.config.ts --update"
/>

Review updated screenshots before committing them. Keep `test.browser.api.allowWrite` set to `true` when tests need to create or update screenshots.

## Run tests

Run Vitest with the Browser Run config:

<PackageManagers
type="exec"
pkg="vitest"
args="run --config vitest.browser-run.config.ts"
/>

To run fewer test files at the same time, lower `CLOUDFLARE_BROWSER_RUN_CONCURRENCY`:

<PackageManagers
type="exec"
pkg="vitest"
args="run --config vitest.browser-run.config.ts"
prefix="CLOUDFLARE_BROWSER_RUN_CONCURRENCY=2"
/>

## Parallelism model

Vitest schedules browser test files across `test.maxWorkers`. The Browser Run provider connects to one hosted Chromium browser and opens isolated pages or contexts for Vitest browser sessions.

This means `CLOUDFLARE_BROWSER_RUN_CONCURRENCY=8` runs up to eight browser test files at once inside one shared Browser Run browser. It does not launch eight separate Browser Run browsers.

If your tests need separate browser processes for isolation, run separate Vitest invocations or lower concurrency. Each new Browser Run browser is subject to [Browser Run limits](/browser-run/limits/).

## Configure provider options

`browserRunCdp()` reads credentials and Browser Run options from environment variables. You can also pass options directly to the provider, but avoid hardcoding secrets in committed files.

| Option | Environment variable | Description |
| -------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| `accountId` | `CLOUDFLARE_ACCOUNT_ID` | Cloudflare account ID used to create the Browser Run URL |
| `apiToken` | `CLOUDFLARE_API_TOKEN` | API token used in the Browser Run authorization header |
| `publicOrigin` | `VITEST_BROWSER_PUBLIC_ORIGIN` or `CLOUDFLARE_TUNNEL_URL` | Public origin for the Vitest browser API |
| `wsEndpoint` | `CLOUDFLARE_BROWSER_RUN_WS_ENDPOINT` | Custom Browser Run CDP WebSocket endpoint |
| `keepAliveMs` | `CLOUDFLARE_BROWSER_RUN_KEEP_ALIVE_MS` | Browser inactivity timeout in milliseconds. Defaults to `600000` |
| `recording` | `CLOUDFLARE_BROWSER_RUN_RECORDING` | Set to `true` to enable [session recording](/browser-run/features/session-recording/) |

For example:

```ts title="vitest.browser-run.config.ts"
provider: browserRunCdp({
keepAliveMs: 600000,
recording: true,
}),
```

<Render file="faq" product="browser-run" />
5 changes: 5 additions & 0 deletions src/content/docs/browser-run/examples.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ Use [CDP](/browser-run/cdp/) to connect to Browser Run from any environment usin
description="Run Puppeteer scripts against Browser Run from Node.js on your local machine, CI/CD, or any external server."
href="/browser-run/cdp/puppeteer/"
/>
<LinkCard
title="Run Vitest Browser Mode tests"
description="Run browser tests and visual regression tests in Browser Run hosted Chromium."
href="/browser-run/cdp/vitest-browser-mode/"
/>
<LinkCard
title="Configure AI agents with MCP"
description="Set up Claude Desktop, Claude Code, Cursor, or other MCP clients to control browsers via Browser Run."
Expand Down
2 changes: 2 additions & 0 deletions src/content/docs/browser-run/playwright/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ export default {

One of the most common use cases for using Playwright is software testing. Playwright includes test assertion features in its APIs; refer to [Assertions](https://playwright.dev/docs/test-assertions) in the Playwright documentation for details. Here's an example of a Worker doing `expect()` test assertions of the [todomvc](https://demo.playwright.dev/todomvc) demo page:

To run Vitest Browser Mode tests against Browser Run hosted Chromium, refer to [Using with Vitest Browser Mode (CDP)](/browser-run/cdp/vitest-browser-mode/).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cross-link breaks the flow of the Assertions example. Move it to the end of the section, after the code block and before the heading.


```ts
import { launch } from "@cloudflare/playwright";
import { expect } from "@cloudflare/playwright/test";
Expand Down
2 changes: 2 additions & 0 deletions src/content/docs/workers/testing/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { Render, LinkButton } from "~/components";

The Workers platform has a variety of ways to test your applications, depending on your requirements. We recommend using the [Vitest integration](/workers/testing/vitest-integration), which allows you to run tests _inside_ the Workers runtime, and unit test individual functions within your Worker.

For browser tests that need DOM APIs, user interactions, screenshots, or visual regression in a real browser, use [Vitest Browser Mode with Browser Run](/browser-run/cdp/vitest-browser-mode/). That setup runs tests in hosted Chromium instead of inside the Workers runtime.

<LinkButton href="/workers/testing/vitest-integration/write-your-first-test/">
Get started with Vitest
</LinkButton>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ The Workers Vitest integration:
- Leverages Vitest's hot-module reloading for near instant reruns.
- Supports projects with multiple Workers.

This integration is separate from [Vitest Browser Mode with Browser Run](/browser-run/cdp/vitest-browser-mode/). Use the Workers Vitest integration for tests that need Workers runtime APIs or bindings. Use Browser Run for tests that need DOM APIs, user interactions, screenshots, or visual regression in real Chromium.

<LinkButton href="/workers/testing/vitest-integration/write-your-first-test/">
Write your first test
</LinkButton>
Original file line number Diff line number Diff line change
Expand Up @@ -187,5 +187,6 @@ Usually this is not a problem, but to run your Worker in a fresh environment tha
## Related resources

- For more complex examples of testing using `@cloudflare/vitest-pool-workers`, refer to [Recipes](/workers/testing/vitest-integration/recipes/).
- To run Vitest Browser Mode tests in real hosted Chromium, refer to [Using with Vitest Browser Mode (CDP)](/browser-run/cdp/vitest-browser-mode/).
- [Configuration API reference](/workers/testing/vitest-integration/configuration/)
- [Test APIs reference](/workers/testing/vitest-integration/test-apis/)
2 changes: 2 additions & 0 deletions src/content/docs/workers/vite-plugin/reference/api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ It accepts an optional `PluginConfig` parameter.

Provide an object to configure a named tunnel or control whether the tunnel starts automatically. Press `t + Enter` to start or close the tunnel. Set `tunnel.autoStart` to `true` if you want the tunnel to open when Vite starts.

Vitest Browser Mode tests that run on Browser Run can use `tunnel.autoStart` to expose the local Vitest browser API to hosted Chromium. For a complete configuration, refer to [Using with Vitest Browser Mode (CDP)](/browser-run/cdp/vitest-browser-mode/).

<TypeScriptExample filename="vite.config.ts">
```ts
import { defineConfig } from "vite";
Expand Down
Loading