Status: done
作为测试开发者,
我想要使用 uploadFileToField() 函数上传文件,
以便测试照片上传、文档上传等功能。
Given Epic 1 的类型定义已存在(BaseOptions, E2ETestError, ErrorContext, DEFAULT_TIMEOUTS)
When 实现 src/file-upload.ts 中的 uploadFileToField(page, selector, fileName) 函数
Then 验收标准如下:
函数从 fixtures 目录加载测试文件
web/tests/fixtures/FileUploadOptions.fixturesDir 自定义 fixtures 目录使用 Playwright 的 setInputFiles() API
page.locator(selector).setInputFiles() 方法data-testid, aria-label, CSS 选择器等多种选择器支持相对路径(相对于 fixtures 目录)
'sample-id-card.jpg' 自动在 fixtures 目录中查找'images/sample-id-card.jpg'错误时提供清晰消息
E2ETestError 和 ErrorContext操作在 5 秒内完成(NFR9)
FileUploadOptions.timeout 自定义配置对象继承 BaseOptions
FileUploadOptions extends BaseOptionstimeout 配置[x] Task 1: 更新 types.ts 中的 FileUploadOptions 定义 (AC: #6)
FileUploadOptions 接口,添加 fixturesDir 和 waitForUpload 选项@beta 标记,因为此功能即将实现[x] Task 2: 实现 src/file-upload.ts 核心函数 (AC: #1, #2, #3, #4, #5)
uploadFileToField() 主函数console.debug() 日志[x] Task 3: 更新 src/index.ts 导出 (AC: #2)
uploadFileToField 函数FileUploadOptions 类型[x] Task 4: 添加 JSDoc 注释 (NFR25-NFR40)
[x] Task 5: 安装依赖并验证类型 (AC: #1)
@types/node 到 devDependencies[x] Task 6: 修改 MinioUploader 组件使其可测试 (AC: #2)
testId prop 支持唯一标识符Epic 3: 文件上传工具开发与验证
遵循 Epic 2 的成功模式,开发文件上传工具并在真实 E2E 测试中验证,解决当前测试超时阻塞问题。
模式: 工具开发 → 真实 E2E 测试验证 → 问题修复 → 稳定性验证
Epic 2 关键经验(必须应用):
当前测试超时问题:
uploadPhoto() 方法使用复杂的 DOM 操作export async function uploadFileToField(
page: Page,
selector: string,
fileName: string,
options?: FileUploadOptions
): Promise<void>
export interface FileUploadOptions extends BaseOptions {
/** fixtures 目录路径,默认为 'tests/fixtures' */
fixturesDir?: string;
/** 是否等待上传完成,默认为 true */
waitForUpload?: boolean;
}
Fixtures 路径解析
tests/fixtures/(相对于测试运行目录)options.fixturesDir 自定义路径path.join() 和 path.resolve() 解析路径文件存在性检查
fs.existsSync() 检查文件是否存在E2ETestError 包含完整路径和建议文件上传
page.locator(selector).setInputFiles(filePath)data-testid > aria-label > CSS selector错误处理
options.timeout 或默认 5000ms日志输出
console.debug() 输出调试信息(仅 Vitest 中可见)包结构:
packages/e2e-test-utils/
├── src/
│ ├── index.ts # 主导出,需要更新
│ ├── types.ts # 共享类型定义,需要更新 FileUploadOptions
│ ├── errors.ts # 错误类和错误处理
│ ├── constants.ts # 常量定义(超时)
│ ├── radix-select.ts # Radix UI Select 工具(参考实现)
│ └── file-upload.ts # 文件上传工具(需要实现)
├── tests/
│ ├── fixtures/
│ │ └── images/
│ │ ├── sample-id-card.jpg # 测试图片占位文件
│ │ └── sample-disability-card.jpg
│ └── unit/
│ └── file-upload.test.ts # 单元测试(下个 story)
web/tests/fixtures 结构(需要创建):
web/tests/fixtures/
├── images/
│ ├── sample-id-card.jpg
│ └── sample-disability-card.jpg
参考 radix-select.ts 的实现模式:
import type { Page } from "@playwright/test";
import type { FileUploadOptions } from "./types";
import { throwError } from "./errors";
import { DEFAULT_TIMEOUTS } from "./constants";
/**
* 上传文件到指定输入框
*
* @description
* 从 fixtures 目录加载测试文件并上传到指定的文件输入框。
* ...
*/
export async function uploadFileToField(
page: Page,
selector: string,
fileName: string,
options?: FileUploadOptions
): Promise<void> {
console.debug(`[uploadFileToField] 开始上传: selector="${selector}", fileName="${fileName}"`);
// 1. 合并默认配置
const config = {
timeout: options?.timeout ?? DEFAULT_TIMEOUTS.static,
fixturesDir: options?.fixturesDir ?? 'tests/fixtures',
waitForUpload: options?.waitForUpload ?? true
};
// 2. 解析文件路径
const filePath = resolveFixturePath(fileName, config.fixturesDir);
// 3. 检查文件是否存在
if (!fs.existsSync(filePath)) {
throwError({
operation: 'uploadFileToField',
target: `文件 "${fileName}"`,
expected: `文件存在于 ${filePath}`,
suggestion: '检查文件名是否正确,或确认文件已添加到 fixtures 目录'
});
}
// 4. 查找文件输入框并上传
// ... 实现
console.debug(`[uploadFileToField] 上传完成`);
}
将在 web/tests/e2e/specs/admin/disability-person-complete.spec.ts 中验证:
架构文档:
_bmad-output/planning-artifacts/architecture.md - 包结构、API 设计模式、错误处理策略E2E 测试标准:
docs/standards/e2e-radix-testing.md - 文件上传测试标准(第 155-198 行)Epic 3 完整需求:
_bmad-output/planning-artifacts/epics.md - Epic 3 和 Story 3.1 详细需求(第 710-777 行)Epic 2 回顾(关键经验):
_bmad-output/implementation-artifacts/epic-2-retrospective.md - DOM 结构假设必须验证TypeScript + Playwright 陷阱:
architecture.md 第 533-657 行 - DOM 结构假设必须验证、选择器策略优先级问题:MinioUploader 原本使用动态创建的文件输入框,无法用于 E2E 测试。
解决方案:修改组件架构,添加隐藏的静态文件输入框:
MinioUploader.tsx 修改:
testId prop 用于唯一标识useRef 保存文件输入框引用<input type="file"> 元素FileSelector.tsx 修改:
testId proptestId 给 MinioUploaderPhotoUploadField.tsx 修改:
testId={photo-upload-${index}}测试使用方式:
// 上传第一张照片
await uploadFileToField(page, '[data-testid="photo-upload-0"]', 'id-card.jpg');
// 上传第二张照片
await uploadFileToField(page, '[data-testid="photo-upload-1"]', 'disability-card.jpg');
适用场景:
<input type="file"> 元素Monorepo 结构对齐:
packages/e2e-test-utils/@d8d/shared-test-util(后端集成测试)分离文件组织:
file-upload.ts 用于文件上传types.ts, errors.ts, constants.tsindex.ts,支持 tree-shaking无冲突检测:
file-upload.ts)FileUploadOptions)源文档引用:
现有代码参考:
Claude Opus 4 (claude-opus-4-5-20251101)
FileUploadOptions 接口,添加 fixturesDir 和 waitForUpload 选项uploadFileToField() 核心函数,包含完整错误处理src/index.ts 导出 uploadFileToField 和 FileUploadOptions@types/node 依赖,TypeScript 类型检查通过testId prop 和隐藏的静态文件输入框testId 给 MinioUploadertestId={photo-upload-${index}}e2e-test-utils 包修改:
packages/e2e-test-utils/src/types.ts - 更新 FileUploadOptions 接口packages/e2e-test-utils/src/index.ts - 添加 uploadFileToField 导出packages/e2e-test-utils/src/file-upload.ts - 新建文件上传工具函数packages/e2e-test-utils/package.json - 添加 @types/node 依赖file-management-ui 包修改:
packages/file-management-ui/src/components/MinioUploader.tsx - 添加 testId prop、隐藏文件输入框packages/file-management-ui/src/components/FileSelector.tsx - 添加 testId prop 并传递disability-person-management-ui 包修改:
allin-packages/disability-person-management-ui/src/components/PhotoUploadField.tsx - 生成唯一 testId2026-01-10 - 完成 Story 3.1 实现(包含架构修改)
uploadFileToField() 通用文件上传工具函数@types/node 依赖支持 Node.js API 类型testId prop 机制,支持页面中多个上传组件的测试定位2026-01-10 - 代码审查修复
web/tests/fixtures(符合 AC #1)Reviewer: Claude (code-review workflow) Review Date: 2026-01-10 Original Status: review Final Status: done
| Severity | Issue | Status | Fix Details |
|---|---|---|---|
| HIGH | 默认 fixtures 路径错误 | ✅ Fixed | 修改为 web/tests/fixtures |
| HIGH | 测试 fixtures 目录为空 | ✅ Fixed | 创建 sample-id-card.jpg 和 sample-disability-card.jpg |
| HIGH | 错误场景区分不清晰 | ✅ Fixed | 改进选择器错误处理,提供详细调试建议 |
| MEDIUM | 路径遍历漏洞风险 | ✅ Fixed | 添加双重验证确保解析路径在 fixtures 目录内 |
| LOW | 缺少单元测试 | ⏳ Deferred | 计划在 Story 3.2 实现 |
packages/e2e-test-utils/src/file-upload.ts - 修复默认路径、加强验证、改进错误处理packages/e2e-test-utils/src/types.ts - 更新 JSDoc 注释web/tests/fixtures/images/ - 创建测试图片占位文件