# Novalon Website E2E测试统一方案设计 **创建时间**: 2026-02-28 **目标**: 统一E2E测试框架,从Python+Pytest迁移到TypeScript+Playwright,建立金融级网站完整的测试体系 --- ## 一、背景与目标 ### 当前状态 - **两套测试框架并存**: - TypeScript + Playwright (`e2e/` 目录) - Python + Pytest (`e2e-tests/` 目录) - **维护成本高**: 需要维护两套技术栈 - **测试覆盖不完整**: 部分测试场景缺失或重复 ### 核心目标 1. **统一技术栈**: 完全迁移到Playwright,与Next.js项目技术栈一致 2. **全面测试覆盖**: 建立业务流程、性能、安全、可访问性完整测试体系 3. **金融级质量**: 满足金融行业对安全、合规、可靠性的高要求 4. **CI/CD集成**: 建立分层测试流程,平衡速度和覆盖率 --- ## 二、技术选型决策 ### 选择: TypeScript + Playwright **决策理由**: - ✅ 与项目技术栈一致(Next.js + TypeScript) - ✅ 可共享类型定义,更好的类型安全 - ✅ Playwright功能强大,支持多浏览器、移动端、视觉测试 - ✅ 社区活跃,文档完善,生态成熟 - ✅ 原生支持并行执行,性能优异 **放弃: Python + Pytest** - ❌ 与项目技术栈不一致,增加维护成本 - ❌ 无法共享类型定义,降低开发效率 - ❌ 需要维护两套依赖和环境 --- ## 三、整体架构设计 ### 3.1 分层架构 ``` ┌─────────────────────────────────────────────────┐ │ 报告与监控层 (Reports & Monitoring) │ │ HTML报告 | JSON/JUnit报告 | 性能指标 | 趋势分析 │ └─────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────┐ │ 测试用例层 (Test Cases) │ │ Smoke | Regression | Performance | Security │ │ Accessibility | Visual | Mobile │ └─────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────┐ │ 页面对象层 (Page Objects) │ │ BasePage | HomePage | ContactPage | Products │ │ Components (Header, Footer, Form) │ └─────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────┐ │ 基础设施层 (Infrastructure) │ │ Playwright配置 | 测试数据 | 工具库 | Fixtures │ └─────────────────────────────────────────────────┘ ``` ### 3.2 目录结构 ``` e2e/ ├── src/ │ ├── fixtures/ # 测试夹具 │ │ ├── base.fixture.ts # 基础fixture │ │ ├── a11y.fixture.ts # 可访问性fixture │ │ └── auth.fixture.ts # 认证fixture(如需要) │ ├── pages/ # 页面对象 │ │ ├── BasePage.ts # 基础页面 │ │ ├── HomePage.ts # 首页 │ │ ├── ContactPage.ts # 联系页面 │ │ ├── ProductsPage.ts # 产品页面 │ │ ├── ServicesPage.ts # 服务页面 │ │ ├── AboutPage.ts # 关于页面 │ │ ├── CasesPage.ts # 案例页面 │ │ ├── SolutionsPage.ts # 解决方案页面 │ │ └── NewsPage.ts # 新闻页面 │ ├── tests/ # 测试用例 │ │ ├── smoke/ # 冒烟测试 │ │ ├── regression/ # 回归测试 │ │ ├── performance/ # 性能测试 │ │ ├── security/ # 安全测试 │ │ ├── accessibility/ # 可访问性测试 │ │ ├── visual/ # 视觉回归测试 │ │ ├── mobile/ # 移动端测试 │ │ ├── responsive/ # 响应式测试 │ │ └── error-handling/ # 错误处理测试 │ ├── types/ # 类型定义 │ │ └── index.ts # 共享类型 │ └── utils/ # 工具库 │ ├── PerformanceMonitor.ts # 性能监控 │ ├── TestDataGenerator.ts # 测试数据生成 │ ├── devices.ts # 设备配置 │ └── helpers.ts # 辅助函数 ├── test-data/ # 测试数据文件 │ ├── contact-form.json # 联系表单数据 │ ├── products.json # 产品数据 │ └── performance-budgets.json # 性能预算 ├── playwright.config.ts # Playwright配置 ├── package.json # 依赖管理 └── tsconfig.json # TypeScript配置 ``` --- ## 四、测试分层策略 ### 4.1 测试金字塔 ``` ┌─────────┐ │ Security│ (每周/发布前) │ Access │ 10-15分钟 └─────────┘ ┌───────────────┐ │ Performance │ (每日/发布前) │ 10-20分钟 │ └───────────────┘ ┌─────────────────────┐ │ Regression │ (PR合并前/每日) │ 15-30分钟 │ └─────────────────────┘ ┌───────────────────────────┐ │ Smoke │ (每次提交) │ < 5分钟 │ └───────────────────────────┘ ``` ### 4.2 各层测试详解 #### Level 1: Smoke Tests(冒烟测试) **执行频率**: 每次代码提交 **执行时间**: < 5分钟 **标签**: `@smoke` **覆盖范围**: - ✅ 所有关键页面可访问性(首页、产品、服务、联系) - ✅ 核心导航功能 - ✅ 关键表单提交(联系表单) - ✅ 页面基本渲染(无JS错误) **测试用例**: ```typescript test.describe('Smoke Tests @smoke', () => { test('首页可访问', async ({ page }) => { await homePage.goto(); await expect(page).toHaveTitle(/Novalon/); }); test('导航功能正常', async ({ page }) => { await homePage.goto(); await homePage.navigateTo('产品'); await expect(page).toHaveURL(/products/); }); test('联系表单可提交', async ({ page }) => { await contactPage.goto(); await contactPage.fillForm({ name: '测试用户', email: 'test@example.com', message: '测试消息' }); await contactPage.submit(); await expect(contactPage.successMessage).toBeVisible(); }); }); ``` #### Level 2: Regression Tests(回归测试) **执行频率**: PR合并前、每日构建 **执行时间**: 15-30分钟 **标签**: `@regression` **覆盖范围**: - ✅ 所有业务流程完整测试 - ✅ 表单验证(必填项、格式校验、错误提示) - ✅ 页面间跳转和数据传递 - ✅ 响应式布局(桌面/平板/移动端) - ✅ 多浏览器兼容性 **测试用例**: ```typescript test.describe('Contact Form Regression @regression', () => { test('表单验证 - 必填项', async ({ page }) => { await contactPage.goto(); await contactPage.submit(); await expect(contactPage.nameError).toHaveText('请输入姓名'); await expect(contactPage.emailError).toHaveText('请输入邮箱'); }); test('表单验证 - 邮箱格式', async ({ page }) => { await contactPage.goto(); await contactPage.fillEmail('invalid-email'); await contactPage.submit(); await expect(contactPage.emailError).toHaveText('邮箱格式不正确'); }); test('表单提交成功', async ({ page }) => { await contactPage.goto(); await contactPage.fillForm(testData.validContact); await contactPage.submit(); await expect(contactPage.successMessage).toBeVisible(); }); }); ``` #### Level 3: Performance Tests(性能测试) **执行频率**: 每日构建、发布前 **执行时间**: 10-20分钟 **标签**: `@performance` **覆盖范围**: - ✅ 页面加载时间(FCP、LCP、TTI) - ✅ 资源大小优化(图片、JS、CSS) - ✅ 网络请求数量和瀑布流 - ✅ 核心交互响应时间 - ✅ 性能预算验证 **性能预算**: ```json { "performanceBudgets": { "home": { "loadTime": 3000, "fcP": 1500, "lcp": 2500, "tti": 3500, "totalSize": 1500000 }, "contact": { "loadTime": 2500, "fcp": 1200, "lcp": 2000, "tti": 3000, "totalSize": 1200000 } } } ``` **测试用例**: ```typescript test.describe('Performance Tests @performance', () => { test('首页性能预算', async ({ page }) => { const metrics = await performanceMonitor.measurePageLoad(page, '/'); expect(metrics.loadTime).toBeLessThan(budgets.home.loadTime); expect(metrics.fcp).toBeLessThan(budgets.home.fcp); expect(metrics.lcp).toBeLessThan(budgets.home.lcp); }); test('图片优化验证', async ({ page }) => { await page.goto('/'); const images = await page.$$eval('img', imgs => imgs.map(img => ({ src: img.src, naturalWidth: img.naturalWidth, naturalHeight: img.naturalHeight, displayWidth: img.width, displayHeight: img.height })) ); for (const img of images) { expect(img.naturalWidth).toBeLessThanOrEqual(img.displayWidth * 2); } }); }); ``` #### Level 4: Security Tests(安全测试) **执行频率**: 每周、发布前 **执行时间**: 10-15分钟 **标签**: `@security` **覆盖范围**: - ✅ XSS漏洞检测(表单输入) - ✅ CSRF保护验证 - ✅ 安全头验证(CSP、HSTS等) - ✅ 敏感数据处理(联系表单数据加密) - ✅ HTTPS强制跳转 **测试用例**: ```typescript test.describe('Security Tests @security', () => { test('XSS防护 - 联系表单', async ({ page }) => { await contactPage.goto(); await contactPage.fillForm({ name: '', email: 'test@example.com', message: '' }); await contactPage.submit(); const pageContent = await page.content(); expect(pageContent).not.toContain('