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.
Installation
Section titled “Installation”npm install autotel autotel-playwrightPeer dependency: @playwright/test.
1. globalSetup
Section titled “1. globalSetup”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, }),});2. API Base URL
Section titled “2. API Base URL”Set env var so trace headers are injected only into API requests:
API_BASE_URL=http://localhost:30003. Use the Fixture
Section titled “3. Use the Fixture”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();});Examples
Section titled “Examples”example-playwright-e2e— Kitchen-sink Playwright project withpage+requestWithTracefixtures,step()helper, andOtelReporter.
How It Works
Section titled “How It Works”Test (trace X) → traceparent header → Server (trace X) ↓ ↓ OTLP export OTLP export ↓ ↓ One trace = test + API spansServer-Side Span Inspection
Section titled “Server-Side Span Inspection”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;}Reporter
Section titled “Reporter”Optional reporter creates test/suite spans:
export default defineConfig({ reporter: [['autotel-playwright/reporter']],});