How to set up End-to-End (E2E) testing with Playwright

End-to-End (E2E) tests give you confidence that your collaborative features work correctly across multiple clients. This guide walks through setting up Playwright to test a Liveblocks-powered Next.js application using the local dev server, so your tests run locally or in CI, without hitting Liveblocks cloud APIs.

Have a project ready

This guide assumes you already have a Liveblocks application set up. We'll use our Collaborative Todo List example as a reference throughout, but a similar approach works with any Liveblocks project.

Overview

The setup has two parts:

  1. Point your app at the local dev server instead of the Liveblocks cloud, using the baseUrl prop and using pk_localdev as the public key.
  2. Configure Playwright to start both the dev server and your app before tests run.

Step 1: Install dependencies

Add Playwright to your project:

Terminal

Step 2: Configure your app for local development

The Liveblocks dev server accepts connections using the magic key pk_localdev and runs on port 1153 by default. Pass these to your LiveblocksProvider via environment variables so they can be overridden per environment:

pages/_app.tsx

Set the environment variables in your .env.local for everyday development, or let Playwright inject them at test time (see Step 3).

Note about baseUrl

The baseUrl prop redirects all Liveblocks traffic, including WebSocket connections, to the given URL. When it is undefined, the client uses the default Liveblocks cloud endpoint, so production builds are unaffected.

Step 3: Create a Playwright configuration

Create playwright.config.ts at the project root. The key idea is to use the webServer option to start the Liveblocks dev server and your Next.js app automatically:

playwright.config.ts

A few things to note:

  • --no-persist—Starts the server in a throwaway directory, so tests never touch the .liveblocks/ folder you use for everyday development. Pair it with --no-check, which skips the interactive project setup check on start.
  • reuseExistingServer—Locally, this lets you leave the servers running between test runs for faster iteration. In CI, a fresh server is started each time.
  • CI check—In CI, the dev server is provided by a Docker service container (see Running in CI), so we skip starting it via webServer.
  • Environment variables—NEXT_PUBLIC_LIVEBLOCKS_PUBLIC_KEY and NEXT_PUBLIC_LIVEBLOCKS_BASE_URL are injected into the Next.js build/dev process, pointing it at the local dev server.

Step 4: Write tests

Create a test file, e.g. test/todo.spec.ts. Each test navigates to a unique room to avoid cross-test interference:

test/todo.spec.ts

Testing multi-client collaboration

One of the most valuable things to test is that changes sync between clients. Playwright makes this straightforward—open a second page in the same browser context and point it at the same room:

test/todo.spec.ts

Testing presence

You can also verify presence features like typing indicators:

test/todo.spec.ts

Step 5: Run the tests

Terminal

Playwright will automatically start the Liveblocks dev server and your Next.js app, run all tests, then shut everything down.

Running in CI

In CI environments like GitHub Actions, use the Liveblocks dev server Docker image as a service container instead of starting it via npx:

.github/workflows/e2e.yml

Key points:

  • NEXT_PUBLIC_ env vars—These are set at the job level so they're available during both npm run build (where Next.js inlines them) and test execution.
  • Build before test—In CI, the Playwright config uses next start (not next dev), so the app must be built first.
  • Artifact upload—On failure, the Playwright HTML report is uploaded so you can debug test failures.

Complete example

The Next.js Todo List example in the Liveblocks repository includes a full Playwright test suite and a CI workflow built using this guide. You can use it as a starting point:

Tips

  • Unique room IDs per test: Room state lives for as long as the dev server does, so using unique room IDs prevents state from leaking between tests within a run.
  • Retries: Collaborative tests involve WebSocket connections and async syncing. Adding a few retries in CI (retries: 2 or more) helps absorb occasional timing-related flakiness.
  • Traces: Setting trace: "on-first-retry" captures a Playwright trace on the first retry, which is invaluable for debugging.
  • reuseExistingServer: During local development, keep the dev server running separately (npx liveblocks dev) for faster test iteration.