diff --git a/src/content/docs/browser-rendering/how-to/e2e-testing.mdx b/src/content/docs/browser-rendering/how-to/e2e-testing.mdx new file mode 100644 index 000000000000000..4f97ac3157ecc62 --- /dev/null +++ b/src/content/docs/browser-rendering/how-to/e2e-testing.mdx @@ -0,0 +1,255 @@ +--- +updated: 2026-01-22 +difficulty: Intermediate +pcx_content_type: tutorial +title: Run E2E tests with Browser Rendering +sidebar: + order: 4 +tags: + - Playwright + - Testing + - E2E +--- + +import { Details, Tabs, TabItem } from "~/components"; + +End-to-end (E2E) testing typically requires running browsers locally or in Docker containers within your CI/CD pipeline. This adds complexity: you need to install browsers, manage dependencies, and allocate resources for browser instances. + +With Cloudflare Browser Rendering, you can run your E2E tests against remote browsers using the [Chrome DevTools Protocol (CDP)](https://chromedevtools.github.io/devtools-protocol/). Your test runner connects to a remote browser over WebSocket, eliminating the need to install or manage browsers in your CI environment. + +## Why use Browser Rendering for E2E tests + +- **Simplified CI/CD**: No need to install browsers or manage Docker images with browser dependencies. +- **Consistent environment**: Tests run against the same browser environment every time, reducing flaky tests caused by local browser differences. +- **Reduced resource usage**: Offload browser execution to Cloudflare, freeing up CI runner resources. +- **Faster pipeline setup**: Skip browser installation steps in your CI configuration. + +
+ +| | Local browser | Remote browser (CDP) | +|---------------------|---------------|----------------------| +| **Best for** | Development, debugging | CI/CD pipelines, production tests | +| **Speed** | Faster (no network latency) | Slightly slower (network hop) | +| **Setup** | Requires browsers installed | No browser installation needed | +| **Debugging** | Visual browser, DevTools access | Headless only (live view coming soon) | +| **Resource usage** | Uses local CPU/RAM | Offloads to Cloudflare | + +**In practice:** +- **Developers** run tests locally during development so they can see the browser, pause tests, and debug visually. +- **CI pipelines** use remote browsers because installing browsers in CI is slow, flaky, and wastes resources. + +The fixture pattern shown in this tutorial supports both workflows automatically — tests run locally by default, and switch to remote browsers when the `CDP_ENDPOINT` environment variable is set. + +
+ +## Prerequisites + +- A [Cloudflare account](https://dash.cloudflare.com/sign-up/workers-and-pages) +- A [Cloudflare API token](/fundamentals/api/get-started/create-token/) with `Browser Rendering - Edit` permissions +- An existing [Playwright](https://playwright.dev/) test suite +- Your application deployed to a publicly accessible URL (or behind [Cloudflare Access](/cloudflare-one/policies/access/)) + +:::note +This tutorial uses the standard `@playwright/test` package, not the Cloudflare fork. The Cloudflare fork (`@cloudflare/playwright`) is designed for running browser automation [inside a Worker](/browser-rendering/workers-bindings/). For E2E testing from external environments like CI runners, use the standard package with CDP connections as shown below. +::: + +## 1. Get your CDP endpoint + +Browser Rendering exposes a CDP WebSocket endpoint that test runners can connect to. The endpoint URL follows this format: + +```txt +wss://api.cloudflare.com/client/v4/accounts//browser-rendering/cdp +``` + +Replace `` with your [Cloudflare account ID](/fundamentals/setup/find-account-and-zone-ids/). + +Store this as an environment variable in your CI system, along with your API token: + +```sh +CDP_ENDPOINT="wss://api.cloudflare.com/client/v4/accounts//browser-rendering/cdp" +CDP_API_TOKEN="" +``` + +:::note +Keep your API token secure. Store it as a secret in your CI/CD system rather than committing it to your repository. +::: + +## 2. Create a CDP connection fixture + +Create a Playwright fixture that conditionally connects to the remote browser. This allows your tests to run locally with installed browsers during development, or remotely via CDP in CI. + +Create a file called `cdp-fixture.ts` in your tests directory: + +```ts title="tests/cdp-fixture.ts" +import { test as base, chromium } from "@playwright/test"; + +const CDP_ENDPOINT = process.env.CDP_ENDPOINT; +const CDP_API_TOKEN = process.env.CDP_API_TOKEN; + +export const test = CDP_ENDPOINT + ? base.extend({ + browser: async ({}, use) => { + const browser = await chromium.connectOverCDP(CDP_ENDPOINT, { + headers: CDP_API_TOKEN + ? { Authorization: `Bearer ${CDP_API_TOKEN}` } + : {}, + }); + await use(browser); + await browser.close(); + }, + }) + : base; + +export { expect } from "@playwright/test"; +``` + +This fixture checks for the `CDP_ENDPOINT` environment variable: +- **If set**: Connects to Browser Rendering via CDP +- **If not set**: Uses the default local browser (standard Playwright behavior) + +## 3. Update your tests to use the fixture + +Import the custom `test` function from your fixture instead of directly from `@playwright/test`: + +```ts title="tests/example.spec.ts" +import { test, expect } from "./cdp-fixture"; + +test("homepage has correct title", async ({ page }) => { + await page.goto("https://your-app.example.com"); + await expect(page).toHaveTitle(/Your App/); +}); + +test("user can navigate to about page", async ({ page }) => { + await page.goto("https://your-app.example.com"); + await page.click('a[href="/about"]'); + await expect(page).toHaveURL(/.*about/); +}); +``` + +## 4. Handle authentication (optional) + +If your application is behind [Cloudflare Access](/cloudflare-one/policies/access/), configure Playwright to include the Access headers in all requests. + +First, create a [service token](/cloudflare-one/identity/service-tokens/) for your CI pipeline. Then update your `playwright.config.ts`: + +```ts title="playwright.config.ts" +import { defineConfig } from "@playwright/test"; + +const CF_ACCESS_CLIENT_ID = process.env.CF_ACCESS_CLIENT_ID; +const CF_ACCESS_CLIENT_SECRET = process.env.CF_ACCESS_CLIENT_SECRET; + +export default defineConfig({ + use: { + baseURL: process.env.PREVIEW_URL || "https://your-app.example.com", + extraHTTPHeaders: + CF_ACCESS_CLIENT_ID && CF_ACCESS_CLIENT_SECRET + ? { + "CF-Access-Client-Id": CF_ACCESS_CLIENT_ID, + "CF-Access-Client-Secret": CF_ACCESS_CLIENT_SECRET, + } + : {}, + }, +}); +``` + +## 5. Update your CI configuration + +Add the CDP endpoint and any authentication credentials to your CI environment. Below are examples for common CI systems. + + + +```yaml title=".github/workflows/e2e.yml" +name: E2E Tests + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Install dependencies + run: npm ci + + - name: Run E2E tests + env: + CDP_ENDPOINT: "wss://api.cloudflare.com/client/v4/accounts/${{ secrets.CLOUDFLARE_ACCOUNT_ID }}/browser-rendering/cdp" + CDP_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CF_ACCESS_CLIENT_ID: ${{ secrets.CF_ACCESS_CLIENT_ID }} + CF_ACCESS_CLIENT_SECRET: ${{ secrets.CF_ACCESS_CLIENT_SECRET }} + run: npx playwright test +``` + + + +```yaml title=".gitlab-ci.yml" +e2e-tests: + image: node:20 + stage: test + variables: + CDP_ENDPOINT: "wss://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/browser-rendering/cdp" + CDP_API_TOKEN: ${CLOUDFLARE_API_TOKEN} + script: + - npm ci + - npx playwright test +``` + + + +:::caution +Do not install Playwright browsers in CI when using Browser Rendering. Skip the `npx playwright install` step to save time and resources. +::: + +## 6. Run your tests + +**Locally (with local browsers):** + +```sh +npx playwright test +``` + +**In CI (with Browser Rendering):** + +Your CI pipeline will automatically use Browser Rendering when the `CDP_ENDPOINT` environment variable is set. + +## Troubleshooting + +### Connection timeouts + +If you experience connection timeouts, ensure your API token has the correct permissions and has not expired. You can also increase the connection timeout in your fixture: + +```ts +const browser = await chromium.connectOverCDP(CDP_ENDPOINT, { + headers: { Authorization: `Bearer ${CDP_API_TOKEN}` }, + timeout: 60000, // 60 seconds +}); +``` + +### Tests pass locally but fail in CI + +This usually indicates environment differences. Ensure your application URL is accessible from Cloudflare's network and that any required authentication headers are configured correctly. + +## Summary + +You have configured your E2E tests to run against Cloudflare Browser Rendering. Your CI pipeline no longer needs to install or manage browsers, and your tests run against a consistent browser environment. + +:::note[Using Puppeteer?] +This tutorial focuses on Playwright, the recommended framework for E2E testing. If you use Puppeteer, the same CDP connection approach works — use [`puppeteer.connect()`](https://pptr.dev/api/puppeteer.puppeteer.connect) with the `browserWSEndpoint` option. +::: + +## Pricing + +Browser Rendering usage from CDP connections is billed based on browser hours. Refer to [Browser Rendering pricing](/browser-rendering/pricing/) for details on costs and how to monitor your usage. + +## Related resources + +- [Playwright documentation](/browser-rendering/playwright/) +- [Browser Rendering limits](/browser-rendering/limits/) +- [Browser Rendering pricing](/browser-rendering/pricing/) +- [Cloudflare Access service tokens](/cloudflare-one/identity/service-tokens/)