# 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('