yourname 9a5a40b1fa feat(e2e-test-utils): 完成 Story 1.2 类型定义和错误处理,通过代码审查 1 săptămână în urmă
..
src 9a5a40b1fa feat(e2e-test-utils): 完成 Story 1.2 类型定义和错误处理,通过代码审查 1 săptămână în urmă
tests c11ffcee87 fix(e2e-test-utils): 完成代码审查修复,Story 1.1 标记为完成 1 săptămână în urmă
PACKAGING.md c11ffcee87 fix(e2e-test-utils): 完成代码审查修复,Story 1.1 标记为完成 1 săptămână în urmă
README.md c11ffcee87 fix(e2e-test-utils): 完成代码审查修复,Story 1.1 标记为完成 1 săptămână în urmă
package.json 9a5a40b1fa feat(e2e-test-utils): 完成 Story 1.2 类型定义和错误处理,通过代码审查 1 săptămână în urmă
tsconfig.json c11ffcee87 fix(e2e-test-utils): 完成代码审查修复,Story 1.1 标记为完成 1 săptămână în urmă
vitest.config.ts 9bac4a49e6 feat(e2e-test-utils): 完成 Story 1.1 包基础结构和配置 1 săptămână în urmă

README.md

@d8d/e2e-test-utils

E2E 测试工具集 - 专门用于测试 Radix UI 组件的 Playwright 工具函数

TypeScript Playwright Vitest

📋 简介

@d8d/e2e-test-utils 是一个专为 Radix UI 组件设计的 E2E 测试工具集。它提供了一组强大且易于使用的函数,帮助你更高效地编写和维护端到端测试。

核心特性

  • 🎯 Radix UI 专用 - 针对无障碍组件优化的选择器策略
  • 🔒 TypeScript 全支持 - 完整的类型定义和 JSDoc 注释
  • 📦 零运行时依赖 - 仅依赖 Playwright 作为 peer dependency
  • 🧪 开箱即用的测试数据 - 包含常用测试场景的 fixtures
  • 🌳 Tree-shakeable - 按需导入,只打包使用的代码
  • 严格模式 - 启用 TypeScript 严格类型检查

📦 安装

Monorepo 项目(Workspace)

pnpm add -D @d8d/e2e-test-utils@workspace:*

独立项目

pnpm add -D @d8d/e2e-test-utils

Peer Dependencies

确保你的项目已安装 @playwright/test

pnpm add -D @playwright/test

🚀 快速入门

基础使用

import { test, expect } from '@playwright/test';
import { BaseOptions, E2ETestError, DEFAULT_TIMEOUTS } from '@d8d/e2e-test-utils';

test('示例测试', async ({ page }) => {
  // 使用工具函数进行测试
  const options: BaseOptions = {
    timeout: DEFAULT_TIMEOUTS.static
  };

  // 测试逻辑...
});

使用类型定义

import type { BaseOptions, ErrorContext } from '@d8d/e2e-test-utils';

// 定义自定义选项
const options: BaseOptions = {
  timeout: 5000
};

// 构建错误上下文
const errorContext: ErrorContext = {
  operation: 'selectOption',
  target: 'dropdown',
  expected: 'Option A',
  actual: 'Option B',
  available: ['Option A', 'Option B', 'Option C'],
  suggestion: '检查选项值是否正确'
};

使用错误类

import { E2ETestError } from '@d8d/e2e-test-utils';

test('错误处理示例', async ({ page }) => {
  const selectElement = await page.$('[data-testid="my-select"]');

  if (!selectElement) {
    throw new E2ETestError({
      operation: 'findSelect',
      target: '[data-testid="my-select"]',
      suggestion: '确认 data-testid 属性是否正确设置'
    });
  }
});

使用测试数据

注意:测试数据文件(fixtures)不会随包发布,需要复制到您的项目中使用。

# 1. 将测试数据复制到您的项目中
cp -r node_modules/@d8d/e2e-test-utils/tests/fixtures ./tests/

# 2. 在测试中使用相对路径导入
import testUsers from './fixtures/data/test-users.json' assert { type: 'json' };

test('用户登录', async ({ page }) => {
  const user = testUsers.users[0];

  await page.goto('/login');
  await page.fill('[name="email"]', user.email);
  await page.fill('[name="password"]', 'test_password');
  await page.click('button[type="submit"]');

  await expect(page).toHaveURL('/dashboard');
});

或者直接从源码引用(仅 Monorepo 开发环境):

import testUsers from '@d8d/e2e-test-utils/tests/fixtures/data/test-users.json' assert { type: 'json' };

📚 API 文档

类型定义

BaseOptions

基础配置选项,所有工具函数配置对象的基类。

interface BaseOptions {
  /** 超时时间(毫秒)*/
  timeout?: number;
}

ErrorContext

错误上下文信息,用于结构化错误报告。

interface ErrorContext {
  /** 操作类型(如 'selectRadixOption')*/
  operation: string;
  /** 目标(如下拉框标签)*/
  target: string;
  /** 期望值 */
  expected?: string;
  /** 实际值 */
  actual?: string;
  /** 可用选项列表 */
  available?: string[];
  /** 修复建议 */
  suggestion?: string;
}

错误类

E2ETestError

E2E 测试专用错误类,提供结构化的错误上下文信息。

class E2ETestError extends Error {
  constructor(
    public readonly context: ErrorContext,
    message?: string
  )
}

示例:

throw new E2ETestError({
  operation: 'selectOption',
  target: 'Role Selector',
  expected: 'Admin',
  actual: 'User',
  available: ['Admin', 'User', 'Guest'],
  suggestion: '确认选项值是否正确拼写'
});
// 输出:
// ❌ selectOption failed
// Target: Role Selector
// Expected: Admin
// Actual: User
// Available: Admin, User, Guest
// 💡 确认选项值是否正确拼写

常量

DEFAULT_TIMEOUTS

默认超时配置(毫秒)。

const DEFAULT_TIMEOUTS = {
  /** 静态选项超时 */
  static: 2000,
  /** 异步选项超时 */
  async: 5000,
  /** 网络空闲超时 */
  networkIdle: 10000
} as const;

使用示例:

import { DEFAULT_TIMEOUTS } from '@d8d/e2e-test-utils';

await page.waitForTimeout(DEFAULT_TIMEOUTS.static);

SELECTOR_STRATEGIES

支持的选择器策略列表。

const SELECTOR_STRATEGIES = [
  'data-testid',
  'aria-label + role',
  'text content + role'
] as const;

策略优先级:

  1. data-testid - 最高优先级,最稳定
  2. aria-label + role - 遵循无障碍标准
  3. text content + role - 兜底方案

🧪 测试

运行测试

# 运行所有单元测试
pnpm test:unit

# 运行测试并生成覆盖率报告
pnpm test:coverage

# 监听模式(开发时使用)
pnpm test

测试结构

tests/
├── fixtures/           # 测试资源
│   ├── data/          # 测试数据(JSON)
│   └── images/        # 测试图片
├── unit/              # 单元测试(Vitest)
├── integration/       # 集成测试(Playwright)
└── stability/         # 稳定性测试

使用 Fixtures

数据文件

// 导入测试用户数据
import testUsers from '@d8d/e2e-test-utils/tests/fixtures/data/test-users.json' assert { type: 'json' };

const user = testUsers.users[0];
console.log(user.name, user.email);

图片文件

图片文件用于测试文件上传功能。请参考 tests/fixtures/images/README.md 了解如何添加测试图片。

🛠️ 开发

项目结构

packages/e2e-test-utils/
├── src/
│   ├── index.ts              # 主导出,tree-shakeable
│   ├── types.ts              # 共享类型定义
│   ├── errors.ts             # 错误类和错误处理
│   ├── constants.ts          # 常量定义
│   ├── radix-select.ts       # Radix UI Select 工具(开发中)
│   ├── file-upload.ts        # 文件上传工具(规划中)
│   ├── form-helper.ts        # 表单辅助函数(规划中)
│   ├── dialog.ts             # 对话框操作(规划中)
│   └── dynamic-list.ts       # 动态列表管理(规划中)
├── tests/
│   ├── fixtures/             # 测试资源
│   ├── unit/                 # Vitest 单元测试
│   ├── integration/          # Playwright 集成测试
│   └── stability/            # 稳定性测试
├── package.json
├── tsconfig.json
├── vitest.config.ts
└── README.md

可用脚本

# 类型检查
pnpm typecheck

# 构建包
pnpm build

# 开发模式(监听文件变化)
pnpm dev

# 运行测试
pnpm test:unit

# 生成覆盖率报告
pnpm test:coverage

🤝 贡献

欢迎贡献!请遵循以下步骤:

  1. Fork 本仓库
  2. 创建特性分支 (git checkout -b feature/amazing-feature)
  3. 提交更改 (git commit -m 'Add some amazing feature')
  4. 推送到分支 (git push origin feature/amazing-feature)
  5. 开启 Pull Request

代码规范

  • 使用 TypeScript 严格模式
  • 所有导出的函数/类型必须有完整的 JSDoc 注释
  • 单元测试覆盖率 ≥ 80%
  • 遵循项目的 ESLint 和 Prettier 配置

📝 License

MIT

🔗 相关资源