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
| File | Purpose |
|---|---|
playwright-tests/global.setup.js | Runs once before all tests; calls clerkSetup() |
playwright-tests/fixtures.js | Extends Playwright test with a clerkSignedIn fixture |
playwright-tests/auth.spec.js | Auth-specific E2E tests |
playwright.config.js | Registers global setup and loads .env.e2e |
.env.e2e | Local-only test credentials (git-ignored) |
.env.e2e.example | Template — commit this, not .env.e2e |
One-time setup
1. Create a test user in Clerk Dashboard
- Open Clerk Dashboard → your test-mode instance.
- Users → Create user — use a dedicated test email (e.g.
e2e-test@gdprlabs.dev) and a strong password. - 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
| Symptom | Likely cause |
|---|---|
CLERK_SECRET_KEY is not set | Missing from .env.local or CI env |
clerkSignIn failed | Wrong credentials or user not in Clerk test instance |
| Tests redirect to sign-in unexpectedly | E2E_CLERK_USER_EMAIL / PASSWORD env vars not loaded |
dotenv not found error | Run npm install — dotenv is a dep of @clerk/testing |