Skip to content

Playwright

autotel-playwright gives each test a span and injects traceparent headers into HTTP requests to your API, so test and server appear as one trace in your OTLP backend.

Terminal window
npm install autotel autotel-playwright

Peer dependency: @playwright/test.

playwright.config.ts
import { defineConfig } from '@playwright/test';
import { createGlobalSetup } from 'autotel-playwright';
export default defineConfig({
globalSetup: createGlobalSetup({
service: 'e2e-tests',
debug: true,
endpoint: process.env.OTLP_ENDPOINT,
}),
});

Set env var so trace headers are injected only into API requests:

Terminal window
API_BASE_URL=http://localhost:3000

Browser tests — page requests to your API get trace context:

import { test, expect } from 'autotel-playwright';
test('health check', async ({ page }) => {
await page.goto(process.env.API_BASE_URL + '/health');
// Request to API_BASE_URL includes traceparent header
});

API tests — use requestWithTrace for Node-side API calls:

import { test, expect } from 'autotel-playwright';
test('create user via API', async ({ requestWithTrace }) => {
const response = await requestWithTrace.post('/api/users', {
data: { email: 'test@example.com' },
});
expect(response.ok()).toBeTruthy();
});
  • example-playwright-e2e — Kitchen-sink Playwright project with page + requestWithTrace fixtures, step() helper, and OtelReporter.
Test (trace X) → traceparent header → Server (trace X)
↓ ↓
OTLP export OTLP export
↓ ↓
One trace = test + API spans

When your app runs server-side code during E2E tests, you can capture and assert on those spans using createTestSpansClient. It pairs with createTestSpansHandlers() from autotel-tanstack/testing (see TanStack E2E setup).

import { test, expect, createTestSpansClient } from 'autotel-playwright';
const spansClient = createTestSpansClient('http://localhost:3100');
test('server function is traced', async ({ page, request }) => {
await spansClient.clearSpans(request);
await page.goto('/');
await page.getByTestId('send-button').click();
const spans = await spansClient.getSpans(request);
const handler = spans.find(s => s.name === 'sendMoney.handler');
expect(handler).toBeDefined();
expect(handler?.attributes?.['transfer.amount']).toBe(100);
});

createTestSpansClient(baseUrl, options?) options:

| Option | Default | Description | |--------|---------|-------------| | path | /api/test-spans | Path of the test-spans endpoint |

Each span in the returned array is a SerializedSpan:

interface SerializedSpan {
name: string;
spanId: string;
traceId: string;
parentSpanId?: string;
attributes?: Record<string, unknown>;
status: { code: number; message?: string };
durationMs: number;
}

Optional reporter creates test/suite spans:

playwright.config.ts
export default defineConfig({
reporter: [['autotel-playwright/reporter']],
});