Playwright
npm install @tracera/playwright
Requires Node.js ≥ 22.19 (see Autotest adapters). @tracera/playwright on npm.
In playwright.config.ts:
import '@tracera/playwright/register';
import { defineConfig } from '@playwright/test';
export default defineConfig({
reporter: [['@tracera/playwright/reporter']],
use: {
screenshot: 'only-on-failure',
video: 'retain-on-failure',
trace: 'retain-on-failure', // or "on-first-retry"
},
});
import "@tracera/playwright/register" patches test.step / hooks in every worker so tracera.comment / tracera.error nest on the current step.
Enable screenshot / video / trace once in config — Playwright artifacts are forwarded into the Tracera result automatically (at most 10 attachments on the result and on each step). Only the final Playwright attempt is reported when retries are enabled. If that attempt is a retry (retry > 0), the result Env tab includes PLAYWRIGHT_RETRY — Playwright’s testInfo.retry (1 is the second run).
Environment variables and run mode: Autotest adapters.
Bind testCaseId and autotestName
Import test / expect from @playwright/test, and tracera / annotations from @tracera/playwright.
If you omit autotestName, the adapter uses the Playwright test title.
At declaration (preferred):
import { test, expect } from '@playwright/test';
import { annotations } from '@tracera/playwright';
test('login', { annotation: annotations(42, 'e2e/login') }, async ({ page }) => {
await page.goto('/login');
await expect(page.getByRole('button', { name: 'Sign in' })).toBeVisible();
});
Inside the test (optional if annotations already bind the case):
import { test, expect } from '@playwright/test';
import { tracera } from '@tracera/playwright';
test('login', async ({ page }) => {
tracera.testCase(42, 'e2e/login');
await page.goto('/login');
await expect(page.getByRole('button', { name: 'Sign in' })).toBeVisible();
});
Steps
Use Playwright test.step — it is recorded under Test. tracera.step is the same reporting API if you prefer it (as on other runners). Steps inside test.beforeEach go to Setup; steps inside test.afterEach go to Teardown.
import { test } from '@playwright/test';
import { annotations } from '@tracera/playwright';
test.beforeEach(async ({ page }) => {
await test.step('open shop', async () => {
await page.goto('/shop');
});
});
test('checkout', { annotation: annotations(10, 'e2e/checkout') }, async ({ page }) => {
await test.step('add to cart', async () => {
await page.getByRole('button', { name: 'Add' }).click();
});
});
test.afterEach(async ({ page }) => {
await test.step('cleanup', async () => {
await page.goto('/cart/clear');
});
});
Comments, errors, and attachments
Inside a step — fields on that step. Outside a step — fields on the result.
import { test } from '@playwright/test';
import { tracera, annotations } from '@tracera/playwright';
test('login', { annotation: annotations(42, 'e2e/login') }, async ({ page }, testInfo) => {
tracera.comment('result-level note');
tracera.error('result-level error text');
await test.step('open login', async () => {
await page.goto('/login');
tracera.comment('landed on login');
});
await testInfo.attach('suite-log.txt', {
body: 'finished login flow',
contentType: 'text/plain',
});
});
Use testInfo.attach for custom files. Playwright attaches its screenshots, browser recording, and trace on its own — in Tracera, images open in a lightbox, the recording plays inline, and traces open in the Trace Viewer.
Parameterized tests
import { test, expect } from '@playwright/test';
import { annotations } from '@tracera/playwright';
const rows = [
{ id: 42, name: 'e2e/login-ok', path: '/login' },
{ id: 43, name: 'e2e/login-sso', path: '/login/sso' },
];
for (const row of rows) {
test(row.name, { annotation: annotations(row.id, row.name) }, async ({ page }) => {
await test.step('open', async () => {
await page.goto(row.path);
});
await expect(page.getByRole('button', { name: 'Sign in' })).toBeVisible();
});
}
Or call tracera.testCase(row.id, row.name) inside the body instead of annotations.
Custom fixtures
Extend stock Playwright test as usual:
import { test as base, expect } from '@playwright/test';
import { tracera } from '@tracera/playwright';
export const test = base.extend<{ asUser: void }>({
asUser: [
async ({ page }, use) => {
await page.goto('/login');
await page.getByLabel('Email').fill('user@example.com');
await page.getByRole('button', { name: 'Sign in' }).click();
await use();
},
{ auto: true },
],
});
test('dashboard', async ({ page }) => {
tracera.testCase(99, 'e2e/dashboard');
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});