From f7904cb92495cded1365df50e19e5deba684334d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Thu, 5 Mar 2026 14:13:09 +0800 Subject: [PATCH] docs: add mobile testing enhancement design document --- ...03-05-mobile-testing-enhancement-design.md | 1383 +++++++++++++++++ 1 file changed, 1383 insertions(+) create mode 100644 docs/plans/2026-03-05-mobile-testing-enhancement-design.md diff --git a/docs/plans/2026-03-05-mobile-testing-enhancement-design.md b/docs/plans/2026-03-05-mobile-testing-enhancement-design.md new file mode 100644 index 0000000..8d76b55 --- /dev/null +++ b/docs/plans/2026-03-05-mobile-testing-enhancement-design.md @@ -0,0 +1,1383 @@ +# 移动端测试完善方案设计文档 + +**创建日期**: 2026-03-05 +**项目**: Novalon Website E2E Testing Enhancement +**目标**: 建立全面完善的移动端测试体系,覆盖性能、兼容性、手势交互、PWA 功能等方面 + +--- + +## 1. 项目背景 + +### 1.1 当前现状 + +项目已建立基于 Playwright 的 E2E 测试框架,具备基础的移动端测试能力: + +- ✅ 基本的移动端 UI 交互测试(菜单、导航、表单) +- ✅ 多设备尺寸配置(iPhone、Android、Tablet) +- ✅ 基础的触摸交互测试 +- ✅ 响应式布局测试 + +### 1.2 存在的问题 + +当前移动端测试体系存在以下不足: + +- 缺乏系统的性能测试,无法验证移动端网络条件下的加载性能 +- 兼容性测试覆盖不全面,缺乏跨设备、跨浏览器的深度验证 +- 手势交互测试仅覆盖基本操作,缺乏复杂手势测试 +- 缺少 PWA 功能测试,无法验证离线缓存、安装等功能 +- 测试报告缺乏移动端特定的分析和可视化 + +### 1.3 项目目标 + +建立企业级移动端测试体系,实现: + +- 🎯 全面的性能测试覆盖,包括 Core Web Vitals 和网络性能 +- 🎯 完整的兼容性测试矩阵,支持多设备、多浏览器验证 +- 🎯 丰富的手势交互测试,覆盖所有常见移动端手势 +- 🎯 完整的 PWA 功能测试,验证移动端特性 +- 🎯 智能的测试报告系统,提供移动端专属的分析和可视化 + +--- + +## 2. 整体架构设计 + +采用分层架构设计,确保系统的可扩展性和可维护性: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 测试执行层 │ +│ - 测试配置扩展 - 并行测试策略 - 测试报告系统 │ +└─────────────────────────────────────────────────────────────┘ + ↑ +┌─────────────────────────────────────────────────────────────┐ +│ 测试用例层 │ +│ - 性能测试套件 - 兼容性测试套件 - 手势交互测试套件 │ +│ - PWA 功能测试套件 │ +└─────────────────────────────────────────────────────────────┘ + ↑ +┌─────────────────────────────────────────────────────────────┐ +│ 测试工具层 │ +│ - 手势模拟器 - 网络环境模拟器 - 性能监控器 │ +│ - 设备兼容性验证工具 │ +└─────────────────────────────────────────────────────────────┘ + ↑ +┌─────────────────────────────────────────────────────────────┐ +│ 测试数据层 │ +│ - 移动端测试数据生成器 - 设备配置数据 - 网络配置数据 │ +│ - 性能基准数据 - 测试数据仓库 │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 2.1 测试数据层 + +**职责**: 提供移动端测试所需的所有数据支持 + +**核心组件**: + +1. **移动端测试数据生成器** + - 扩展现有的 `TestDataGenerator` + - 增加设备特定的测试数据(User Agent、触摸事件数据) + - 支持不同网络条件下的测试数据 + +2. **设备配置数据** + - 扩展 `devices.ts`,增加更多真实设备配置 + - 支持 iPhone 13/14/15 系列 + - 支持 Samsung Galaxy、Google Pixel 系列 + - 支持 iPad Air/Pro 系列 + +3. **网络配置数据** + - 预设网络配置(2G、3G、4G、WiFi) + - 自定义网络配置(延迟、丢包率、带宽) + - 网络切换场景配置 + +4. **性能基准数据** + - Core Web Vitals 基准值 + - 不同设备类型的性能阈值 + - 性能回归检测基准 + +5. **测试数据仓库** + - 集中管理所有测试数据 + - 支持数据版本控制 + - 提供数据查询和复用机制 + +### 2.2 测试工具层 + +**职责**: 提供移动端测试所需的专用工具和功能 + +**核心组件**: + +1. **手势模拟器 (GestureSimulator)** + - 基于 Playwright Touch API 构建 + - 支持的手势类型: + - 单指滑动(水平、垂直,支持速度和距离控制) + - 双指捏合缩放(支持缩放比例控制) + - 长按操作(支持自定义时长) + - 双击操作(支持自定义间隔时间) + - 拖拽操作(支持方向和距离控制) + - 提供统一的 API 接口 + - 支持链式调用和参数化配置 + +2. **网络环境模拟器 (NetworkSimulator)** + - 利用 Chrome DevTools Protocol 实现 + - 支持的网络场景: + - 弱网环境(2G、3G、4G 不同信号强度) + - 离线模式(完全断网) + - 网络切换(在线→离线、弱网→强网) + - 网络延迟和丢包模拟 + - 提供预设配置和自定义配置 + - 支持动态切换网络环境 + +3. **性能监控器 (PerformanceMonitor)** + - 集成 Lighthouse API + - 监控的 Core Web Vitals 指标: + - First Contentful Paint (FCP) + - Largest Contentful Paint (LCP) + - Cumulative Layout Shift (CLS) + - First Input Delay (FID) + - Time to Interactive (TTI) + - 建立移动端性能基准 + - 支持性能回归检测 + - 生成详细性能分析报告 + +4. **设备兼容性验证工具 (CompatibilityValidator)** + - 跨设备布局验证 + - 功能兼容性检查 + - 渲染一致性对比 + - 系统版本降级测试 + +### 2.3 测试用例层 + +**职责**: 组织和管理所有移动端测试用例 + +**核心组件**: + +1. **性能测试套件 (PerformanceTestSuite)** + - 首屏加载性能测试 + - 页面交互响应测试 + - 资源加载优化测试 + - 内存泄漏检测测试 + - 每个测试用例设置明确的性能阈值 + +2. **兼容性测试套件 (CompatibilityTestSuite)** + - 不同屏幕尺寸的布局适配测试 + - 不同操作系统的功能兼容性测试 + - 不同浏览器的渲染一致性测试 + - 不同系统版本的功能降级测试 + - 采用参数化测试方式 + +3. **手势交互测试套件 (GestureTestSuite)** + - 单指滑动测试(页面滚动、轮播图切换) + - 双指捏合缩放测试(图片查看、地图缩放) + - 长按操作测试(上下文菜单、多选操作) + - 双击操作测试(图片放大、点赞功能) + - 拖拽操作测试(卡片排序、元素移动) + +4. **PWA 功能测试套件 (PWATestSuite)** + - Service Worker 注册测试 + - 离线缓存功能测试 + - 应用安装测试 + - 推送通知测试 + - 应用更新测试 + +### 2.4 测试执行层 + +**职责**: 管理测试的执行、调度和报告 + +**核心组件**: + +1. **测试配置扩展** + - 扩展现有 Playwright 配置 + - 增加移动设备项目配置 + - 增加网络环境项目配置 + - 增加性能测试专用项目配置 + - 支持动态设备选择和网络条件切换 + +2. **并行测试策略** + - 设备级并行(不同设备上的测试用例同时执行) + - 网络条件级并行(同一设备在不同网络条件下并行测试) + - 智能测试调度(根据依赖关系和资源使用优化执行顺序) + - 显著缩短测试执行时间 + +3. **测试报告系统** + - 移动端测试概览(通过率、失败率、执行时间) + - 设备兼容性报告(每个设备上的测试结果对比) + - 性能分析报告(Core Web Vitals 趋势、性能回归检测) + - 网络环境影响报告(不同网络条件下的性能对比) + - 支持多种输出格式(HTML、JSON、JUnit) + +--- + +## 3. 详细设计 + +### 3.1 测试数据层详细设计 + +#### 3.1.1 移动端测试数据生成器 + +**文件位置**: `e2e/src/utils/MobileTestDataGenerator.ts` + +**核心功能**: + +```typescript +export class MobileTestDataGenerator { + // 生成设备特定的 User Agent + static generateUserAgent(device: string): string; + + // 生成触摸事件数据 + static generateTouchEvent(type: 'tap' | 'swipe' | 'pinch' | 'longPress'): TouchEventData; + + // 生成网络配置数据 + static generateNetworkConfig(type: '2G' | '3G' | '4G' | 'WiFi' | 'offline'): NetworkConfig; + + // 生成性能基准数据 + static generatePerformanceBaseline(device: string): PerformanceBaseline; + + // 生成手势测试数据 + static generateGestureData(gesture: GestureType): GestureData; +} +``` + +#### 3.1.2 扩展设备配置 + +**文件位置**: `e2e/src/utils/devices.ts` + +**新增设备配置**: + +```typescript +export const devices: Record = { + // 现有设备配置... + + // 新增 iPhone 系列 + 'iphone-13-pro': { + name: 'iPhone 13 Pro', + viewport: { width: 390, height: 844 }, + userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)...', + isMobile: true, + deviceScaleFactor: 3, + }, + 'iphone-14-pro': { + name: 'iPhone 14 Pro', + viewport: { width: 393, height: 852 }, + userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X)...', + isMobile: true, + deviceScaleFactor: 3, + }, + 'iphone-15-pro': { + name: 'iPhone 15 Pro', + viewport: { width: 393, height: 852 }, + userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)...', + isMobile: true, + deviceScaleFactor: 3, + }, + + // 新增 Android 系列 + 'pixel-7': { + name: 'Google Pixel 7', + viewport: { width: 412, height: 915 }, + userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel 7)...', + isMobile: true, + deviceScaleFactor: 2.625, + }, + 'samsung-galaxy-s23': { + name: 'Samsung Galaxy S23', + viewport: { width: 360, height: 780 }, + userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-S911B)...', + isMobile: true, + deviceScaleFactor: 3, + }, + + // 新增 iPad 系列 + 'ipad-air': { + name: 'iPad Air', + viewport: { width: 820, height: 1180 }, + userAgent: 'Mozilla/5.0 (iPad; CPU OS 16_0 like Mac OS X)...', + isMobile: true, + deviceScaleFactor: 2, + }, + 'ipad-pro-12-9': { + name: 'iPad Pro 12.9"', + viewport: { width: 1024, height: 1366 }, + userAgent: 'Mozilla/5.0 (iPad; CPU OS 16_0 like Mac OS X)...', + isMobile: true, + deviceScaleFactor: 2, + }, +}; +``` + +#### 3.1.3 网络配置数据 + +**文件位置**: `e2e/src/config/network-configs.ts` + +**网络配置示例**: + +```typescript +export const networkConfigs: Record = { + '2g-slow': { + name: '2G Slow', + offline: false, + downloadThroughput: 250 * 1024, // 250 Kbps + uploadThroughput: 50 * 1024, // 50 Kbps + latency: 2000, // 2000ms + }, + '3g-fast': { + name: '3G Fast', + offline: false, + downloadThroughput: 1.6 * 1024 * 1024, // 1.6 Mbps + uploadThroughput: 750 * 1024, // 750 Kbps + latency: 100, // 100ms + }, + '4g-lte': { + name: '4G LTE', + offline: false, + downloadThroughput: 4 * 1024 * 1024, // 4 Mbps + uploadThroughput: 3 * 1024 * 1024, // 3 Mbps + latency: 20, // 20ms + }, + 'wifi-fast': { + name: 'WiFi Fast', + offline: false, + downloadThroughput: 30 * 1024 * 1024, // 30 Mbps + uploadThroughput: 15 * 1024 * 1024, // 15 Mbps + latency: 2, // 2ms + }, + 'offline': { + name: 'Offline', + offline: true, + }, +}; +``` + +### 3.2 测试工具层详细设计 + +#### 3.2.1 手势模拟器 + +**文件位置**: `e2e/src/utils/GestureSimulator.ts` + +**核心功能**: + +```typescript +export class GestureSimulator { + constructor(private page: Page) {} + + // 单指滑动 + async swipe(options: SwipeOptions): Promise; + + // 双指捏合缩放 + async pinch(options: PinchOptions): Promise; + + // 长按 + async longPress(element: Locator, duration: number): Promise; + + // 双击 + async doubleTap(element: Locator): Promise; + + // 拖拽 + async drag(options: DragOptions): Promise; + + // 快速滑动(用于刷新) + async quickSwipeDown(): Promise; + + // 慢速滑动(用于浏览) + async slowSwipeUp(): Promise; +} +``` + +**使用示例**: + +```typescript +const gestureSimulator = new GestureSimulator(page); + +// 向上滑动页面 +await gestureSimulator.swipe({ + startX: 200, + startY: 600, + endX: 200, + endY: 200, + duration: 500, +}); + +// 双指缩放图片 +await gestureSimulator.pinch({ + centerX: 200, + centerY: 300, + startDistance: 100, + endDistance: 50, + duration: 300, +}); + +// 长按元素 +await gestureSimulator.longPress(page.locator('.card'), 1000); +``` + +#### 3.2.2 网络环境模拟器 + +**文件位置**: `e2e/src/utils/NetworkSimulator.ts` + +**核心功能**: + +```typescript +export class NetworkSimulator { + constructor(private context: BrowserContext) {} + + // 设置网络条件 + async setNetworkCondition(config: NetworkConfig): Promise; + + // 切换到离线模式 + async goOffline(): Promise; + + // 切换到在线模式 + async goOnline(): Promise; + + // 模拟网络切换 + async simulateNetworkSwitch(fromConfig: NetworkConfig, toConfig: NetworkConfig): Promise; + + // 重置网络条件 + async resetNetworkCondition(): Promise; + + // 监控网络请求 + async monitorNetworkRequests(): Promise; +} +``` + +**使用示例**: + +```typescript +const networkSimulator = new NetworkSimulator(context); + +// 设置 3G 网络条件 +await networkSimulator.setNetworkCondition(networkConfigs['3g-fast']); + +// 测试弱网环境下的页面加载 +await page.goto('/'); + +// 切换到离线模式 +await networkSimulator.goOffline(); + +// 验证离线缓存功能 +await expect(page.locator('.offline-content')).toBeVisible(); + +// 恢复在线 +await networkSimulator.goOnline(); +``` + +#### 3.2.3 性能监控器 + +**文件位置**: `e2e/src/utils/MobilePerformanceMonitor.ts` + +**核心功能**: + +```typescript +export class MobilePerformanceMonitor { + constructor(private page: Page) {} + + // 运行 Lighthouse 性能测试 + async runLighthouseAudit(options: LighthouseOptions): Promise; + + // 获取 Core Web Vitals 指标 + async getCoreWebVitals(): Promise; + + // 检测性能回归 + async detectPerformanceRegression(baseline: PerformanceBaseline): Promise; + + // 生成性能报告 + async generatePerformanceReport(): Promise; + + // 监控资源加载 + async monitorResourceLoading(): Promise; + + // 监控内存使用 + async monitorMemoryUsage(): Promise; +} +``` + +**使用示例**: + +```typescript +const performanceMonitor = new MobilePerformanceMonitor(page); + +// 运行 Lighthouse 审计 +const lighthouseResult = await performanceMonitor.runLighthouseAudit({ + onlyAudits: ['first-contentful-paint', 'largest-contentful-paint', 'cumulative-layout-shift'], +}); + +// 获取 Core Web Vitals +const vitals = await performanceMonitor.getCoreWebVitals(); +expect(vitals.LCP).toBeLessThan(2500); +expect(vitals.CLS).toBeLessThan(0.1); + +// 检测性能回归 +const regressionReport = await performanceMonitor.detectPerformanceRegression(baseline); +expect(regressionReport.hasRegression).toBe(false); +``` + +### 3.3 测试用例层详细设计 + +#### 3.3.1 性能测试套件 + +**文件位置**: `e2e/src/tests/mobile/performance/mobile-performance.spec.ts` + +**测试用例结构**: + +```typescript +test.describe('移动端性能测试 @mobile @performance', () => { + const devices = getMobileDevices(); + const networkConfigs = ['wifi-fast', '4g-lte', '3g-fast', '2g-slow']; + + for (const device of devices) { + for (const network of networkConfigs) { + test(`${device.name} - ${network} - 首屏加载性能`, async ({ page, context }) => { + // 设置设备和网络条件 + await page.setViewportSize(device.viewport); + await new NetworkSimulator(context).setNetworkCondition(networkConfigs[network]); + + // 运行性能测试 + const monitor = new MobilePerformanceMonitor(page); + await page.goto('/'); + + const vitals = await monitor.getCoreWebVitals(); + + // 验证性能指标 + expect(vitals.LCP).toBeLessThan(getPerformanceThreshold(device.name, network, 'LCP')); + expect(vitals.FCP).toBeLessThan(getPerformanceThreshold(device.name, network, 'FCP')); + expect(vitals.CLS).toBeLessThan(getPerformanceThreshold(device.name, network, 'CLS')); + }); + } + } + + test('移动端 - 交互响应性能', async ({ page }) => { + await page.setViewportSize({ width: 375, height: 667 }); + await page.goto('/'); + + const monitor = new MobilePerformanceMonitor(page); + + // 测量点击响应时间 + const button = page.locator('button').first(); + const startTime = Date.now(); + await button.click(); + const responseTime = Date.now() - startTime; + + expect(responseTime).toBeLessThan(100); + }); + + test('移动端 - 内存泄漏检测', async ({ page }) => { + await page.setViewportSize({ width: 375, height: 667 }); + await page.goto('/'); + + const monitor = new MobilePerformanceMonitor(page); + + // 执行多次页面导航 + const initialMemory = await monitor.monitorMemoryUsage(); + for (let i = 0; i < 10; i++) { + await page.goto('/about'); + await page.goto('/'); + } + const finalMemory = await monitor.monitorMemoryUsage(); + + // 验证内存增长在合理范围内 + const memoryGrowth = finalMemory.usedJSHeapSize - initialMemory.usedJSHeapSize; + expect(memoryGrowth).toBeLessThan(10 * 1024 * 1024); // 10MB + }); +}); +``` + +#### 3.3.2 兼容性测试套件 + +**文件位置**: `e2e/src/tests/mobile/compatibility/device-compatibility.spec.ts` + +**测试用例结构**: + +```typescript +test.describe('移动端兼容性测试 @mobile @compatibility', () => { + const devices = getMobileDevices(); + + for (const device of devices) { + test.describe(`${device.name} 兼容性`, () => { + test('布局适配正确', async ({ page }) => { + await page.setViewportSize(device.viewport); + await page.goto('/'); + + // 验证关键元素可见 + await expect(page.locator('header')).toBeVisible(); + await expect(page.locator('main')).toBeVisible(); + await expect(page.locator('footer')).toBeVisible(); + + // 验证布局没有溢出 + const bodyWidth = await page.evaluate(() => document.body.scrollWidth); + const viewportWidth = device.viewport.width; + expect(bodyWidth).toBeLessThanOrEqual(viewportWidth); + }); + + test('导航功能正常', async ({ page }) => { + await page.setViewportSize(device.viewport); + await page.goto('/'); + + // 测试移动菜单 + const menuButton = page.locator('button[aria-label="打开菜单"]'); + if (await menuButton.isVisible()) { + await menuButton.click(); + await expect(page.locator('#mobile-menu')).toBeVisible(); + } + + // 测试页面导航 + await page.goto('/about'); + await expect(page).toHaveURL(/.*about.*/); + }); + + test('表单功能正常', async ({ page }) => { + await page.setViewportSize(device.viewport); + await page.goto('/contact'); + + // 测试表单输入 + const nameInput = page.locator('input[name="name"]'); + await nameInput.fill('测试用户'); + + const nameValue = await nameInput.inputValue(); + expect(nameValue).toBe('测试用户'); + }); + }); + } + + test('跨设备布局一致性', async ({ page }) => { + const screenshots: Buffer[] = []; + + for (const device of devices) { + await page.setViewportSize(device.viewport); + await page.goto('/'); + await page.waitForLoadState('networkidle'); + + const screenshot = await page.screenshot(); + screenshots.push(screenshot); + } + + // 对比截图,验证布局一致性 + for (let i = 1; i < screenshots.length; i++) { + const similarity = await compareImages(screenshots[0], screenshots[i]); + expect(similarity).toBeGreaterThan(0.85); // 85% 相似度 + } + }); +}); +``` + +#### 3.3.3 手势交互测试套件 + +**文件位置**: `e2e/src/tests/mobile/gesture/gesture-interaction.spec.ts` + +**测试用例结构**: + +```typescript +test.describe('移动端手势交互测试 @mobile @gesture', () => { + let gestureSimulator: GestureSimulator; + + test.beforeEach(async ({ page }) => { + await page.setViewportSize({ width: 375, height: 667 }); + gestureSimulator = new GestureSimulator(page); + }); + + test('单指滑动 - 页面滚动', async ({ page }) => { + await page.goto('/'); + + const initialScrollY = await page.evaluate(() => window.scrollY); + expect(initialScrollY).toBe(0); + + // 向上滑动 + await gestureSimulator.swipe({ + startX: 200, + startY: 600, + endX: 200, + endY: 200, + duration: 500, + }); + + const afterScrollY = await page.evaluate(() => window.scrollY); + expect(afterScrollY).toBeGreaterThan(0); + }); + + test('双指捏合 - 图片缩放', async ({ page }) => { + await page.goto('/products'); + + const image = page.locator('.product-image').first(); + await image.click(); + + // 双指放大 + await gestureSimulator.pinch({ + centerX: 200, + centerY: 300, + startDistance: 100, + endDistance: 50, + duration: 300, + }); + + // 验证图片已放大 + const transform = await image.evaluate((el) => { + const style = window.getComputedStyle(el); + return style.transform; + }); + expect(transform).toContain('scale'); + }); + + test('长按 - 上下文菜单', async ({ page }) => { + await page.goto('/'); + + const card = page.locator('.card').first(); + + // 长按卡片 + await gestureSimulator.longPress(card, 1000); + + // 验证上下文菜单出现 + await expect(page.locator('.context-menu')).toBeVisible(); + }); + + test('双击 - 图片放大', async ({ page }) => { + await page.goto('/products'); + + const image = page.locator('.product-image').first(); + + // 双击图片 + await gestureSimulator.doubleTap(image); + + // 验证图片已放大 + const transform = await image.evaluate((el) => { + const style = window.getComputedStyle(el); + return style.transform; + }); + expect(transform).toContain('scale'); + }); + + test('拖拽 - 卡片排序', async ({ page }) => { + await page.goto('/products'); + + const firstCard = page.locator('.card').first(); + const secondCard = page.locator('.card').nth(1); + + const firstCardInitialPosition = await firstCard.boundingBox(); + + // 拖拽第一个卡片到第二个卡片位置 + await gestureSimulator.drag({ + source: firstCard, + target: secondCard, + duration: 500, + }); + + // 验证卡片位置已改变 + const firstCardFinalPosition = await firstCard.boundingBox(); + expect(firstCardFinalPosition.y).toBeGreaterThan(firstCardInitialPosition.y); + }); + + test('快速滑动 - 下拉刷新', async ({ page }) => { + await page.goto('/news'); + + // 快速向下滑动 + await gestureSimulator.quickSwipeDown(); + + // 验证刷新指示器出现 + await expect(page.locator('.refresh-indicator')).toBeVisible(); + + // 等待刷新完成 + await page.waitForSelector('.refresh-indicator', { state: 'hidden', timeout: 5000 }); + }); +}); +``` + +#### 3.3.4 PWA 功能测试套件 + +**文件位置**: `e2e/src/tests/mobile/pwa/pwa-functionality.spec.ts` + +**测试用例结构**: + +```typescript +test.describe('移动端 PWA 功能测试 @mobile @pwa', () => { + test('Service Worker 注册成功', async ({ page }) => { + await page.goto('/'); + + // 等待 Service Worker 注册 + const swRegistration = await page.evaluate(() => { + return navigator.serviceWorker.getRegistration(); + }); + + expect(swRegistration).toBeTruthy(); + }); + + test('离线缓存功能正常', async ({ page, context }) => { + // 首次访问,缓存资源 + await page.goto('/'); + await page.waitForLoadState('networkidle'); + + // 切换到离线模式 + await context.setOffline(true); + + // 刷新页面,应该从缓存加载 + await page.reload(); + await expect(page.locator('header')).toBeVisible(); + await expect(page.locator('main')).toBeVisible(); + + // 恢复在线 + await context.setOffline(false); + }); + + test('应用安装提示出现', async ({ page }) => { + await page.goto('/'); + + // 检查是否有安装提示 + const installPrompt = await page.evaluate(() => { + return 'beforeinstallprompt' in window; + }); + + expect(installPrompt).toBe(true); + }); + + test('离线页面显示正确', async ({ page, context }) => { + // 首次访问 + await page.goto('/'); + await page.waitForLoadState('networkidle'); + + // 切换到离线模式 + await context.setOffline(true); + + // 访问未缓存的页面 + await page.goto('/non-existent-page'); + + // 验证显示离线页面 + await expect(page.locator('.offline-page')).toBeVisible(); + }); + + test('应用更新检测正常', async ({ page }) => { + await page.goto('/'); + + // 模拟应用更新 + await page.evaluate(() => { + window.dispatchEvent(new Event('sw-update')); + }); + + // 验证更新提示出现 + await expect(page.locator('.update-notification')).toBeVisible(); + }); +}); +``` + +### 3.4 测试执行层详细设计 + +#### 3.4.1 测试配置扩展 + +**文件位置**: `e2e/playwright.config.ts` + +**扩展配置**: + +```typescript +import { defineConfig, devices } from '@playwright/test'; +import { getEnvironment } from './src/config/environments'; +import { getMobileDevices } from './src/utils/devices'; + +const env = getEnvironment(); +const mobileDevices = getMobileDevices(); + +export default defineConfig({ + testDir: './src/tests', + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: env.retries, + workers: process.env.CI ? 4 : undefined, + reporter: [ + ['html', { open: 'never' }], + ['json', { outputFile: 'test-results/results.json' }], + ['junit', { outputFile: 'test-results/junit.xml' }], + ['line'], + ['list'], + ['allure-playwright', { + outputFolder: 'allure-results', + detail: true, + suiteTitle: false, + }], + ], + timeout: env.timeout, + expect: { + timeout: 30000 + }, + use: { + baseURL: env.baseURL, + trace: env.trace, + screenshot: env.screenshot, + video: env.video, + headless: env.headless, + viewport: { width: 1280, height: 720 }, + actionTimeout: 30000, + navigationTimeout: 60000, + launchOptions: { + slowMo: env.slowMo, + }, + }, + projects: [ + // 桌面浏览器项目 + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + + // 移动设备项目 + ...mobileDevices.map(device => ({ + name: `mobile-${device.name.replace(/\s+/g, '-').toLowerCase()}`, + use: { + ...devices['Mobile Chrome'], + viewport: device.viewport, + userAgent: device.userAgent, + deviceScaleFactor: device.deviceScaleFactor, + isMobile: true, + }, + })), + + // 性能测试项目 + { + name: 'performance-mobile', + use: { + ...devices['Mobile Chrome'], + viewport: { width: 375, height: 667 }, + isMobile: true, + }, + testMatch: /.*\.perf\.spec\.ts/, + }, + + // PWA 测试项目 + { + name: 'pwa-mobile', + use: { + ...devices['Mobile Chrome'], + viewport: { width: 375, height: 667 }, + isMobile: true, + serviceWorkers: 'allow', + }, + testMatch: /.*\.pwa\.spec\.ts/, + }, + ], + webServer: env.name === 'development' ? { + command: 'cd .. && npm run dev', + url: 'http://localhost:3000', + timeout: 120000, + reuseExistingServer: !process.env.CI, + } : undefined, +}); +``` + +#### 3.4.2 并行测试策略 + +**文件位置**: `e2e/src/utils/TestScheduler.ts` + +**核心功能**: + +```typescript +export class TestScheduler { + private testQueue: TestTask[] = []; + private runningTasks: Map = new Map(); + + // 添加测试任务 + addTest(task: TestTask): void; + + // 智能调度测试 + async scheduleTests(): Promise; + + // 监控测试执行 + monitorExecution(): ExecutionStatus; + + // 优化执行顺序 + optimizeExecutionOrder(): TestTask[]; + + // 获取执行统计 + getExecutionStats(): ExecutionStats; +} +``` + +**调度策略**: + +1. **设备级并行**: 不同设备上的测试用例可以同时执行 +2. **网络条件级并行**: 同一设备在不同网络条件下的测试可以并行 +3. **依赖关系优化**: 根据测试依赖关系优化执行顺序 +4. **资源使用优化**: 根据 CPU、内存使用情况动态调整并行度 + +#### 3.4.3 测试报告系统 + +**文件位置**: `e2e/src/utils/MobileTestReporter.ts` + +**核心功能**: + +```typescript +export class MobileTestReporter { + // 生成移动端测试概览 + generateOverview(): TestOverview; + + // 生成设备兼容性报告 + generateCompatibilityReport(): CompatibilityReport; + + // 生成性能分析报告 + generatePerformanceReport(): PerformanceReport; + + // 生成网络环境影响报告 + generateNetworkImpactReport(): NetworkImpactReport; + + // 生成 HTML 报告 + generateHtmlReport(): string; + + // 生成 JSON 报告 + generateJsonReport(): object; + + // 生成 JUnit 报告 + generateJUnitReport(): string; +} +``` + +**报告内容**: + +1. **移动端测试概览** + - 总体通过率 + - 各设备测试结果 + - 执行时间统计 + - 失败用例分析 + +2. **设备兼容性报告** + - 每个设备上的测试结果对比 + - 兼容性问题汇总 + - 布局差异截图 + - 功能降级情况 + +3. **性能分析报告** + - Core Web Vitals 指标趋势 + - 性能回归检测结果 + - 资源加载分析 + - 性能优化建议 + +4. **网络环境影响报告** + - 不同网络条件下的性能对比 + - 离线功能验证结果 + - 网络切换测试结果 + - 弱网优化建议 + +--- + +## 4. 实施计划 + +### 4.1 第一阶段:基础设施扩展(优先级:高) + +**时间**: 1-2 周 + +**任务**: + +1. **扩展设备配置** + - [ ] 添加 iPhone 13/14/15 系列配置 + - [ ] 添加 Samsung Galaxy 系列配置 + - [ ] 添加 Google Pixel 系列配置 + - [ ] 添加 iPad Air/Pro 系列配置 + - [ ] 更新设备选择工具函数 + +2. **建立移动端测试数据生成器** + - [ ] 创建 `MobileTestDataGenerator` 类 + - [ ] 实现设备特定数据生成 + - [ ] 实现网络配置数据生成 + - [ ] 实现性能基准数据生成 + +3. **扩展 Playwright 配置** + - [ ] 添加移动设备测试项目 + - [ ] 配置性能测试项目 + - [ ] 配置 PWA 测试项目 + - [ ] 优化并行执行策略 + +**验收标准**: +- 所有新设备配置正确 +- 测试数据生成器功能完整 +- Playwright 配置支持多设备并行测试 + +### 4.2 第二阶段:核心测试工具开发(优先级:高) + +**时间**: 2-3 周 + +**任务**: + +1. **开发手势模拟器** + - [ ] 实现 `GestureSimulator` 类 + - [ ] 实现单指滑动功能 + - [ ] 实现双指捏合功能 + - [ ] 实现长按功能 + - [ ] 实现双击功能 + - [ ] 实现拖拽功能 + - [ ] 编写使用文档和示例 + +2. **开发网络环境模拟器** + - [ ] 实现 `NetworkSimulator` 类 + - [ ] 实现网络条件设置 + - [ ] 实现离线模式切换 + - [ ] 实现网络切换模拟 + - [ ] 实现网络请求监控 + - [ ] 编写使用文档和示例 + +3. **集成 Lighthouse 性能测试** + - [ ] 实现 `MobilePerformanceMonitor` 类 + - [ ] 集成 Lighthouse API + - [ ] 实现 Core Web Vitals 监控 + - [ ] 实现性能回归检测 + - [ ] 实现性能报告生成 + - [ ] 编写使用文档和示例 + +4. **建立移动端测试报告系统** + - [ ] 实现 `MobileTestReporter` 类 + - [ ] 实现测试概览报告 + - [ ] 实现兼容性报告 + - [ ] 实现性能分析报告 + - [ ] 实现网络影响报告 + - [ ] 支持多种输出格式 + +**验收标准**: +- 手势模拟器支持所有基本手势 +- 网络模拟器支持所有网络场景 +- 性能监控器能准确测量 Core Web Vitals +- 测试报告系统生成完整的移动端报告 + +### 4.3 第三阶段:测试用例开发(优先级:中) + +**时间**: 3-4 周 + +**任务**: + +1. **开发性能测试套件** + - [ ] 首屏加载性能测试 + - [ ] 页面交互响应测试 + - [ ] 资源加载优化测试 + - [ ] 内存泄漏检测测试 + - [ ] 建立性能基准和阈值 + +2. **开发兼容性测试套件** + - [ ] 不同屏幕尺寸布局测试 + - [ ] 不同操作系统功能测试 + - [ ] 不同浏览器渲染测试 + - [ ] 不同系统版本降级测试 + - [ ] 建立设备测试矩阵 + +3. **开发手势交互测试套件** + - [ ] 单指滑动测试 + - [ ] 双指捏合缩放测试 + - [ ] 长按操作测试 + - [ ] 双击操作测试 + - [ ] 拖拽操作测试 + +4. **开发 PWA 功能测试套件** + - [ ] Service Worker 注册测试 + - [ ] 离线缓存功能测试 + - [ ] 应用安装测试 + - [ ] 推送通知测试 + - [ ] 应用更新测试 + +**验收标准**: +- 所有测试用例通过 +- 测试覆盖率达到预期目标 +- 测试执行时间在可接受范围内 + +### 4.4 第四阶段:高级功能和优化(优先级:低) + +**时间**: 2-3 周 + +**任务**: + +1. **开发复杂手势模拟器** + - [ ] 实现三指手势 + - [ ] 实现复杂手势组合 + - [ ] 优化手势识别算法 + +2. **完善网络环境模拟** + - [ ] 实现网络丢包模拟 + - [ ] 实现网络抖动模拟 + - [ ] 实现自定义网络配置 + +3. **建立 PWA 功能测试套件** + - [ ] 完善离线功能测试 + - [ ] 完善推送通知测试 + - [ ] 完善应用更新测试 + +4. **优化测试执行效率** + - [ ] 实现智能测试调度 + - [ ] 优化并行执行策略 + - [ ] 实现测试缓存机制 + +5. **完善测试报告系统** + - [ ] 优化报告可视化 + - [ ] 添加趋势分析 + - [ ] 添加性能对比功能 + +**验收标准**: +- 所有高级功能正常工作 +- 测试执行效率提升 30% 以上 +- 测试报告更加直观和有用 + +--- + +## 5. 技术栈 + +### 5.1 核心技术 + +- **测试框架**: Playwright +- **性能测试**: Lighthouse +- **网络模拟**: Chrome DevTools Protocol +- **报告生成**: Allure、HTML、JSON、JUnit +- **类型系统**: TypeScript + +### 5.2 依赖包 + +```json +{ + "dependencies": { + "@playwright/test": "^1.40.0", + "lighthouse": "^11.0.0", + "chrome-remote-interface": "^0.33.0", + "@types/lighthouse": "^11.0.0" + } +} +``` + +### 5.3 开发工具 + +- **代码编辑器**: VS Code +- **版本控制**: Git +- **包管理器**: npm +- **构建工具**: TypeScript Compiler + +--- + +## 6. 成功标准 + +### 6.1 功能完整性 + +- ✅ 支持所有主流移动设备(iPhone、Android、iPad) +- ✅ 支持所有基本手势操作(滑动、点击、长按、双击、拖拽) +- ✅ 支持所有网络条件(在线、离线、弱网、网络切换) +- ✅ 支持所有 Core Web Vitals 指标监控 +- ✅ 支持完整的 PWA 功能测试 + +### 6.2 测试覆盖率 + +- ✅ 移动端功能测试覆盖率 ≥ 80% +- ✅ 移动端性能测试覆盖率 ≥ 70% +- ✅ 移动端兼容性测试覆盖率 ≥ 90% +- ✅ 移动端 PWA 功能测试覆盖率 ≥ 85% + +### 6.3 性能指标 + +- ✅ 测试执行时间 ≤ 30 分钟(完整测试套件) +- ✅ 测试通过率 ≥ 95% +- ✅ 性能回归检测准确率 ≥ 90% +- ✅ 测试报告生成时间 ≤ 1 分钟 + +### 6.4 可维护性 + +- ✅ 代码覆盖率 ≥ 80% +- ✅ 文档完整性 ≥ 90% +- ✅ 代码审查通过率 100% +- ✅ 技术债务控制良好 + +--- + +## 7. 风险和挑战 + +### 7.1 技术风险 + +1. **Playwright 限制** + - 风险: 某些高级手势可能无法完美模拟 + - 缓解: 优先实现基本手势,高级手势作为可选功能 + +2. **网络模拟准确性** + - 风险: 模拟的网络条件可能与真实网络有差异 + - 缓解: 使用真实设备进行验证和校准 + +3. **性能测试稳定性** + - 风险: 性能测试结果可能受环境影响 + - 缓解: 建立稳定的测试环境,多次测试取平均值 + +### 7.2 项目风险 + +1. **时间压力** + - 风险: 项目时间可能紧张 + - 缓解: 采用敏捷开发,优先实现高优先级功能 + +2. **资源限制** + - 风险: 开发资源可能不足 + - 缓解: 合理分配资源,必要时调整优先级 + +3. **需求变更** + - 风险: 需求可能在开发过程中变更 + - 缓解: 采用迭代开发,及时响应需求变更 + +--- + +## 8. 后续优化方向 + +### 8.1 短期优化(1-3 个月) + +1. **真实设备集成** + - 集成 BrowserStack 或 Sauce Labs + - 支持真实设备测试 + - 建立设备云管理 + +2. **测试数据管理** + - 建立测试数据仓库 + - 支持数据版本控制 + - 实现数据复用机制 + +3. **测试结果分析** + - 建立测试结果趋势分析 + - 实现智能问题诊断 + - 提供优化建议 + +### 8.2 中期优化(3-6 个月) + +1. **AI 辅助测试** + - 使用 AI 生成测试用例 + - 智能测试用例选择 + - 自动化问题定位 + +2. **测试平台化** + - 构建测试管理平台 + - 支持测试计划管理 + - 实现测试资源调度 + +3. **持续监控** + - 建立生产环境监控 + - 实现实时性能监控 + - 集成告警机制 + +### 8.3 长期优化(6-12 个月) + +1. **测试即代码** + - 将测试代码与生产代码同等对待 + - 建立测试代码审查机制 + - 实现测试代码质量监控 + +2. **测试左移** + - 在需求分析阶段引入测试 + - 实现测试驱动开发 + - 建立质量门禁 + +3. **测试右移** + - 在生产环境进行测试 + - 实现金丝雀发布 + - 建立快速回滚机制 + +--- + +## 9. 总结 + +本设计文档详细描述了移动端测试完善方案的整体架构、详细设计、实施计划和技术栈。通过四个阶段的实施,我们将建立一个全面完善的移动端测试体系,覆盖性能、兼容性、手势交互、PWA 功能等方面。 + +该方案具有以下优势: + +1. **渐进式实施**: 在现有框架基础上逐步扩展,风险可控 +2. **全面覆盖**: 覆盖移动端测试的所有重要方面 +3. **可扩展性**: 架构设计支持后续功能扩展 +4. **可维护性**: 代码结构清晰,易于维护和优化 + +通过实施本方案,我们将显著提升移动端测试的质量和效率,为用户提供更好的移动端体验。 + +--- + +**文档版本**: 1.0 +**最后更新**: 2026-03-05 +**文档状态**: 待审核