完整的前后端图片分析应用,包含: - 后端:Express + Prisma + SQLite,101个单元测试全部通过 - 前端:React + TypeScript + Vite,47个单元测试,89.73%覆盖率 - E2E测试:Playwright 测试套件 - MCP集成:Playwright MCP配置完成并测试通过 功能模块: - 用户认证(JWT) - 文档管理(CRUD) - 待办管理(三态工作流) - 图片管理(上传、截图、OCR) 测试覆盖: - 后端单元测试:101/101 ✅ - 前端单元测试:47/47 ✅ - E2E测试:通过 ✅ - MCP Playwright测试:通过 ✅ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
245 lines
8.4 KiB
TypeScript
245 lines
8.4 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('前端应用完整流程测试', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
// Mock API 响应
|
|
await page.route('**/api/auth/login', route => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: true,
|
|
data: {
|
|
token: 'test-token-abc123',
|
|
user: {
|
|
id: '1',
|
|
username: 'testuser',
|
|
email: 'test@example.com',
|
|
created_at: '2024-01-01',
|
|
updated_at: '2024-01-01'
|
|
}
|
|
}
|
|
})
|
|
});
|
|
});
|
|
|
|
await page.route('**/api/documents**', route => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: true,
|
|
data: [
|
|
{
|
|
id: '1',
|
|
title: '示例文档',
|
|
content: '这是一个示例文档内容,用于演示文档管理功能。',
|
|
created_at: '2024-01-01T00:00:00Z',
|
|
updated_at: '2024-01-01T00:00:00Z',
|
|
user_id: '1',
|
|
category_id: null
|
|
}
|
|
],
|
|
count: 1
|
|
})
|
|
});
|
|
});
|
|
|
|
await page.route('**/api/todos**', route => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: true,
|
|
data: [
|
|
{
|
|
id: '1',
|
|
title: '完成项目文档',
|
|
description: '编写完整的项目文档',
|
|
priority: 'high',
|
|
status: 'pending',
|
|
created_at: '2024-01-01T00:00:00Z',
|
|
updated_at: '2024-01-01T00:00:00Z',
|
|
user_id: '1',
|
|
document_id: null,
|
|
category_id: null,
|
|
due_date: null,
|
|
completed_at: null,
|
|
confirmed_at: null
|
|
}
|
|
]
|
|
})
|
|
});
|
|
});
|
|
|
|
await page.route('**/api/todos/pending', route => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: true,
|
|
data: [
|
|
{
|
|
id: '1',
|
|
title: '完成项目文档',
|
|
description: '编写完整的项目文档',
|
|
priority: 'high',
|
|
status: 'pending',
|
|
created_at: '2024-01-01T00:00:00Z',
|
|
updated_at: '2024-01-01T00:00:00Z',
|
|
user_id: '1',
|
|
document_id: null,
|
|
category_id: null,
|
|
due_date: null,
|
|
completed_at: null,
|
|
confirmed_at: null
|
|
}
|
|
]
|
|
})
|
|
});
|
|
});
|
|
|
|
await page.route('**/api/todos/completed', route => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: true,
|
|
data: []
|
|
})
|
|
});
|
|
});
|
|
|
|
await page.route('**/api/images**', route => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: true,
|
|
data: [
|
|
{
|
|
id: '1',
|
|
file_path: 'https://via.placeholder.com/300',
|
|
file_size: 102400,
|
|
mime_type: 'image/jpeg',
|
|
ocr_result: '这是 OCR 识别的文本结果',
|
|
ocr_confidence: 0.95,
|
|
processing_status: 'completed',
|
|
quality_score: 0.9,
|
|
error_message: null,
|
|
created_at: '2024-01-01T00:00:00Z',
|
|
updated_at: '2024-01-01T00:00:00Z',
|
|
user_id: '1',
|
|
document_id: null
|
|
}
|
|
],
|
|
count: 1
|
|
})
|
|
});
|
|
});
|
|
|
|
await page.route('**/api/images/pending', route => {
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
success: true,
|
|
data: []
|
|
})
|
|
});
|
|
});
|
|
});
|
|
|
|
test('完整用户流程:登录 -> 浏览所有页面', async ({ page }) => {
|
|
console.log('\n═════════════════════════════════════════════════════');
|
|
console.log('🚀 开始完整用户流程测试');
|
|
console.log('═════════════════════════════════════════════════════\n');
|
|
|
|
// 1. 访问登录页面
|
|
console.log('📄 步骤 1: 访问登录页面');
|
|
await page.goto('http://localhost:3000', { waitUntil: 'networkidle' });
|
|
await page.screenshot({ path: 'screenshots/01-login.png' });
|
|
console.log('✅ 登录页面截图完成\n');
|
|
|
|
// 2. 填写登录表单
|
|
console.log('🔐 步骤 2: 填写登录表单');
|
|
await page.fill('input[label="用户名"]', 'testuser');
|
|
await page.fill('input[label="密码"]', 'Password123@');
|
|
await page.screenshot({ path: 'screenshots/02-login-filled.png' });
|
|
console.log('✅ 表单填写完成\n');
|
|
|
|
// 3. 点击登录按钮
|
|
console.log('🔑 步骤 3: 点击登录按钮');
|
|
await page.click('button[type="submit"]');
|
|
|
|
// 等待跳转到仪表盘
|
|
await page.waitForURL('**/dashboard', { timeout: 10000 });
|
|
await page.waitForLoadState('networkidle');
|
|
await page.screenshot({ path: 'screenshots/03-dashboard.png', fullPage: true });
|
|
console.log('✅ 登录成功,仪表盘截图完成\n');
|
|
|
|
// 4. 访问文档页面
|
|
console.log('📄 步骤 4: 访问文档页面');
|
|
await page.click('text=文档');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.screenshot({ path: 'screenshots/04-documents.png', fullPage: true });
|
|
console.log('✅ 文档页面截图完成\n');
|
|
|
|
// 5. 访问待办页面
|
|
console.log('✅ 步骤 5: 访问待办页面');
|
|
await page.click('text=待办');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.screenshot({ path: 'screenshots/05-todos.png', fullPage: true });
|
|
console.log('✅ 待办页面截图完成\n');
|
|
|
|
// 6. 访问图片页面
|
|
console.log('🖼️ 步骤 6: 访问图片页面');
|
|
await page.click('text=图片');
|
|
await page.waitForLoadState('networkidle');
|
|
await page.screenshot({ path: 'screenshots/06-images.png', fullPage: true });
|
|
console.log('✅ 图片页面截图完成\n');
|
|
|
|
console.log('═════════════════════════════════════════════════════');
|
|
console.log('🎉 所有测试完成!');
|
|
console.log('═════════════════════════════════════════════════════');
|
|
console.log('\n📁 截图已保存到 screenshots/ 目录:');
|
|
console.log(' 1. 01-login.png - 登录页面');
|
|
console.log(' 2. 02-login-filled.png - 填写表单');
|
|
console.log(' 3. 03-dashboard.png - 仪表盘');
|
|
console.log(' 4. 04-documents.png - 文档管理');
|
|
console.log(' 5. 05-todos.png - 待办事项');
|
|
console.log(' 6. 06-images.png - 图片管理');
|
|
console.log('');
|
|
});
|
|
|
|
test('验证页面元素', async ({ page }) => {
|
|
// 先登录
|
|
await page.goto('http://localhost:3000');
|
|
await page.fill('input[label="用户名"]', 'testuser');
|
|
await page.fill('input[label="密码"]', 'Password123@');
|
|
await page.click('button[type="submit"]');
|
|
await page.waitForURL('**/dashboard');
|
|
|
|
// 验证仪表盘元素
|
|
await expect(page.locator('h2')).toContainText('仪表盘');
|
|
await expect(page.locator('text=文档总数')).toBeVisible();
|
|
await expect(page.locator('text=待办任务')).toBeVisible();
|
|
await expect(page.locator('text=已完成')).toBeVisible();
|
|
|
|
// 访问文档页面
|
|
await page.click('text=文档');
|
|
await expect(page.locator('h1')).toContainText('文档管理');
|
|
await expect(page.locator('text=新建文档')).toBeVisible();
|
|
|
|
// 访问待办页面
|
|
await page.click('text=待办');
|
|
await expect(page.locator('h1')).toContainText('待办事项');
|
|
await expect(page.locator('text=新建待办')).toBeVisible();
|
|
|
|
// 访问图片页面
|
|
await page.click('text=图片');
|
|
await expect(page.locator('h1')).toContainText('图片管理');
|
|
await expect(page.locator('text=上传图片')).toBeVisible();
|
|
});
|
|
});
|