基于项目需求文档(PRD.md, FeatureSummary.md, DevelopmentPlan.md, UIDesign.md, User_Role_Interfaces.md)编写的 TDD 测试用例。 后端测试 (Python/pytest): - 单元测试: rule_engine, brief_parser, timestamp_alignment, video_auditor, validators - 集成测试: API Brief, Video, Review 端点 - AI 模块测试: ASR, OCR, Logo 检测服务 - 全局 fixtures 和 pytest 配置 前端测试 (TypeScript/Vitest): - 工具函数测试: utils.test.ts - 组件测试: Button, VideoPlayer, ViolationList - Hooks 测试: useVideoAudit, useVideoPlayer, useAppeal - MSW mock handlers 配置 E2E 测试 (Playwright): - 认证流程测试 - 视频上传流程测试 - 视频审核流程测试 - 申诉流程测试 所有测试当前使用 pytest.skip() / it.skip() 作为占位符, 遵循 TDD 红灯阶段 - 等待实现代码后运行。 验收标准覆盖: - ASR WER ≤ 10% - OCR 准确率 ≥ 95% - Logo F1 ≥ 0.85 - 时间戳误差 ≤ 0.5s - 频次统计准确率 ≥ 95% Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
202 lines
7.3 KiB
TypeScript
202 lines
7.3 KiB
TypeScript
/**
|
||
* 视频上传流程 E2E 测试
|
||
*
|
||
* TDD 测试用例 - 测试达人上传视频的完整流程
|
||
*
|
||
* 用户流程参考:User_Role_Interfaces.md
|
||
*/
|
||
|
||
import { test, expect } from '@playwright/test'
|
||
import path from 'path'
|
||
|
||
test.describe('Video Upload Flow', () => {
|
||
test.beforeEach(async ({ page }) => {
|
||
// 以达人身份登录
|
||
// await page.goto('/login')
|
||
// await page.getByPlaceholder('邮箱').fill('creator@example.com')
|
||
// await page.getByPlaceholder('密码').fill('password123')
|
||
// await page.getByRole('button', { name: '登录' }).click()
|
||
// await page.waitForURL('/dashboard')
|
||
})
|
||
|
||
test.skip('should display upload page', async ({ page }) => {
|
||
// await page.goto('/videos/upload')
|
||
//
|
||
// await expect(page.getByRole('heading', { name: '上传视频' })).toBeVisible()
|
||
// await expect(page.getByTestId('upload-dropzone')).toBeVisible()
|
||
// await expect(page.getByText('支持 MP4、MOV 格式')).toBeVisible()
|
||
// await expect(page.getByText('最大 100MB')).toBeVisible()
|
||
})
|
||
|
||
test.skip('should select task before upload', async ({ page }) => {
|
||
// await page.goto('/videos/upload')
|
||
//
|
||
// // 验证需要先选择任务
|
||
// await expect(page.getByLabel('选择任务')).toBeVisible()
|
||
//
|
||
// // 选择任务
|
||
// await page.getByLabel('选择任务').click()
|
||
// await page.getByRole('option', { name: 'XX美妆产品推广' }).click()
|
||
//
|
||
// // 验证任务信息显示
|
||
// await expect(page.getByText('Brief 要求')).toBeVisible()
|
||
})
|
||
|
||
test.skip('should upload video file', async ({ page }) => {
|
||
// await page.goto('/videos/upload')
|
||
//
|
||
// // 选择任务
|
||
// await page.getByLabel('选择任务').click()
|
||
// await page.getByRole('option').first().click()
|
||
//
|
||
// // 上传文件
|
||
// const fileInput = page.getByTestId('file-input')
|
||
// await fileInput.setInputFiles(path.join(__dirname, '../fixtures/sample-video.mp4'))
|
||
//
|
||
// // 验证上传进度
|
||
// await expect(page.getByTestId('upload-progress')).toBeVisible()
|
||
// await expect(page.getByText(/上传中/)).toBeVisible()
|
||
//
|
||
// // 等待上传完成
|
||
// await expect(page.getByText('上传成功')).toBeVisible({ timeout: 60000 })
|
||
})
|
||
|
||
test.skip('should show validation error for unsupported format', async ({ page }) => {
|
||
// await page.goto('/videos/upload')
|
||
//
|
||
// // 选择任务
|
||
// await page.getByLabel('选择任务').click()
|
||
// await page.getByRole('option').first().click()
|
||
//
|
||
// // 上传不支持的格式
|
||
// const fileInput = page.getByTestId('file-input')
|
||
// await fileInput.setInputFiles(path.join(__dirname, '../fixtures/sample.avi'))
|
||
//
|
||
// // 验证错误提示
|
||
// await expect(page.getByText('不支持的文件格式')).toBeVisible()
|
||
// await expect(page.getByText('仅支持 MP4、MOV')).toBeVisible()
|
||
})
|
||
|
||
test.skip('should show error for oversized file', async ({ page }) => {
|
||
// await page.goto('/videos/upload')
|
||
//
|
||
// // 选择任务
|
||
// await page.getByLabel('选择任务').click()
|
||
// await page.getByRole('option').first().click()
|
||
//
|
||
// // 尝试上传超大文件(模拟)
|
||
// // 由于无法真正创建超大文件,这里验证前端校验逻辑
|
||
//
|
||
// // 假设通过 JavaScript 注入一个超大文件
|
||
// await page.evaluate(() => {
|
||
// const file = new File(['x'.repeat(101 * 1024 * 1024)], 'large.mp4', { type: 'video/mp4' })
|
||
// const event = new Event('change', { bubbles: true })
|
||
// const input = document.querySelector('[data-testid="file-input"]') as HTMLInputElement
|
||
// Object.defineProperty(input, 'files', { value: [file] })
|
||
// input.dispatchEvent(event)
|
||
// })
|
||
//
|
||
// await expect(page.getByText('文件大小超过限制')).toBeVisible()
|
||
// await expect(page.getByText('最大 100MB')).toBeVisible()
|
||
})
|
||
|
||
test.skip('should show processing status after upload', async ({ page }) => {
|
||
// await page.goto('/videos/upload')
|
||
//
|
||
// // 选择任务并上传
|
||
// await page.getByLabel('选择任务').click()
|
||
// await page.getByRole('option').first().click()
|
||
//
|
||
// const fileInput = page.getByTestId('file-input')
|
||
// await fileInput.setInputFiles(path.join(__dirname, '../fixtures/sample-video.mp4'))
|
||
//
|
||
// // 等待上传完成
|
||
// await expect(page.getByText('上传成功')).toBeVisible({ timeout: 60000 })
|
||
//
|
||
// // 验证显示处理状态
|
||
// await expect(page.getByText('AI 审核中')).toBeVisible()
|
||
// await expect(page.getByTestId('processing-progress')).toBeVisible()
|
||
})
|
||
|
||
test.skip('should navigate to video detail after processing', async ({ page }) => {
|
||
// // 假设视频已上传并处理完成
|
||
// await page.goto('/videos/video_new')
|
||
//
|
||
// // 验证显示审核结果
|
||
// await expect(page.getByTestId('audit-result')).toBeVisible()
|
||
// await expect(page.getByText('AI 检测完成')).toBeVisible()
|
||
})
|
||
})
|
||
|
||
test.describe('Drag and Drop Upload', () => {
|
||
test.skip('should highlight dropzone on drag over', async ({ page }) => {
|
||
// await page.goto('/videos/upload')
|
||
//
|
||
// // 模拟拖拽进入
|
||
// const dropzone = page.getByTestId('upload-dropzone')
|
||
//
|
||
// // 触发 dragenter 事件
|
||
// await dropzone.dispatchEvent('dragenter', {
|
||
// dataTransfer: { types: ['Files'] },
|
||
// })
|
||
//
|
||
// // 验证高亮状态
|
||
// await expect(dropzone).toHaveClass(/border-primary/)
|
||
})
|
||
|
||
test.skip('should remove highlight on drag leave', async ({ page }) => {
|
||
// await page.goto('/videos/upload')
|
||
//
|
||
// const dropzone = page.getByTestId('upload-dropzone')
|
||
//
|
||
// // 触发 dragenter
|
||
// await dropzone.dispatchEvent('dragenter', {
|
||
// dataTransfer: { types: ['Files'] },
|
||
// })
|
||
//
|
||
// // 触发 dragleave
|
||
// await dropzone.dispatchEvent('dragleave')
|
||
//
|
||
// // 验证高亮已移除
|
||
// await expect(dropzone).not.toHaveClass(/border-primary/)
|
||
})
|
||
})
|
||
|
||
test.describe('Resumable Upload', () => {
|
||
test.skip('should resume interrupted upload', async ({ page }) => {
|
||
// await page.goto('/videos/upload')
|
||
//
|
||
// // 选择任务
|
||
// await page.getByLabel('选择任务').click()
|
||
// await page.getByRole('option').first().click()
|
||
//
|
||
// // 开始上传
|
||
// const fileInput = page.getByTestId('file-input')
|
||
// await fileInput.setInputFiles(path.join(__dirname, '../fixtures/sample-video.mp4'))
|
||
//
|
||
// // 等待开始上传
|
||
// await expect(page.getByTestId('upload-progress')).toBeVisible()
|
||
//
|
||
// // 模拟中断(刷新页面)
|
||
// await page.reload()
|
||
//
|
||
// // 验证显示恢复上传选项
|
||
// await expect(page.getByText('检测到未完成的上传')).toBeVisible()
|
||
// await expect(page.getByRole('button', { name: '继续上传' })).toBeVisible()
|
||
//
|
||
// // 点击继续上传
|
||
// await page.getByRole('button', { name: '继续上传' }).click()
|
||
//
|
||
// // 验证从中断处继续
|
||
// await expect(page.getByTestId('upload-progress')).toBeVisible()
|
||
})
|
||
|
||
test.skip('should allow canceling pending upload', async ({ page }) => {
|
||
// await page.goto('/videos/upload')
|
||
//
|
||
// // 如果有未完成的上传
|
||
// // await page.getByRole('button', { name: '取消' }).click()
|
||
// // await expect(page.getByText('已取消上传')).toBeVisible()
|
||
})
|
||
})
|