Skip to main content

Clerk Playwright E2E Testing

Modelled after the official clerk-playwright-nextjs reference implementation.

Overview

Clerk authentication is tested end-to-end using @clerk/testing/playwright, which provides a clerkSignIn() helper that bypasses the Clerk UI and sets a valid session token directly in the browser. This is fast, deterministic, and does not depend on form automation.

The protected-route surface in this app is:

/:locale/e/* → clerkMiddleware calls auth.protect()

Files

FilePurpose
playwright-tests/global.setup.jsRuns once before all tests; calls clerkSetup()
playwright-tests/fixtures.jsExtends Playwright test with a clerkSignedIn fixture
playwright-tests/auth.spec.jsAuth-specific E2E tests
playwright.config.jsRegisters global setup and loads .env.e2e
.env.e2eLocal-only test credentials (git-ignored)
.env.e2e.exampleTemplate — commit this, not .env.e2e

One-time setup

1. Create a test user in Clerk Dashboard

  1. Open Clerk Dashboard → your test-mode instance.
  2. Users → Create user — use a dedicated test email (e.g. e2e-test@gdprlabs.dev) and a strong password.
  3. Assign the user to the organisation(s) you want to test against.

2. Create .env.e2e

cp .env.e2e.example .env.e2e

Fill in the email of the test user. No password is needed — CLERK_SECRET_KEY (already in .env.local) is used by the fixture to create a short-lived sign-in token via Clerk's backend API (ticket strategy).

E2E_CLERK_USER_EMAIL=e2e-test@gdprlabs.dev

.env.e2e is git-ignored — never commit it.

3. Ensure CLERK_SECRET_KEY is available

clerkSetup() reads CLERK_SECRET_KEY at runtime to generate testing tokens. This key is already in .env.local. For CI, set it as a secret environment variable.


Running the tests

# Start dev server separately (optional — playwright.config.js starts it automatically)
npm run dev

# Run all E2E tests including auth
npm run test:playwright

# Run only auth tests
npx playwright test auth.spec.js

# Interactive UI mode
npm run test:playwright:ui

How clerkSignedIn fixture works

// playwright-tests/fixtures.js
import { clerk } from "@clerk/testing/playwright";

clerkSignedIn: async ({ page }, use) => {
await page.goto("/");
await clerk.signIn({
page,
emailAddress: process.env.E2E_CLERK_USER_EMAIL,
});
await use(page); // run the test
await clerk.signOut({ page });
},

clerk.signIn({ emailAddress }) uses CLERK_SECRET_KEY to create a sign-in token via Clerk's backend API (ticket strategy) — no password, no UI interaction. After the test, clerk.signOut() clears the session.

Using the fixture in a test

import { test, expect } from "./fixtures.js";

test("protected page is accessible when signed in", async ({
page,
clerkSignedIn, // ← declare this to trigger sign-in
}) => {
await page.goto("/en/e/1");
await expect(page).not.toHaveURL(/sign-in/);
});

The fixture is opt-in (auto: false) — tests that do not declare clerkSignedIn run unauthenticated.


CI configuration

Add these secrets to your CI environment:

CLERK_SECRET_KEY=sk_test_…
E2E_CLERK_USER_EMAIL=…

The playwright.config.js loads .env.e2e via dotenv — in CI, the variables are already in the environment so the missing .env.e2e file is harmless (dotenv.config silently ignores absent files).


Troubleshooting

SymptomLikely cause
CLERK_SECRET_KEY is not setMissing from .env.local or CI env
clerkSignIn failedWrong credentials or user not in Clerk test instance
Tests redirect to sign-in unexpectedlyE2E_CLERK_USER_EMAIL / PASSWORD env vars not loaded
dotenv not found errorRun npm install — dotenv is a dep of @clerk/testing