diff --git a/src/content/changelog/browser-run/2026-07-03-vitest-browser-mode-provider.mdx b/src/content/changelog/browser-run/2026-07-03-vitest-browser-mode-provider.mdx new file mode 100644 index 00000000000..7b7ad37f789 --- /dev/null +++ b/src/content/changelog/browser-run/2026-07-03-vitest-browser-mode-provider.mdx @@ -0,0 +1,54 @@ +--- +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" }], + }, + }, +}); +``` + +After configuring the provider, you can write Vitest Browser Mode tests that run in Browser Run hosted Chromium. For example, the following test captures and compares 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 = ` +
+
+

Build with Cloudflare

+
+
+ `; + + await expect.element(page.getByTestId("hero")).toMatchScreenshot("hero"); +}); +``` + +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/). diff --git a/src/content/docs/browser-run/cdp/index.mdx b/src/content/docs/browser-run/cdp/index.mdx index bc098f7d27d..3320ec2bbd4 100644 --- a/src/content/docs/browser-run/cdp/index.mdx +++ b/src/content/docs/browser-run/cdp/index.mdx @@ -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. @@ -29,7 +29,7 @@ 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 @@ -37,7 +37,7 @@ 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 diff --git a/src/content/docs/browser-run/cdp/vitest-browser-mode.mdx b/src/content/docs/browser-run/cdp/vitest-browser-mode.mdx new file mode 100644 index 00000000000..16ceee593f2 --- /dev/null +++ b/src/content/docs/browser-run/cdp/vitest-browser-mode.mdx @@ -0,0 +1,272 @@ +--- +pcx_content_type: how-to +title: Use Vitest Browser Mode (CDP) +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](https://vitest.dev/guide/browser/) 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). + + + +## 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: + + + +## Configure credentials + +Set the Browser Run credentials in your shell, CI environment, or a `.env` file that is ignored by Git. By default, `browserRunCdp()` reads `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN`: + +```txt title=".env" +CLOUDFLARE_ACCOUNT_ID="" +CLOUDFLARE_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: + + + +```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 }, + }, + ], + }, + }, +}); +``` + + + +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. + +By default, `tunnel: { autoStart: true }` creates a short-lived [Quick Tunnel](/tunnel/setup/#quick-tunnels-development). Anyone with the tunnel URL can reach your dev server while the tunnel is active. Use this mode for local runs only after you review what the dev server exposes. For more information, refer to [Share a local dev server](/workers/local-development/local-dev-tunnels/#security-considerations). + +For a stable hostname or stricter access control, use a [named tunnel](/tunnel/setup/#create-a-tunnel) with the Cloudflare Vite plugin: + + + +```ts +cloudflare({ + tunnel: { + name: "vitest-browser-run", + autoStart: true, + }, +}), +``` + + + +You can protect the named tunnel hostname by adding a [self-hosted Access application](/cloudflare-one/access-controls/applications/http-apps/self-hosted-public-app/). For non-interactive test runs, use a [Service Auth policy](/cloudflare-one/access-controls/policies/#service-auth) and an [Access service token](/cloudflare-one/access-controls/service-credentials/service-tokens/). Browser Run must be able to load the Vitest browser runner through that Access policy; service tokens authenticate HTTP requests with the `CF-Access-Client-Id` and `CF-Access-Client-Secret` headers. + +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 = ` +
+ + + +

+
+ `; + + const input = document.querySelector("#name"); + const button = document.querySelector("button"); + const greeting = document.querySelector( + "[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 = ` +
+
+

Build with Cloudflare

+

Run browser tests in hosted Chromium.

+
+
+ `; + + 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`: + + + +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: + + + +To run fewer test files at the same time, lower `CLOUDFLARE_BROWSER_RUN_CONCURRENCY`: + + + +## 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, +}), +``` + + diff --git a/src/content/docs/browser-run/examples.mdx b/src/content/docs/browser-run/examples.mdx index d197904812d..5fcdf2c3820 100644 --- a/src/content/docs/browser-run/examples.mdx +++ b/src/content/docs/browser-run/examples.mdx @@ -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/" /> + Get started with Vitest diff --git a/src/content/docs/workers/testing/vitest-integration/browser-mode.mdx b/src/content/docs/workers/testing/vitest-integration/browser-mode.mdx new file mode 100644 index 00000000000..9ec2c0c68b6 --- /dev/null +++ b/src/content/docs/workers/testing/vitest-integration/browser-mode.mdx @@ -0,0 +1,10 @@ +--- +pcx_content_type: navigation +title: Browser Mode with Browser Run +description: Run Vitest Browser Mode tests in hosted Chromium with Browser Run. +external_link: /browser-run/cdp/vitest-browser-mode/ +sidebar: + order: 3.5 +products: + - workers +--- diff --git a/src/content/docs/workers/testing/vitest-integration/index.mdx b/src/content/docs/workers/testing/vitest-integration/index.mdx index 2e037b3ba41..9efffd0e878 100644 --- a/src/content/docs/workers/testing/vitest-integration/index.mdx +++ b/src/content/docs/workers/testing/vitest-integration/index.mdx @@ -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. + Write your first test diff --git a/src/content/docs/workers/testing/vitest-integration/recipes.mdx b/src/content/docs/workers/testing/vitest-integration/recipes.mdx index 79fb21b23ea..8045e476b3d 100644 --- a/src/content/docs/workers/testing/vitest-integration/recipes.mdx +++ b/src/content/docs/workers/testing/vitest-integration/recipes.mdx @@ -10,8 +10,9 @@ products: - workers --- -Recipes are examples that help demonstrate how to write unit tests and integration tests for Workers projects using the [`@cloudflare/vitest-pool-workers`](https://www.npmjs.com/package/@cloudflare/vitest-pool-workers) package. +Recipes are examples that help demonstrate how to write unit tests and integration tests for Workers projects using the [`@cloudflare/vitest-pool-workers`](https://www.npmjs.com/package/@cloudflare/vitest-pool-workers) package. For tests that need real browser APIs, use Vitest Browser Mode with Browser Run. +- [Browser tests and visual regression tests with Vitest Browser Mode and Browser Run](/browser-run/cdp/vitest-browser-mode/) - [Basic unit and integration tests for Workers using `exports.default`](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/basics-unit-integration-self) - [Basic unit and integration tests for Pages Functions using `exports.default`](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/pages-functions-unit-integration-self) - [Basic integration tests using an auxiliary Worker](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/basics-integration-auxiliary) @@ -30,4 +31,4 @@ Recipes are examples that help demonstrate how to write unit tests and integrati - [Resolving modules with Vite Dependency Pre-Bundling](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/module-resolution) - [Mocking Workers AI and Vectorize bindings in unit tests](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/ai-vectorize) - [Tests using the Images binding](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/images) -- [Tests mocking Workers Assets](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/workers-assets) \ No newline at end of file +- [Tests mocking Workers Assets](https://github.com/cloudflare/workers-sdk/tree/main/fixtures/vitest-pool-workers-examples/workers-assets) diff --git a/src/content/docs/workers/testing/vitest-integration/write-your-first-test.mdx b/src/content/docs/workers/testing/vitest-integration/write-your-first-test.mdx index 9e64bb42b71..2b67b22ae19 100644 --- a/src/content/docs/workers/testing/vitest-integration/write-your-first-test.mdx +++ b/src/content/docs/workers/testing/vitest-integration/write-your-first-test.mdx @@ -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/) diff --git a/src/content/docs/workers/vite-plugin/reference/api.mdx b/src/content/docs/workers/vite-plugin/reference/api.mdx index b946c68e293..231d6653203 100644 --- a/src/content/docs/workers/vite-plugin/reference/api.mdx +++ b/src/content/docs/workers/vite-plugin/reference/api.mdx @@ -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/). + ```ts import { defineConfig } from "vite"; diff --git a/src/content/partials/browser-run/integration-methods.mdx b/src/content/partials/browser-run/integration-methods.mdx index 0a5af1e218e..df1b9b69d5b 100644 --- a/src/content/partials/browser-run/integration-methods.mdx +++ b/src/content/partials/browser-run/integration-methods.mdx @@ -5,7 +5,7 @@ Browser Run offers two categories of integration methods: - **[Quick Actions](/browser-run/quick-actions/)**: Simple, stateless browser tasks like screenshots, PDFs, and scraping. No code deployment needed. -- **Browser Sessions**: Direct browser control via [Puppeteer](/browser-run/puppeteer/), [Playwright](/browser-run/playwright/), [CDP](/browser-run/cdp/), or [Stagehand](/browser-run/stagehand/). Deploy within Cloudflare Workers or connect from any environment via CDP. +- **Browser Sessions**: Direct browser control via [Puppeteer](/browser-run/puppeteer/), [Playwright](/browser-run/playwright/), [CDP](/browser-run/cdp/), [Vitest Browser Mode](/browser-run/cdp/vitest-browser-mode/), or [Stagehand](/browser-run/stagehand/). Deploy within Cloudflare Workers or connect from any environment via CDP. | Use case | Recommended | Why | | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------- | @@ -15,5 +15,6 @@ Browser Run offers two categories of integration methods: | AI-powered data extraction | [JSON endpoint](/browser-run/quick-actions/json-endpoint/) | Structured data via natural language prompts | | Site-wide crawling | [Crawl endpoint](/browser-run/quick-actions/crawl-endpoint/) | Multi-page content extraction with async results | | AI agent browsing | [Playwright MCP](/browser-run/playwright/playwright-mcp/) or [CDP with MCP clients](/browser-run/cdp/mcp-clients/) | LLMs control browsers via MCP | +| Browser tests and visual regression | [Vitest Browser Mode](/browser-run/cdp/vitest-browser-mode/) | Real hosted Chromium for local or CI test suites | | Resilient scraping | [Stagehand](/browser-run/stagehand/) | AI finds elements by intent, not selectors | | Direct browser control from any environment | [CDP](/browser-run/cdp/) | WebSocket access from local machines, CI/CD, or external servers | diff --git a/src/content/release-notes/browser-run.yaml b/src/content/release-notes/browser-run.yaml index 726694e7707..58f7abce33e 100644 --- a/src/content/release-notes/browser-run.yaml +++ b/src/content/release-notes/browser-run.yaml @@ -3,6 +3,10 @@ link: "/browser-run/changelog/" productName: Browser Run productLink: "/browser-run/" entries: + - publish_date: "2026-07-03" + title: "Vitest Browser Mode provider for Browser Run" + description: |- + * You can now run [Vitest Browser Mode](/browser-run/cdp/vitest-browser-mode/) test suites on Browser Run hosted Chromium using `@cloudflare/vitest-browser-run-provider`. The provider connects Vitest's Playwright browser provider to Browser Run over CDP and supports parallel browser files in one hosted Chromium session. Set `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN` before running tests. The token needs `Browser Rendering - Edit` permission. - publish_date: "2026-06-12" title: "New tutorial: Pre-render pages for crawlers" description: |-