web-testing-playwright-e2e

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Playwright E2E Testing Patterns

Playwright E2E Testing Patterns

Quick Guide: Use Playwright for end-to-end tests that verify complete user workflows through the real browser. Focus on critical user journeys, use accessibility-based locators (
getByRole
), and leverage auto-waiting assertions -- never use manual sleeps. Isolate each test with its own browser context. Mock external APIs via route interception for reliability.

<critical_requirements>
快速指南:使用Playwright进行端到端测试,通过真实浏览器验证完整的用户工作流。聚焦关键用户旅程,使用基于可访问性的定位器(
getByRole
),并利用自动等待断言——切勿使用手动休眠。为每个测试分配独立的浏览器上下文。通过路由拦截模拟外部API以确保测试可靠性。

<critical_requirements>

CRITICAL: Before Using This Skill

重要提示:使用此技能前须知

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type
, named constants)
(You MUST use getByRole() as your primary locator strategy - it mirrors how users interact with the page)
(You MUST test complete user workflows end-to-end - login flows, checkout processes, form submissions)
(You MUST use web-first assertions that auto-wait - toBeVisible(), toHaveText(), not manual sleeps)
(You MUST isolate tests - each test runs independently with its own browser context)
(You MUST use named constants for test data - no magic strings or numbers in test files)
</critical_requirements>

Auto-detection: Playwright, E2E testing, end-to-end testing, browser automation, page.goto, test.describe, expect(page), getByRole, getByTestId, toBeVisible, toHaveScreenshot, toMatchAriaSnapshot
When to use:
  • Testing critical user-facing workflows (login, checkout, form submission)
  • Multi-step user journeys that span multiple pages
  • Cross-browser compatibility testing
  • Testing real integration with backend APIs
  • Visual regression testing with screenshots
  • Accessibility tree validation with ARIA snapshots
When NOT to use:
  • Testing pure utility functions (use unit tests)
  • Testing individual component variants in isolation (use component testing tools)
  • API-only testing without UI (use API testing)
Key patterns covered:
  • Test structure and organization (test.describe, test, hooks)
  • Page Object Model pattern for maintainability
  • Locator strategies prioritizing accessibility (getByRole, getByLabel, chaining, operators)
  • Web-first assertions with auto-waiting (toBeVisible, toHaveText, soft assertions)
  • Network mocking and interception (route.fulfill, route.abort, response modification)
  • Visual regression testing (toHaveScreenshot, masking, thresholds)
  • Custom and worker-scoped fixtures
  • Clock API for time-dependent testing (v1.45+)
  • ARIA snapshot testing for accessibility (v1.49+)
  • Accessibility assertions (v1.44+): toHaveAccessibleName, toHaveRole
Detailed Resources:
  • examples/core.md - User flows, page objects, network mocking, config, auth fixtures
  • examples/page-objects.md - Base page inheritance, hierarchy
  • examples/api-mocking.md - Response interception and modification
  • examples/visual-testing.md - Screenshot comparison, component visual testing
  • examples/fixtures.md - Combined fixtures, database seeding, IndexedDB state
  • examples/advanced-features.md - Clock API, ARIA snapshots, worker fixtures, polling assertions, accessibility assertions
  • reference.md - Decision frameworks, locator/assertion tables, anti-patterns, CLI commands, configuration reference

<philosophy>
所有代码必须遵循CLAUDE.md中的项目约定(短横线命名法、命名导出、导入顺序、
import type
、命名常量)
(必须将getByRole()作为主要定位器策略——它模拟用户与页面的交互方式)
(必须端到端测试完整的用户工作流——登录流程、结账流程、表单提交)
(必须使用支持自动等待的Web优先断言——toBeVisible()、toHaveText(),而非手动休眠)
(必须隔离测试——每个测试在独立的浏览器上下文中运行)
(必须为测试数据使用命名常量——测试文件中不得存在魔法字符串或数字)
</critical_requirements>

自动检测关键词:Playwright, E2E testing, end-to-end testing, browser automation, page.goto, test.describe, expect(page), getByRole, getByTestId, toBeVisible, toHaveScreenshot, toMatchAriaSnapshot
适用场景:
  • 测试关键用户面向的工作流(登录、结账、表单提交)
  • 跨多个页面的多步骤用户旅程
  • 跨浏览器兼容性测试
  • 测试与后端API的真实集成
  • 结合截图的视觉回归测试
  • 通过ARIA快照验证可访问性树
不适用场景:
  • 测试纯工具函数(应使用单元测试)
  • 孤立测试单个组件变体(应使用组件测试工具)
  • 无UI的纯API测试(应使用API测试)
涵盖的核心模式:
  • 测试结构与组织(test.describe、test、钩子函数)
  • 提升可维护性的页面对象模型模式
  • 优先考虑可访问性的定位器策略(getByRole、getByLabel、链式调用、操作符)
  • 支持自动等待的Web优先断言(toBeVisible、toHaveText、软断言)
  • 网络模拟与拦截(route.fulfill、route.abort、响应修改)
  • 视觉回归测试(toHaveScreenshot、遮罩、阈值)
  • 自定义与工作器作用域的测试夹具(fixtures)
  • 用于时间相关测试的Clock API(v1.45+)
  • 用于可访问性的ARIA快照测试(v1.49+)
  • 可访问性断言(v1.44+):toHaveAccessibleName、toHaveRole
详细资源:
  • examples/core.md - 用户流程、页面对象、网络模拟、配置、认证夹具
  • examples/page-objects.md - 基础页面继承、层级结构
  • examples/api-mocking.md - 响应拦截与修改
  • examples/visual-testing.md - 截图对比、组件视觉测试
  • examples/fixtures.md - 组合夹具、数据库初始化、IndexedDB状态
  • examples/advanced-features.md - Clock API、ARIA快照、工作器夹具、轮询断言、可访问性断言
  • reference.md - 决策框架、定位器/断言对照表、反模式、CLI命令、配置参考

<philosophy>

Philosophy

理念

Playwright E2E tests verify that your application works correctly from the user's perspective. They interact with the real browser, navigate through actual pages, and validate user-visible behavior.
Core Principles:
  1. Test user-visible behavior - Focus on what end users see and interact with, not implementation details
  2. Use accessibility locators -
    getByRole
    mirrors how screen readers and users interact with pages
  3. Isolate tests completely - Each test has its own browser context, cookies, and storage
  4. Trust auto-waiting - Playwright automatically waits for elements; no manual sleeps needed
  5. Mock external dependencies - Use route interception for third-party APIs to ensure reliability
When E2E tests provide the most value:
  • Critical business workflows (authentication, payments, core features)
  • User journeys spanning multiple pages or components
  • Testing real backend integration
  • Cross-browser compatibility verification
  • Catching integration bugs that unit tests miss
When E2E tests may not be the best choice:
  • Testing pure utility functions (unit tests are faster and more precise)
  • Testing component styling variants (use visual testing tools)
  • Testing every edge case (balance with unit and integration tests)
</philosophy>
<patterns>
Playwright E2E测试从用户视角验证应用是否正常工作。它们与真实浏览器交互、导航实际页面,并验证用户可见的行为。
核心原则:
  1. 测试用户可见行为 - 聚焦终端用户看到和交互的内容,而非实现细节
  2. 使用可访问性定位器 -
    getByRole
    模拟屏幕阅读器和用户与页面的交互方式
  3. 完全隔离测试 - 每个测试拥有独立的浏览器上下文、Cookie和存储
  4. 信任自动等待机制 - Playwright会自动等待元素加载,无需手动休眠
  5. 模拟外部依赖 - 使用路由拦截处理第三方API,确保测试可靠性
E2E测试价值最大化的场景:
  • 关键业务工作流(认证、支付、核心功能)
  • 跨多个页面或组件的用户旅程
  • 测试真实后端集成
  • 跨浏览器兼容性验证
  • 捕捉单元测试遗漏的集成bug
E2E测试并非最佳选择的场景:
  • 测试纯工具函数(单元测试更快更精准)
  • 测试组件样式变体(应使用视觉测试工具)
  • 测试每个边缘情况(需与单元测试和集成测试平衡)
</philosophy>
<patterns>

Core Patterns

核心模式

Pattern 1: Test Structure and Organization

模式1:测试结构与组织

Group related tests with
test.describe
, use
beforeEach
for common navigation, and name constants for all test data.
typescript
const LOGIN_URL = "/login";
const VALID_EMAIL = "user@example.com";

test.describe("Login Flow", () => {
  test.beforeEach(async ({ page }) => {
    await page.goto(LOGIN_URL);
  });

  test("successful login redirects to dashboard", async ({ page }) => {
    await page.getByLabel(/email/i).fill(VALID_EMAIL);
    // ... fill password, click sign in
    await expect(page).toHaveURL("/dashboard");
  });
});
Why good: Groups related tests logically,
beforeEach
maintains isolation, named constants prevent magic strings
See examples/core.md Pattern 1 for complete user flow with error scenarios.

使用
test.describe
分组相关测试,用
beforeEach
处理通用导航,为所有测试数据定义命名常量。
typescript
const LOGIN_URL = "/login";
const VALID_EMAIL = "user@example.com";

test.describe("Login Flow", () => {
  test.beforeEach(async ({ page }) => {
    await page.goto(LOGIN_URL);
  });

  test("successful login redirects to dashboard", async ({ page }) => {
    await page.getByLabel(/email/i).fill(VALID_EMAIL);
    // ... 填写密码,点击登录
    await expect(page).toHaveURL("/dashboard");
  });
});
优势:按逻辑分组相关测试,
beforeEach
维持测试隔离,命名常量避免魔法字符串
查看examples/core.md模式1获取包含错误场景的完整用户流程示例。

Pattern 2: Page Object Model

模式2:页面对象模型

Encapsulate page structure and interactions in reusable classes. Define locators in the constructor, expose domain-specific methods.
typescript
export class LoginPage {
  readonly emailInput: Locator;
  readonly signInButton: Locator;

  constructor(page: Page) {
    this.emailInput = page.getByLabel(/email/i);
    this.signInButton = page.getByRole("button", { name: /sign in/i });
  }

  async login(email: string, password: string) {
    await this.emailInput.fill(email);
    // ...
  }
}
Why good: Centralizes locators -- UI changes update one place, methods encapsulate interactions
When to use: Tests spanning multiple interactions on the same page, reusable flows across test files.
When not to use: Simple one-off tests where inline locators are clearer.
See examples/core.md Pattern 2 for full page objects with fixtures, examples/page-objects.md for base page inheritance.

将页面结构和交互封装到可复用类中。在构造函数中定义定位器,暴露领域特定方法。
typescript
export class LoginPage {
  readonly emailInput: Locator;
  readonly signInButton: Locator;

  constructor(page: Page) {
    this.emailInput = page.getByLabel(/email/i);
    this.signInButton = page.getByRole("button", { name: /sign in/i });
  }

  async login(email: string, password: string) {
    await this.emailInput.fill(email);
    // ...
  }
}
优势:集中管理定位器——UI变更只需更新一处,方法封装交互逻辑
适用场景:同一页面涉及多步交互的测试、跨测试文件的可复用流程。
不适用场景:简单一次性测试,内联定位器更清晰的情况。
查看examples/core.md模式2获取带夹具的完整页面对象,examples/page-objects.md获取基础页面继承示例。

Pattern 3: Locator Strategies

模式3:定位器策略

Prioritize accessibility-based locators that mirror how users interact with the application.
typescript
// BEST: Accessibility-based
await page.getByRole("button", { name: /submit/i });
await page.getByLabel(/email address/i);
await page.getByText(/welcome back/i);

// ACCEPTABLE: Test IDs for complex cases
await page.getByTestId("user-avatar"); // When no semantic role exists

// AVOID: Implementation-dependent
await page.locator("#submit-btn"); // Fragile
await page.locator(".btn-primary"); // CSS class can change
Chaining and filtering narrow down to specific elements without fragile selectors:
typescript
await page
  .getByRole("listitem")
  .filter({ hasText: "Product A" })
  .getByRole("button", { name: /add to cart/i })
  .click();

// Exclude elements (v1.33+)
await page
  .getByRole("listitem")
  .filter({ hasNot: page.getByText("Out of stock") })
  .first()
  .click();

// Combine conditions (v1.33+)
const btn = page.getByRole("button").and(page.getByTitle("Subscribe"));
Why good:
getByRole
validates accessibility as a side effect, survives UI refactoring, chaining handles dynamic lists
See reference.md for locator priority table and common ARIA role mappings.

优先使用模拟用户与应用交互方式的基于可访问性的定位器。
typescript
// 最佳选择:基于可访问性
await page.getByRole("button", { name: /submit/i });
await page.getByLabel(/email address/i);
await page.getByText(/welcome back/i);

// 可接受:复杂场景下使用测试ID
await page.getByTestId("user-avatar"); // 当不存在语义化角色时

// 应避免:依赖实现细节
await page.locator("#submit-btn"); // 易失效
await page.locator(".btn-primary"); // CSS类可能变更
链式调用与过滤无需脆弱选择器即可缩小到特定元素:
typescript
await page
  .getByRole("listitem")
  .filter({ hasText: "Product A" })
  .getByRole("button", { name: /add to cart/i })
  .click();

// 排除元素(v1.33+)
await page
  .getByRole("listitem")
  .filter({ hasNot: page.getByText("Out of stock") })
  .first()
  .click();

// 组合条件(v1.33+)
const btn = page.getByRole("button").and(page.getByTitle("Subscribe"));
优势:
getByRole
附带验证可访问性的效果,能在UI重构后依然有效,链式调用可处理动态列表
查看reference.md获取定位器优先级表和常见ARIA角色映射。

Pattern 4: Web-First Assertions

模式4:Web优先断言

Use assertions that automatically wait and retry until the condition is met.
typescript
// Auto-waits for element visibility
await expect(page.getByText("Welcome")).toBeVisible();
// Auto-waits for URL
await expect(page).toHaveURL(/\/dashboard/);
// Negated assertions also auto-wait
await expect(page.getByRole("progressbar")).not.toBeVisible();
Why good: Eliminates flaky tests from race conditions, no manual sleeps needed
typescript
// BAD: Manual waiting
await page.waitForTimeout(2000); // Arbitrary sleep!
const text = await page.textContent(".result");
expect(text).toBe("Success"); // Non-waiting assertion
Why bad: Fixed timeouts are either too short (flaky) or too long (slow), doesn't adapt to actual page load time
Soft assertions collect all failures in one run:
typescript
await expect.soft(page.getByTestId("avatar")).toBeVisible();
await expect.soft(page.getByText("Premium")).toBeVisible();
// Test continues, all failures reported at end
See reference.md for complete assertion table, polling assertions, and accessibility assertions (v1.44+).

使用自动等待并重试直到条件满足的断言。
typescript
// 自动等待元素可见
await expect(page.getByText("Welcome")).toBeVisible();
// 自动等待URL变更
await expect(page).toHaveURL(/\/dashboard/);
// 否定断言同样支持自动等待
await expect(page.getByRole("progressbar")).not.toBeVisible();
优势:消除竞争条件导致的不稳定测试,无需手动休眠
typescript
// 错误做法:手动等待
await page.waitForTimeout(2000); // 任意休眠!
const text = await page.textContent(".result");
expect(text).toBe("Success"); // 无等待的断言
劣势:固定超时要么太短(导致不稳定)要么太长(拖慢测试),无法适应实际页面加载时间
软断言会收集一次运行中的所有失败:
typescript
await expect.soft(page.getByTestId("avatar")).toBeVisible();
await expect.soft(page.getByText("Premium")).toBeVisible();
// 测试继续执行,最终报告所有失败
查看reference.md获取完整断言表、轮询断言及可访问性断言(v1.44+)。

Pattern 5: Network Mocking and Interception

模式5:网络模拟与拦截

Mock external APIs for reliable, isolated tests. Use
page.route()
to intercept and fulfill requests.
typescript
const API_USERS = "**/api/users";

await page.route(API_USERS, (route) =>
  route.fulfill({
    status: 200,
    contentType: "application/json",
    body: JSON.stringify({ id: "user-123", name: "John Doe" }),
  }),
);

// Error simulation
await page.route(API_USERS, (route) => route.abort("failed")); // Network failure
Why good: Eliminates third-party flakiness, enables testing error states, controls exact data
Modifying real responses (hybrid approach):
typescript
await page.route("**/api/products", async (route) => {
  const response = await route.fetch();
  const json = await response.json();
  json.products = json.products.map((p: { price: number }) => ({
    ...p,
    price: p.price * 0.9,
  }));
  await route.fulfill({ response, json });
});
See examples/core.md Pattern 3 for complete mocking with error states, examples/api-mocking.md for response modification.

模拟外部API以实现可靠、隔离的测试。使用
page.route()
拦截并响应请求。
typescript
const API_USERS = "**/api/users";

await page.route(API_USERS, (route) =>
  route.fulfill({
    status: 200,
    contentType: "application/json",
    body: JSON.stringify({ id: "user-123", name: "John Doe" }),
  }),
);

// 模拟错误
await page.route(API_USERS, (route) => route.abort("failed")); // 网络失败
优势:消除第三方服务导致的不稳定,支持测试错误状态,可精确控制数据
修改真实响应(混合方式):
typescript
await page.route("**/api/products", async (route) => {
  const response = await route.fetch();
  const json = await response.json();
  json.products = json.products.map((p: { price: number }) => ({
    ...p,
    price: p.price * 0.9,
  }));
  await route.fulfill({ response, json });
});
查看examples/core.md模式3获取包含错误状态的完整模拟示例,examples/api-mocking.md获取响应修改示例。

Pattern 6: Visual Regression Testing

模式6:视觉回归测试

Capture and compare screenshots to detect unintended visual changes.
typescript
await expect(page).toHaveScreenshot("homepage.png");

// Mask dynamic content
await expect(page).toHaveScreenshot("dashboard.png", {
  mask: [page.getByTestId("current-time"), page.getByTestId("random-ad")],
});

// Disable animations for deterministic screenshots
await expect(page).toHaveScreenshot("stable.png", { animations: "disabled" });
Why good: Catches unintended visual changes, masking prevents false positives from timestamps
See examples/visual-testing.md for component visual testing with state variations.

捕获并对比截图以检测意外的视觉变化。
typescript
await expect(page).toHaveScreenshot("homepage.png");

// 遮罩动态内容
await expect(page).toHaveScreenshot("dashboard.png", {
  mask: [page.getByTestId("current-time"), page.getByTestId("random-ad")],
});

// 禁用动画以生成确定性截图
await expect(page).toHaveScreenshot("stable.png", { animations: "disabled" });
优势:捕获意外视觉变化,遮罩避免时间戳等内容导致的误报
查看examples/visual-testing.md获取带状态变体的组件视觉测试示例。

Pattern 7: Custom Fixtures

模式7:自定义夹具

Extend the base test with reusable fixtures for page objects, authentication, and shared setup.
typescript
export const test = base.extend<{
  loginPage: LoginPage;
  authenticatedPage: void;
}>({
  loginPage: async ({ page }, use) => {
    await use(new LoginPage(page));
  },
  authenticatedPage: [
    async ({ context }, use) => {
      await context.addCookies([
        { name: "session", value: "token", domain: "localhost", path: "/" },
      ]);
      await use();
      await context.clearCookies();
    },
    { auto: true },
  ],
});
Why good: Encapsulates setup + teardown, auto fixtures eliminate repetitive auth, composable
See examples/core.md Pattern 5 for auth fixtures, examples/fixtures.md for combined fixtures and database seeding, examples/advanced-features.md for worker-scoped fixtures.

扩展基础测试,为页面对象、认证和共享初始化添加可复用夹具。
typescript
export const test = base.extend<{
  loginPage: LoginPage;
  authenticatedPage: void;
}>({
  loginPage: async ({ page }, use) => {
    await use(new LoginPage(page));
  },
  authenticatedPage: [
    async ({ context }, use) => {
      await context.addCookies([
        { name: "session", value: "token", domain: "localhost", path: "/" },
      ]);
      await use();
      await context.clearCookies();
    },
    { auto: true },
  ],
});
优势:封装初始化+清理逻辑,自动夹具消除重复的认证代码,可组合使用
查看examples/core.md模式5获取认证夹具示例,examples/fixtures.md获取组合夹具和数据库初始化示例,examples/advanced-features.md获取工作器作用域夹具示例。

Pattern 8: Clock API (v1.45+)

模式8:Clock API(v1.45+)

Control time for testing countdowns, session timeouts, and scheduled events.
typescript
await page.clock.install({ time: new Date("2024-02-02T08:00:00") });
await page.goto("/dashboard");

await page.clock.fastForward("25:00"); // Jump 25 minutes
await expect(page.getByText(/session expires/i)).toBeVisible();
CRITICAL:
clock.install()
MUST be called before any other clock methods.
See examples/advanced-features.md for countdown testing and session timeout patterns.

控制时间以测试倒计时、会话超时和定时事件。
typescript
await page.clock.install({ time: new Date("2024-02-02T08:00:00") });
await page.goto("/dashboard");

await page.clock.fastForward("25:00"); // 快进25分钟
await expect(page.getByText(/session expires/i)).toBeVisible();
重要提示:
clock.install()
必须在任何其他时钟方法之前调用。
查看examples/advanced-features.md获取倒计时测试和会话超时模式示例。

Pattern 9: ARIA Snapshot Testing (v1.49+)

模式9:ARIA快照测试(v1.49+)

Validate accessibility tree structure programmatically.
typescript
await expect(page.getByRole("navigation")).toMatchAriaSnapshot(`
  - navigation:
    - link "Home"
    - link "Products"
    - link "About"
`);
Why good: Catches ARIA issues before production, documents expected accessibility behavior
See examples/advanced-features.md for complex component ARIA snapshots.
</patterns>
<red_flags>
以编程方式验证可访问性树结构。
typescript
await expect(page.getByRole("navigation")).toMatchAriaSnapshot(`
  - navigation:
    - link "Home"
    - link "Products"
    - link "About"
`);
优势:在生产前发现ARIA问题,记录预期的可访问性行为
查看examples/advanced-features.md获取复杂组件的ARIA快照示例。
</patterns>
<red_flags>

RED FLAGS

风险警示

High Priority Issues:
  • Using
    page.waitForTimeout()
    with fixed delays -- causes flaky or slow tests, use auto-waiting assertions instead
  • Using CSS selectors like
    .btn-primary
    or
    #submit-btn
    -- fragile and break on refactoring, use
    getByRole
  • Not testing error states -- only happy paths leaves error handling untested
  • Tests sharing state or data -- causes flaky failures in parallel execution, isolate each test completely
Medium Priority Issues:
  • Using
    getByTestId
    as primary locator -- misses accessibility validation, prioritize
    getByRole
  • No network mocking for external APIs -- third-party flakiness affects your tests
  • Running E2E tests only on one browser -- cross-browser issues go undetected
  • Screenshots without masking dynamic content -- timestamps and ads cause false positives
Common Mistakes:
  • Hardcoded test data scattered throughout files -- use named constants
  • Testing implementation details (e.g., Redux state via
    window.__REDUX_STATE__
    ) instead of user behavior
  • Not using
    beforeEach
    for common setup -- leads to duplicated code
  • Mixing E2E tests with unit tests in the same directory
Gotchas & Edge Cases:
  • toBeVisible()
    auto-waits for the element;
    toBeAttached()
    checks DOM presence without visibility -- prefer visibility checks for most cases
  • Screenshots vary by OS and browser -- run visual tests in consistent CI environment
  • beforeAll
    runs once per worker, not once globally -- use
    globalSetup
    in config for true one-time setup
  • Network routes are global to context -- routes set in
    beforeEach
    override previous; always set up fresh per test
  • Parallel tests cannot share state -- use fixtures for per-test setup, not shared variables
  • toBeEditable()
    throws on non-editable elements (v1.50+) -- verify element type first
  • Glob URL patterns in
    page.route()
    no longer support
    ?
    and
    []
    (v1.52+) -- use regex instead
  • route.continue()
    cannot override Cookie header (v1.52+) -- use
    context.addCookies()
    instead
  • _react
    and
    _vue
    selectors removed (v1.58) -- use data-testid or role-based locators
</red_flags>

<critical_reminders>
高优先级问题:
  • 使用
    page.waitForTimeout()
    设置固定延迟——导致测试不稳定或缓慢,应使用自动等待断言
  • 使用
    .btn-primary
    或
    #submit-btn
    等CSS选择器——易失效,UI重构时会中断,应使用
    getByRole
  • 未测试错误状态——仅测试正常路径会导致错误处理未被验证
  • 测试共享状态或数据——并行执行时会导致不稳定失败,需完全隔离每个测试
中优先级问题:
  • 将
    getByTestId
    作为主要定位器——遗漏可访问性验证,应优先使用
    getByRole
  • 未对外部API进行网络模拟——第三方服务的不稳定会影响测试
  • 仅在单个浏览器上运行E2E测试——跨浏览器问题无法被发现
  • 截图未遮罩动态内容——时间戳和广告会导致误报
常见错误:
  • 测试数据硬编码在多个文件中——应使用命名常量
  • 测试实现细节(如通过
    window.__REDUX_STATE__
    测试Redux状态)而非用户行为
  • 未使用
    beforeEach
    处理通用初始化——导致代码重复
  • E2E测试与单元测试混放在同一目录
注意事项与边缘情况:
  • toBeVisible()
    会自动等待元素;
    toBeAttached()
    仅检查DOM存在性不验证可见性——大多数场景优先选择可见性检查
  • 截图会因操作系统和浏览器而异——在一致的CI环境中运行视觉测试
  • beforeAll
    在每个工作器中运行一次,而非全局一次——如需真正的一次性初始化,使用配置中的
    globalSetup
  • 网络路由对上下文全局生效——
    beforeEach
    中设置的路由会覆盖之前的;需为每个测试重新设置
  • 并行测试无法共享状态——使用夹具处理每个测试的初始化,而非共享变量
  • toBeEditable()
    在非可编辑元素上会抛出错误(v1.50+)——先验证元素类型
  • page.route()
    中的Glob URL模式不再支持
    ?
    和
    []
    (v1.52+)——改用正则表达式
  • route.continue()
    无法覆盖Cookie头(v1.52+)——改用
    context.addCookies()
  • _react
    和
    _vue
    选择器已移除(v1.58)——使用data-testid或基于角色的定位器
</red_flags>

<critical_reminders>

CRITICAL REMINDERS

重要提醒

All code must follow project conventions in CLAUDE.md
(You MUST use getByRole() as your primary locator strategy - it mirrors how users interact with the page)
(You MUST test complete user workflows end-to-end - login flows, checkout processes, form submissions)
(You MUST use web-first assertions that auto-wait - toBeVisible(), toHaveText(), not manual sleeps)
(You MUST isolate tests - each test runs independently with its own browser context)
(You MUST use named constants for test data - no magic strings or numbers in test files)
Failure to follow these rules will result in flaky tests, false positives, and maintenance nightmares.
</critical_reminders>
所有代码必须遵循CLAUDE.md中的项目约定
(必须将getByRole()作为主要定位器策略——它模拟用户与页面的交互方式)
(必须端到端测试完整的用户工作流——登录流程、结账流程、表单提交)
(必须使用支持自动等待的Web优先断言——toBeVisible()、toHaveText(),而非手动休眠)
(必须隔离测试——每个测试在独立的浏览器上下文中运行)
(必须为测试数据使用命名常量——测试文件中不得存在魔法字符串或数字)
不遵守这些规则会导致测试不稳定、误报和维护噩梦。
</critical_reminders>