Playwright JS

npm install @tracera/playwright

需要 Node.js ≥ 22.19 和 Playwright ≥ 1.40(见 自动化测试适配器)。@tracera/playwright。

在 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" 会在每个 worker 中修补 test.step 和 hooks,使 tracera.comment 和 tracera.error 落在当前步骤。

环境变量、运行模式与重试见 自动化测试适配器。

重试时 Env 键:PLAYWRIGHT_RETRY。

绑定 testCaseId 与 autotestName

从 @playwright/test 导入 test 和 expect,从 @tracera/playwright 导入 tracera 和 annotations。annotations 名称为字面量(无 {param})。

在声明时(推荐):

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();
});

在测试中(若 annotations 已绑定用例则可省略):

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();
});

Skip

使用 Playwright 标准 skip(test.skip、testInfo.skip(…))Skip reason 作为 comment 添加到结果

import { test } from '@playwright/test';
import { annotations } from '@tracera/playwright';

test('login', { annotation: annotations(42, 'e2e/login') }, async ({}, testInfo) => {
  testInfo.skip(!process.env.READY, 'env not ready');
});

步骤

使用 Playwright 的 test.step — 记入 测试。也可用同样的 tracera.step:调用 tracera.step(title, fn),在类方法上使用装饰器 @tracera.step("…"),或 tracera.step("…")(fn)。装饰器标题中的 {name} / {0} 从方法参数填入;也可以传入返回标题的函数。调用形式不会填充 {name} / {0}——请传入已拼好的标题字符串。会等待异步方法与 Promise。写在 test.beforeEach 里的步骤进入结果的 准备;写在 test.afterEach 里的步骤进入结果的 清理。套件级钩子(如 test.beforeAll / test.afterAll)不会上报。

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');
  });
});

注释、错误与附件

评论、错误与附件:自动化测试适配器。

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',
  });
});

要用 testInfo.attach 把文件加到结果上。Playwright 会自行附上截图、视频和 Playwright trace — 在 Tracera 中,图片在灯箱中打开,视频可内联播放,trace 在 Trace Viewer 中打开。

参数化测试

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();
  });
}

也可以在测试中调用 tracera.testCase(row.id, row.name),而不使用 annotations。

自定义 fixtures

像往常一样扩展 Playwright 标准 test:

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();
});