Status: ready-for-dev
作为测试开发者,
我想要 uploadFileToField() 函数支持一次上传多个文件,
以便测试前端 <input type="file" multiple> 的多文件选择功能。
Given Story 3.1 已实现 uploadFileToField() 函数(单文件上传)
Given Story 3.4 已完成,Select 工具问题已修复,测试可以顺利进行到文件上传步骤
When 扩展 uploadFileToField() 函数支持多文件上传
Then 验收标准如下:
函数接受文件名数组或字符串(向后兼容)
uploadFileToField(page, selector, 'file.jpg') 继续工作uploadFileToField(page, selector, ['file1.jpg', 'file2.jpg', 'file3.jpg']) 正常工作使用 Playwright 的 setInputFiles([path1, path2, ...]) API
setInputFiles(filePathArray)setInputFiles(filePath)(保持原有行为)支持相对路径数组(相对于 fixtures 目录)
错误时提供清晰消息(包含所有文件路径)
单元测试覆盖所有场景
E2E 测试验证多文件上传
file-upload-validation.spec.ts 中添加多文件上传场景[ ] Task 1: 分析当前 uploadFileToField 实现 (AC: #1, #2)
packages/e2e-test-utils/src/file-upload.ts[ ] Task 2: 扩展函数签名支持多文件 (AC: #1)
FileUploadOptions 接口)[ ] Task 3: 实现多文件上传逻辑 (AC: #2, #3)
setInputFiles(filePathArray) 处理多文件[ ] Task 4: 更新错误消息 (AC: #4)
[ ] Task 5: 编写单元测试 (AC: #5)
[ ] Task 6: 在 E2E 测试中验证 (AC: #6)
file-upload-validation.spec.ts 添加多文件上传场景Epic 3: 文件上传工具开发与验证
遵循 Epic 2 的成功模式,开发文件上传工具并在真实 E2E 测试中验证,解决当前测试超时阻塞问题。
模式: 工具开发 → 真实 E2E 测试验证 → 问题修复 → 稳定性验证
当前进度:
uploadFileToField() 函数已实现(单文件上传)* 标签的问题)Select 工具修复经验:
getByText(label, { exact: true }) 代替 text= 选择器更稳定*)需要额外的策略文件上传工具验证结果:
MinioUploader 组件:
// packages/file-management-ui/src/components/MinioUploader.tsx
<input
type="file"
multiple={multiple} // 已支持 multiple 属性
className="hidden"
/>
前端组件已经支持多文件选择,但 E2E 测试工具 uploadFileToField() 只支持单文件上传。
使用 TypeScript 函数重载支持两种调用方式:
// 单文件上传(向后兼容)
export async function uploadFileToField(
page: Page,
selector: string,
fileName: string,
options?: FileUploadOptions
): Promise<void>
// 多文件上传
export async function uploadFileToField(
page: Page,
selector: string,
fileNames: string[],
options?: FileUploadOptions
): Promise<void>
// 单文件上传
await page.locator(selector).setInputFiles(filePath)
// 多文件上传
await page.locator(selector).setInputFiles([path1, path2, path3])
保持与单文件上传相同的逻辑:
fixturesDir(默认为 tests/fixtures)// 文件不存在错误(多文件)
Error: 文件上传失败:部分文件不存在
选择器: [data-testid="photo-upload-0"]
缺失文件:
- images/not-exist-1.jpg
- images/not-exist-2.jpg
可用文件:
- images/sample-id-card.jpg
- images/sample-disability-card.jpg
// 空数组错误
Error: 文件上传失败:文件列表为空
选择器: [data-testid="photo-upload-0"]
提示: 至少需要一个文件路径
describe('uploadFileToField - 多文件上传', () => {
test('应该成功上传多个文件', async () => {
await uploadFileToField(page, selector, [
'images/sample-id-card.jpg',
'images/sample-disability-card.jpg',
'images/sample-photo.jpg'
])
// 验证 setInputFiles 被调用一次,参数为文件路径数组
})
test('应该保持向后兼容(单文件)', async () => {
await uploadFileToField(page, selector, 'images/sample-id-card.jpg')
// 验证单文件上传仍然工作
})
test('应该在文件不存在时抛出错误', async () => {
await expect(
uploadFileToField(page, selector, ['not-exist.jpg'])
).rejects.toThrow('文件不存在')
})
test('应该在空数组时抛出错误', async () => {
await expect(
uploadFileToField(page, selector, [])
).rejects.toThrow('文件列表为空')
})
})
test('场景 2:应该成功上传多张照片(一次性)', async () => {
await page.goto('/admin/disability-person/create')
// 填写基本信息(使用已修复的 Select 工具)
await selectRadixOption(page, '性别', '男')
// 一次性上传 3 张照片
await uploadFileToField(page, '[data-testid="photo-upload-0"]', [
'images/sample-id-card.jpg',
'images/sample-disability-card.jpg',
'images/sample-photo.jpg'
])
// 验证上传成功
await expect(page.locator('.photo-preview')).toHaveCount(3)
})
相关文件:
packages/e2e-test-utils/
├── src/
│ └── file-upload.ts # 需要修改:添加多文件支持
├── tests/
│ └── unit/
│ └── file-upload.test.ts # 需要修改:添加多文件测试
web/tests/e2e/
├── specs/admin/
│ └── file-upload-validation.spec.ts # 需要修改:添加多文件场景
├── fixtures/images/
│ ├── sample-id-card.jpg
│ ├── sample-disability-card-front.jpg
│ └── sample-disability-card-back.jpg
└── pages/admin/
└── disability-person.page.ts # 可能需要修改:使用多文件上传
架构文档:
_bmad-output/planning-artifacts/architecture.md - 类型系统、错误处理策略E2E 测试标准:
docs/standards/e2e-radix-testing.md - 文件上传测试标准Epic 3 完整需求:
_bmad-output/planning-artifacts/epics.md - Epic 3 和 Story 3.5 详细需求前置 Story 文件:
_bmad-output/implementation-artifacts/3-1-file-upload-tool.md - 单文件上传工具实现_bmad-output/implementation-artifacts/3-2-upload-unit-tests.md - 单元测试_bmad-output/implementation-artifacts/3-3-upload-e2e-integration.md - E2E 集成测试_bmad-output/implementation-artifacts/3-4-collect-feedback-fix.md - 收集反馈并修复问题Epic 2 经验(关键):
_bmad-output/implementation-artifacts/epic-2-retrospective.md - DOM 结构假设必须验证// Playwright Locator API
class Locator {
// 设置文件输入框的值
setInputFiles(files: Path | string | Buffer | Array<Path | string | Buffer | FilePayload>): Promise<void>
}
// 使用示例
await page.locator('input[type="file"]').setInputFiles('path/to/file.jpg')
await page.locator('input[type="file"]').setInputFiles([
'path/to/file1.jpg',
'path/to/file2.jpg',
'path/to/file3.jpg'
])
Monorepo 结构对齐:
packages/e2e-test-utils/ 目录web/tests/e2e/ 目录@d8d/e2e-test-utils workspace 包文件组织:
源文档引用:
Playwright 文档:
Claude Opus 4 (claude-opus-4-5-20251101)
本 Story 创建于 2026-01-10
待创建/修改的文件:
packages/e2e-test-utils/src/file-upload.ts - 添加多文件上传支持packages/e2e-test-utils/tests/unit/file-upload.test.ts - 添加多文件测试web/tests/e2e/specs/admin/file-upload-validation.spec.ts - 添加多文件 E2E 场景Story 创建日期: 2026-01-10 Story 状态: ready-for-dev