Status: done
作为测试开发者,
我想要可以安装 @d8d/e2e-test-utils 包,
以便在项目中使用测试工具函数。
Given 项目是 Monorepo 结构
When 创建 packages/e2e-test-utils/ 目录
Then 目录结构包含 src/, tests/, package.json, tsconfig.json, vitest.config.ts
And package.json 包含正确的包名、版本、peer dependencies(@playwright/test)
And tsconfig.json 启用严格模式,目标 ES2020+
And 可以通过 pnpm add -D @d8d/e2e-test-utils@workspace:* 安装
packages/e2e-test-utils/ 根目录src/ 源代码目录tests/ 测试目录tests/fixtures/ 测试资源目录tests/unit/ 单元测试目录tests/integration/ 集成测试目录tests/stability/ 稳定性测试目录@d8d/e2e-test-utils1.0.0@playwright/teststrict: true)target: "ES2020")module: "ESNext")baseUrl: ".", paths)tests/unitsrc/index.ts 导出入口src/types.ts 类型定义占位src/errors.ts 错误类占位src/constants.ts 常量占位README.md 基础文档pnpm install@d8d/e2e-test-utils@workspace:*pnpm typecheck 验证类型检查通过web/ 或其他包中运行 pnpm add -D @d8d/e2e-test-utils@workspace:* 并验证可以正常导入 [1-1-create-package-structure.md:56]tests/unit/ 中创建占位测试文件以验证 Vitest 配置正常工作 [packages/e2e-test-utils/tests/unit/]test-users.json 和 README 说明在 tests/fixtures/ 目录中 [packages/e2e-test-utils/tests/fixtures/]@d8d/e2e-test-utils 路径配置 [packages/e2e-test-utils/tsconfig.json:26-28]src/index.ts 添加文件级别的 JSDoc 注释 [packages/e2e-test-utils/src/index.ts:1]9bac4a4)[git status]审查发现: 9 个问题(3 HIGH, 3 MEDIUM, 2 LOW) 修复状态: 所有 HIGH 和 MEDIUM 问题已自动修复(6/6)
已修复的问题:
未修复的 LOW 问题(可后续处理):
测试验证结果:
Epic 1 目标: 测试开发者可以安装 @d8d/e2e-test-utils 包,立即使用 Select 工具测试 Radix UI Select 组件(最常用、最关键的测试场景)。
本故事在 Epic 中的位置: 第一个故事,建立包基础设施,为后续所有工具函数提供基础。
从架构文档中提取的关键架构决策:
包结构决策(按功能分组):
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
API 设计模式: 3个必需参数 + 可选配置对象
类型系统策略: 分层类型(共享+特定)
types.ts 存放共享类型(BaseOptions)AsyncSelectOptions)选择器策略: 混合策略(testid → ARIA → 文本)
data-testid - 最高优先级aria-label + role - 无障碍标准错误处理策略: 结构化错误类 + 友好消息
export class E2ETestError extends Error {
constructor(
public readonly context: ErrorContext,
message?: string
) { ... }
}
语言与类型系统:
any 类型依赖管理:
@playwright/test(由测试项目提供)参考现有的 @d8d/shared-test-util 包结构:
{
"name": "@d8d/e2e-test-utils",
"version": "1.0.0",
"description": "E2E testing utilities for Radix UI components",
"type": "module",
"main": "src/index.ts",
"types": "src/index.ts",
"exports": {
".": {
"import": "./src/index.ts",
"require": "./src/index.ts",
"types": "./src/index.ts"
}
},
"files": [
"src"
],
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"test": "vitest",
"test:unit": "vitest run tests/unit/**/*.test.ts",
"test:coverage": "vitest --coverage",
"typecheck": "tsc --noEmit"
},
"peerDependencies": {
"@playwright/test": "^1.40.0"
},
"devDependencies": {
"typescript": "^5.8.3",
"vitest": "^3.2.4",
"@vitest/coverage-v8": "^3.2.4"
},
"keywords": [
"e2e",
"testing",
"playwright",
"radix-ui",
"test-utilities"
]
}
关键点:
type: "module" 启用 ES 模块main 和 types 都指向 src/index.ts(源代码直接使用)exports 配置支持 tree-shakingpeerDependencies 指定 Playwright 版本范围(兼容最新稳定版和上一个 LTS 版本)参考项目 TypeScript 严格模式配置:
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
"moduleResolution": "bundler",
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"baseUrl": ".",
"paths": {
"@d8d/e2e-test-utils": ["./src/index.ts"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "tests"]
}
关键点:
strict: true + 额外检查)target: "ES2020")declaration: true)rootDir 和 outDirimport { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
dir: './tests/unit',
environment: 'node',
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
statements: 80,
branches: 80,
functions: 80,
lines: 80,
exclude: [
'node_modules/',
'tests/',
'**/*.test.ts',
'**/*.spec.ts',
'src/index.ts'
]
},
testTimeout: 10000,
globals: false
}
});
关键点:
tests/unitsrc/index.ts 从覆盖率(主导出文件)src/index.ts - 主导出文件(tree-shakeable):
// 导出类型定义
export * from './types';
// 导出错误类
export * from './errors';
// 导出常量
export * from './constants';
// Radix UI Select 工具(后续故事实现)
// export * from './radix-select';
src/types.ts - 共享类型定义:
import type { Page } from '@playwright/test';
/**
* 基础配置选项,所有配置对象的基类
*/
export interface BaseOptions {
/** 超时时间(毫秒)*/
timeout?: number;
}
/**
* 错误上下文接口
*/
export interface ErrorContext {
/** 操作类型(如 'selectRadixOption')*/
operation: string;
/** 目标(如下拉框标签)*/
target: string;
/** 期望值 */
expected?: string;
/** 实际值 */
actual?: string;
/** 可用选项列表 */
available?: string[];
/** 修复建议 */
suggestion?: string;
}
// 重新导出 Playwright 类型以便使用者使用
export type { Page } from '@playwright/test';
src/errors.ts - 错误处理:
import type { ErrorContext } from './types';
/**
* E2E 测试专用错误类
* 提供结构化的错误上下文信息
*/
export class E2ETestError extends Error {
constructor(
public readonly context: ErrorContext,
message?: string
) {
super(message || formatErrorMessage(context));
this.name = 'E2ETestError';
}
}
/**
* 格式化错误消息
*/
function formatErrorMessage(context: ErrorContext): string {
const parts = [
`❌ ${context.operation} failed`,
`Target: ${context.target}`
];
if (context.expected) {
parts.push(`Expected: ${context.expected}`);
}
if (context.actual) {
parts.push(`Actual: ${context.actual}`);
}
if (context.available && context.available.length > 0) {
parts.push(`Available: ${context.available.join(', ')}`);
}
if (context.suggestion) {
parts.push(`\n💡 ${context.suggestion}`);
}
return parts.join('\n');
}
src/constants.ts - 常量定义:
/**
* 默认超时配置(毫秒)
*/
export const DEFAULT_TIMEOUTS = {
/** 静态选项超时 */
static: 2000,
/** 异步选项超时 */
async: 5000,
/** 网络空闲超时 */
networkIdle: 10000
} as const;
/**
* 选择器策略常量
*/
export const SELECTOR_STRATEGIES = [
'data-testid',
'aria-label + role',
'text content + role'
] as const;
# 在项目根目录执行
mkdir -p packages/e2e-test-utils/src
mkdir -p packages/e2e-test-utils/tests/fixtures/images
mkdir -p packages/e2e-test-utils/tests/fixtures/data
mkdir -p packages/e2e-test-utils/tests/unit
mkdir -p packages/e2e-test-utils/tests/integration
mkdir -p packages/e2e-test-utils/tests/stability
1. 验证目录结构:
ls -la packages/e2e-test-utils/
# 应显示:src/, tests/, package.json, tsconfig.json, vitest.config.ts, README.md
ls -la packages/e2e-test-utils/src/
# 应显示:index.ts, types.ts, errors.ts, constants.ts
ls -la packages/e2e-test-utils/tests/
# 应显示:fixtures/, unit/, integration/, stability/
2. 验证 TypeScript 配置:
cd packages/e2e-test-utils
pnpm typecheck
# 应无错误输出
3. 验证包可以被安装:
# 在 monorepo 根目录
pnpm install
# 检查是否成功注册 workspace 包
# 验证可以被引用(在 web/ 目录)
pnpm add -D @d8d/e2e-test-utils@workspace:*
4. 验证 Vitest 配置:
cd packages/e2e-test-utils
pnpm test:unit
# 应显示 Vitest 成功启动(即使没有测试)
对齐项目 Monorepo 架构:
pnpm workspace 协议@d8d/ 命名空间@d8d/shared-test-util(后端集成测试)分离@d8d/e2e-test-utils 专门用于 Playwright E2E 测试与项目标准对齐:
docs/standards/testing-standards.md 中的测试规范docs/standards/coding-standards.md 中的编码标准docs/standards/e2e-radix-testing.md 中的 Radix UI E2E 测试标准(核心标准文档)PRD 来源:
Architecture 来源:
标准文档来源:
Epic 来源:
参考包:
Claude (d8d-model) via dev-story workflow
创建的文件(初始实施):
packages/e2e-test-utils/package.jsonpackages/e2e-test-utils/tsconfig.jsonpackages/e2e-test-utils/vitest.config.tspackages/e2e-test-utils/README.mdpackages/e2e-test-utils/src/index.tspackages/e2e-test-utils/src/types.tspackages/e2e-test-utils/src/errors.tspackages/e2e-test-utils/src/constants.ts创建的目录(初始实施):
packages/e2e-test-utils/src/packages/e2e-test-utils/tests/fixtures/images/packages/e2e-test-utils/tests/fixtures/data/packages/e2e-test-utils/tests/unit/packages/e2e-test-utils/tests/integration/packages/e2e-test-utils/tests/stability/审查后续处理新增文件:
packages/e2e-test-utils/tests/unit/index.test.ts - 单元测试文件packages/e2e-test-utils/tests/fixtures/data/test-users.json - 测试用户数据packages/e2e-test-utils/tests/fixtures/data/README.md - 数据文件说明packages/e2e-test-utils/tests/fixtures/images/README.md - 图片文件说明packages/e2e-test-utils/tests/fixtures/images/sample-id-card.jpg - 身份证占位图片packages/e2e-test-utils/tests/fixtures/images/sample-disability-card.jpg - 残疾证占位图片packages/e2e-test-utils/tests/integration/README.md - 集成测试目录说明packages/e2e-test-utils/tests/stability/README.md - 稳定性测试目录说明packages/e2e-test-utils/PACKAGING.md - 包配置策略说明审查后续处理修改文件:
packages/e2e-test-utils/package.json - 修复 test:unit 脚本、添加 exports types 字段packages/e2e-test-utils/tsconfig.json - 添加 baseUrl 配置packages/e2e-test-utils/README.md - 完善为完整的 API 文档、修正导入示例packages/e2e-test-utils/tests/unit/index.test.ts - 增强测试用例(13个测试,100%覆盖率)packages/e2e-test-utils/PACKAGING.md - 同步 exports 配置示例代码审查修复(2026-01-08):
修改的项目文件:
_bmad-output/implementation-artifacts/sprint-status.yaml - 跟踪 story 状态_bmad-output/implementation-artifacts/1-1-create-package-structure.md - 本故事文件