Done
As a 开发者, I want 为统一广告管理UI包补充缺失的测试场景, so that 可以达到更高的测试覆盖率,确保代码质量和稳定性。
data-testid 进行可靠选择[ ] 任务1: 创建 API 错误处理测试 (AC: 1, 7)
tests/integration/error-handling.integration.test.tsx[ ] 任务2: 创建表单验证测试 (AC: 2, 7)
[ ] 任务3: 创建分页功能测试 (AC: 3, 7)
[ ] 任务4: 创建编辑表单状态切换测试 (AC: 4, 7)
unified-advertisement-type-management.integration.test.tsx 添加测试[ ] 任务5: 创建广告类型选择器交互测试 (AC: 5, 7)
tests/components/UnifiedAdvertisementTypeSelector.test.tsx[ ] 任务6: 创建图片选择器交互测试 (AC: 6, 7)
[ ] 任务7: 更新测试覆盖率配置 (AC: 8)
vitest.config.ts 配置覆盖率收集[ ] 任务8: 代码质量检查 (AC: 8)
pnpm test:coverage 确认覆盖率达标pnpm typecheck 确保无类型错误pnpm test 确保所有测试通过已覆盖场景 (13 个测试):
未覆盖场景 (待补充):
新增测试文件:
packages/unified-advertisement-management-ui/tests/
├── integration/
│ ├── error-handling.integration.test.tsx # [新增] API错误处理测试
│ ├── pagination.integration.test.tsx # [新增] 分页功能测试
│ ├── form-validation.integration.test.tsx # [新增] 表单验证测试
│ ├── unified-advertisement-management.integration.test.tsx # [扩展] 添加验证测试
│ └── unified-advertisement-type-management.integration.test.tsx # [扩展] 添加编辑状态测试
├── components/
│ └── UnifiedAdvertisementTypeSelector.test.tsx # [新增] 选择器组件测试
└── setup.ts # [扩展] 添加错误mock
错误场景分类:
// 1. 网络错误(fetch 失败)
describe('网络错误处理', () => {
it('应该显示网络错误提示当 API 调用失败时', async () => {
mockGetClient.mockReturnValue({
index: {
$get: vi.fn().mockRejectedValue(new TypeError('Failed to fetch')),
$post: vi.fn()
},
':id': { $get: vi.fn(), $put: vi.fn(), $delete: vi.fn() }
} as any);
renderWithProviders(<UnifiedAdvertisementManagement />);
await waitFor(() => {
expect(toast.error).toHaveBeenCalledWith(
expect.stringContaining('网络错误')
);
});
});
});
// 2. 服务器错误(500, 503)
it('应该显示服务器错误提示', async () => {
mockGetClient.mockReturnValue({
index: {
$get: vi.fn().mockResolvedValue({
json: async () => ({ code: 500, message: 'Internal Server Error' }),
status: 500
}),
$post: vi.fn()
},
':id': { $get: vi.fn(), $put: vi.fn(), $delete: vi.fn() }
} as any);
// ... 验证错误消息
});
// 3. 业务错误(400, 404, 409)
it('应该显示业务错误提示(400 验证失败)', async () => {
mockGetClient.mockReturnValue({
index: {
$post: vi.fn().mockResolvedValue({
json: async () => ({ code: 400, message: '标题不能为空' }),
status: 400
})
},
// ...
} as any);
// ... 验证错误消息
});
it('应该显示冲突错误提示(409 重复)', async () => {
mockGetClient.mockReturnValue({
index: {
$post: vi.fn().mockResolvedValue({
json: async () => ({ code: 409, message: '广告别名已存在' }),
status: 409
})
},
// ...
} as any);
// ... 验证错误消息
});
必填字段验证:
describe('表单验证', () => {
it('应该显示验证错误当提交空标题时', async () => {
const user = userEvent.setup();
renderWithProviders(<UnifiedAdvertisementManagement />);
await user.click(screen.getByTestId('create-unified-advertisement-button'));
await waitFor(() => {
expect(screen.getByTestId('modal-title')).toBeInTheDocument();
});
// 直接提交不填写任何字段
await user.click(screen.getByTestId('create-submit-button'));
await waitFor(() => {
expect(screen.getByText(/标题.*/)).toBeInTheDocument();
});
});
it('应该显示验证错误当未选择广告类型时', async () => {
// ... 类似测试
});
it('应该限制标题长度最多30个字符', async () => {
// ... 填写31个字符,验证错误消息
});
});
describe('分页功能', () => {
it('应该正确切换到下一页', async () => {
const user = userEvent.setup();
// Mock 返回第二页数据
mockGetClient.mockReturnValue({
index: {
$get: vi.fn()
.mockResolvedValueOnce({ json: async () => ({ data: { list: [...], total: 25 } }), status: 200 }) // 第1页
.mockResolvedValueOnce({ json: async () => ({ data: { list: [...], total: 25 } }), status: 200 }) // 第2页
,
$post: vi.fn()
},
':id': { /* ... */ }
} as any);
renderWithProviders(<UnifiedAdvertisementManagement />);
// 点击下一页按钮
await user.click(screen.getByTestId('next-page-button'));
await waitFor(() => {
expect(mockGetClient().index.$get).toHaveBeenCalledWith({
query: expect.objectContaining({ page: 2 })
});
});
});
it('应该禁用上一页按钮在第一页时', async () => {
// ... 验证按钮禁用状态
});
});
describe('UnifiedAdvertisementTypeSelector', () => {
it('应该打开下拉菜单当点击触发器时', async () => {
const user = userEvent.setup();
const mockTypes = [{ id: 1, name: '首页轮播', code: 'home' }];
renderWithProviders(
<UnifiedAdvertisementTypeSelector
value={undefined}
onChange={vi.fn()}
options={mockTypes}
testId="type-selector"
/>
);
await user.click(screen.getByTestId('type-selector-trigger'));
await waitFor(() => {
expect(screen.getByTestId('type-selector-content')).toBeVisible();
});
});
it('应该调用 onChange 当选择类型时', async () => {
const user = userEvent.setup();
const handleChange = vi.fn();
const mockTypes = [{ id: 1, name: '首页轮播', code: 'home' }];
renderWithProviders(
<UnifiedAdvertisementTypeSelector
value={undefined}
onChange={handleChange}
options={mockTypes}
testId="type-selector"
/>
);
await user.click(screen.getByTestId('type-selector-trigger'));
await user.click(screen.getByTestId('type-selector-item-1'));
expect(handleChange).toHaveBeenCalledWith(1);
});
});
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./tests/setup.ts'],
include: ['tests/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
fileParallelism: false,
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'tests/',
'**/*.test.{ts,tsx}',
'**/*.spec.{ts,tsx}',
'src/types/index.ts', // 类型文件不计入覆盖率
],
thresholds: {
statements: 70,
branches: 65,
functions: 70,
lines: 70
}
}
}
});
问题: vitest 和 @vitest/coverage-v8 版本不匹配
解决方案:
{
"devDependencies": {
"vitest": "^4.0.10",
"@vitest/coverage-v8": "^4.0.10" // 版本号与 vitest 一致
}
}
tests/integration/error-handling.integration.test.tsxtests/integration/pagination.integration.test.tsxtests/integration/form-validation.integration.test.tsxtests/components/UnifiedAdvertisementTypeSelector.test.tsx| 测试类型 | 当前覆盖 | 目标覆盖 |
|---|---|---|
| 组件测试 | ~60% | 70%+ |
| 集成测试 | ~70% | 80%+ |
cd packages/unified-advertisement-management-ui
# 运行所有测试
pnpm test
# 生成覆盖率报告
pnpm test:coverage
# 运行特定测试文件
pnpm test error-handling.integration.test.tsx
| Date | Version | Description | Author |
|---|---|---|---|
| 2026-01-03 | 1.0 | 初始故事创建 | James (Claude Code) |
| 2026-01-03 | 1.1 | 故事实施完成 - 创建 51 个集成测试,覆盖率达到 87.33% statements | Claude Code (Happy) |
claude-opus-4-5-20251101 (via Code Agent SDK)
无特殊调试需求,所有测试通过标准 Vitest 测试运行器执行。
测试成果:
data-testid 进行可靠的元素选择技术要点:
toast.error,query 错误不会expect.objectContaining 匹配问题已知限制:
新增测试文件:
packages/unified-advertisement-management-ui/tests/integration/error-handling.integration.test.tsx - API 错误处理测试 (5 个测试)packages/unified-advertisement-management-ui/tests/integration/form-validation.integration.test.tsx - 表单验证测试 (8 个测试)packages/unified-advertisement-management-ui/tests/integration/pagination.integration.test.tsx - 分页功能测试 (6 个测试)packages/unified-advertisement-management-ui/tests/integration/edit-form-state.integration.test.tsx - 编辑表单状态测试 (7 个测试)packages/unified-advertisement-management-ui/tests/integration/ad-type-selector.integration.test.tsx - 广告类型选择器测试 (7 个测试)packages/unified-advertisement-management-ui/tests/integration/file-selector.integration.test.tsx - 图片选择器测试 (5 个测试)修改文件:
packages/unified-advertisement-management-ui/vitest.config.ts - 添加覆盖率配置和阈值packages/unified-advertisement-management-ui/package.json - 添加 @vitest/coverage-v8 依赖docs/stories/010.005.story.md - 更新故事状态和开发记录QA代理待填写