# 移动端测试完善实施计划 > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** 建立全面完善的移动端测试体系,覆盖性能、兼容性、手势交互、PWA 功能等方面 **Architecture:** 采用分层架构设计,包括测试数据层、测试工具层、测试用例层、测试执行层。基于现有 Playwright 框架,渐进式扩展移动端测试能力。 **Tech Stack:** Playwright, TypeScript, Lighthouse, Chrome DevTools Protocol, Allure --- ## Phase 1: 基础设施扩展(1-2 周) ### Task 1.1: 扩展设备配置 - 添加 iPhone 系列 **Files:** - Modify: `e2e/src/utils/devices.ts` **Step 1: 添加 iPhone 13 Pro 配置** 在 `devices` 对象中添加: ```typescript '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) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1', isMobile: true, deviceScaleFactor: 3, }, ``` **Step 2: 添加 iPhone 14 Pro 配置** 在 `devices` 对象中添加: ```typescript '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) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1', isMobile: true, deviceScaleFactor: 3, }, ``` **Step 3: 添加 iPhone 15 Pro 配置** 在 `devices` 对象中添加: ```typescript '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) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1', isMobile: true, deviceScaleFactor: 3, }, ``` **Step 4: 验证设备配置** 运行测试验证新设备配置: ```bash cd e2e npm run test -- --grep "mobile-ux" --project=chromium ``` **Step 5: Commit** ```bash git add e2e/src/utils/devices.ts git commit -m "feat: add iPhone 13/14/15 Pro device configurations" ``` --- ### Task 1.2: 扩展设备配置 - 添加 Android 系列 **Files:** - Modify: `e2e/src/utils/devices.ts` **Step 1: 添加 Google Pixel 7 配置** 在 `devices` 对象中添加: ```typescript 'pixel-7': { name: 'Google Pixel 7', viewport: { width: 412, height: 915 }, userAgent: 'Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36', isMobile: true, deviceScaleFactor: 2.625, }, ``` **Step 2: 添加 Samsung Galaxy S23 配置** 在 `devices` 对象中添加: ```typescript 'samsung-galaxy-s23': { name: 'Samsung Galaxy S23', viewport: { width: 360, height: 780 }, userAgent: 'Mozilla/5.0 (Linux; Android 13; SM-S911B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36', isMobile: true, deviceScaleFactor: 3, }, ``` **Step 3: 验证 Android 设备配置** 运行测试验证新设备配置: ```bash cd e2e npm run test -- --grep "mobile-ux" --project=chromium ``` **Step 4: Commit** ```bash git add e2e/src/utils/devices.ts git commit -m "feat: add Android device configurations (Pixel 7, Galaxy S23)" ``` --- ### Task 1.3: 扩展设备配置 - 添加 iPad 系列 **Files:** - Modify: `e2e/src/utils/devices.ts` **Step 1: 添加 iPad Air 配置** 在 `devices` 对象中添加: ```typescript 'ipad-air': { name: 'iPad Air', viewport: { width: 820, height: 1180 }, userAgent: 'Mozilla/5.0 (iPad; CPU OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1', isMobile: true, deviceScaleFactor: 2, }, ``` **Step 2: 添加 iPad Pro 12.9" 配置** 在 `devices` 对象中添加: ```typescript '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) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1', isMobile: true, deviceScaleFactor: 2, }, ``` **Step 3: 验证 iPad 设备配置** 运行测试验证新设备配置: ```bash cd e2e npm run test -- --grep "mobile-ux" --project=chromium ``` **Step 4: Commit** ```bash git add e2e/src/utils/devices.ts git commit -m "feat: add iPad device configurations (iPad Air, iPad Pro 12.9)" ``` --- ### Task 1.4: 创建网络配置文件 **Files:** - Create: `e2e/src/config/network-configs.ts` **Step 1: 创建网络配置类型定义** 创建文件并添加类型定义: ```typescript export interface NetworkConfig { name: string; offline: boolean; downloadThroughput?: number; uploadThroughput?: number; latency?: number; } ``` **Step 2: 添加网络配置对象** 添加完整的网络配置: ```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, }, }; ``` **Step 3: 添加辅助函数** ```typescript export function getNetworkConfig(key: string): NetworkConfig { return networkConfigs[key] || networkConfigs['wifi-fast']; } export function getAllNetworkConfigs(): NetworkConfig[] { return Object.values(networkConfigs); } ``` **Step 4: 验证网络配置** 创建简单的验证测试: ```bash cd e2e node -e "const { networkConfigs } = require('./src/config/network-configs.ts'); console.log(Object.keys(networkConfigs));" ``` **Step 5: Commit** ```bash git add e2e/src/config/network-configs.ts git commit -m "feat: add network configuration for mobile testing" ``` --- ### Task 1.5: 创建移动端测试数据生成器 **Files:** - Create: `e2e/src/utils/MobileTestDataGenerator.ts` **Step 1: 创建基础类结构** ```typescript import { Page } from '@playwright/test'; import { getNetworkConfig, NetworkConfig } from '../config/network-configs'; import { getDevice, DeviceConfig } from './devices'; export class MobileTestDataGenerator { static generateUserAgent(device: string): string { const deviceConfig = getDevice(device); return deviceConfig.userAgent || ''; } static generateNetworkConfig(type: '2G' | '3G' | '4G' | 'WiFi' | 'offline'): NetworkConfig { const configMap = { '2G': '2g-slow', '3G': '3g-fast', '4G': '4g-lte', 'WiFi': 'wifi-fast', 'offline': 'offline', }; return getNetworkConfig(configMap[type]); } } ``` **Step 2: 添加触摸事件数据生成** ```typescript export interface TouchEventData { type: 'tap' | 'swipe' | 'pinch' | 'longPress'; x: number; y: number; duration?: number; distance?: number; } static generateTouchEvent(type: 'tap' | 'swipe' | 'pinch' | 'longPress'): TouchEventData { const baseData = { x: Math.floor(Math.random() * 375), y: Math.floor(Math.random() * 667), }; switch (type) { case 'tap': return { ...baseData, type: 'tap' }; case 'swipe': return { ...baseData, type: 'swipe', duration: 500 }; case 'pinch': return { ...baseData, type: 'pinch', distance: 100 }; case 'longPress': return { ...baseData, type: 'longPress', duration: 1000 }; default: return { ...baseData, type: 'tap' }; } } ``` **Step 3: 添加性能基准数据生成** ```typescript export interface PerformanceBaseline { device: string; network: string; LCP: number; FCP: number; CLS: number; FID: number; TTI: number; } static generatePerformanceBaseline(device: string, network: string): PerformanceBaseline { const deviceMultiplier = { 'mobile-375x667': 1.2, 'mobile-414x896': 1.1, 'tablet-768x1024': 0.9, 'desktop-1920x1080': 0.8, }[device] || 1; const networkMultiplier = { '2g-slow': 3, '3g-fast': 2, '4g-lte': 1.5, 'wifi-fast': 1, }[network] || 1; const multiplier = deviceMultiplier * networkMultiplier; return { device, network, LCP: 2500 * multiplier, FCP: 1800 * multiplier, CLS: 0.1, FID: 100 * multiplier, TTI: 3800 * multiplier, }; } ``` **Step 4: 验证测试数据生成器** 创建简单的验证脚本: ```bash cd e2e node -e "const { MobileTestDataGenerator } = require('./src/utils/MobileTestDataGenerator.ts'); console.log(MobileTestDataGenerator.generateTouchEvent('tap'));" ``` **Step 5: Commit** ```bash git add e2e/src/utils/MobileTestDataGenerator.ts git commit -m "feat: add mobile test data generator" ``` --- ### Task 1.6: 扩展 Playwright 配置 **Files:** - Modify: `e2e/playwright.config.ts` **Step 1: 导入新设备配置** 在文件顶部添加导入: ```typescript import { getMobileDevices } from './src/utils/devices'; ``` **Step 2: 添加移动设备项目** 在 `projects` 数组中添加移动设备项目: ```typescript const mobileDevices = getMobileDevices(); // 在现有项目后添加 ...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, }, })), ``` **Step 3: 添加性能测试项目** ```typescript { name: 'performance-mobile', use: { ...devices['Mobile Chrome'], viewport: { width: 375, height: 667 }, isMobile: true, }, testMatch: /.*\.perf\.spec\.ts/, }, ``` **Step 4: 添加 PWA 测试项目** ```typescript { name: 'pwa-mobile', use: { ...devices['Mobile Chrome'], viewport: { width: 375, height: 667 }, isMobile: true, serviceWorkers: 'allow', }, testMatch: /.*\.pwa\.spec\.ts/, }, ``` **Step 5: 验证配置** 运行测试验证新配置: ```bash cd e2e npm run test -- --list ``` **Step 6: Commit** ```bash git add e2e/playwright.config.ts git commit -m "feat: extend Playwright config for mobile testing" ``` --- ## Phase 2: 核心测试工具开发(2-3 周) ### Task 2.1: 创建手势模拟器 - 基础结构 **Files:** - Create: `e2e/src/utils/GestureSimulator.ts` **Step 1: 创建基础类结构** ```typescript import { Page, Locator } from '@playwright/test'; export interface SwipeOptions { startX: number; startY: number; endX: number; endY: number; duration: number; } export interface PinchOptions { centerX: number; centerY: number; startDistance: number; endDistance: number; duration: number; } export interface DragOptions { source: Locator; target: Locator; duration: number; } export class GestureSimulator { constructor(private page: Page) {} } ``` **Step 2: 验证基础结构** ```bash cd e2e node -e "const { GestureSimulator } = require('./src/utils/GestureSimulator.ts'); console.log('GestureSimulator class created');" ``` **Step 3: Commit** ```bash git add e2e/src/utils/GestureSimulator.ts git commit -m "feat: create GestureSimulator base structure" ``` --- ### Task 2.2: 实现手势模拟器 - 单指滑动 **Files:** - Modify: `e2e/src/utils/GestureSimulator.ts` **Step 1: 实现 swipe 方法** ```typescript async swipe(options: SwipeOptions): Promise { const { startX, startY, endX, endY, duration } = options; await this.page.touchscreen.tap(startX, startY); const steps = 10; const stepDuration = duration / steps; for (let i = 1; i <= steps; i++) { const x = startX + (endX - startX) * (i / steps); const y = startY + (endY - startY) * (i / steps); await this.page.touchscreen.touchMove(x, y); await this.page.waitForTimeout(stepDuration); } await this.page.touchscreen.touchEnd(); } ``` **Step 2: 添加快速滑动方法** ```typescript async quickSwipeDown(): Promise { const viewport = this.page.viewportSize(); if (!viewport) return; await this.swipe({ startX: viewport.width / 2, startY: viewport.height * 0.3, endX: viewport.width / 2, endY: viewport.height * 0.7, duration: 300, }); } async slowSwipeUp(): Promise { const viewport = this.page.viewportSize(); if (!viewport) return; await this.swipe({ startX: viewport.width / 2, startY: viewport.height * 0.7, endX: viewport.width / 2, endY: viewport.height * 0.3, duration: 800, }); } ``` **Step 3: 编写测试用例** 创建测试文件 `e2e/src/tests/utils/gesture-simulator.spec.ts`: ```typescript import { test, expect } from '@playwright/test'; import { GestureSimulator } from '../../utils/GestureSimulator'; test.describe('GestureSimulator - Swipe', () => { test('should perform swipe up', async ({ page }) => { await page.goto('/'); const simulator = new GestureSimulator(page); const initialScrollY = await page.evaluate(() => window.scrollY); expect(initialScrollY).toBe(0); await simulator.slowSwipeUp(); const afterScrollY = await page.evaluate(() => window.scrollY); expect(afterScrollY).toBeGreaterThan(0); }); }); ``` **Step 4: 运行测试验证** ```bash cd e2e npm run test -- e2e/src/tests/utils/gesture-simulator.spec.ts ``` **Step 5: Commit** ```bash git add e2e/src/utils/GestureSimulator.ts e2e/src/tests/utils/gesture-simulator.spec.ts git commit -m "feat: implement swipe gesture in GestureSimulator" ``` --- ### Task 2.3: 实现手势模拟器 - 双指捏合 **Files:** - Modify: `e2e/src/utils/GestureSimulator.ts` **Step 1: 实现 pinch 方法** ```typescript async pinch(options: PinchOptions): Promise { const { centerX, centerY, startDistance, endDistance, duration } = options; const startDistanceX = startDistance / 2; const startDistanceY = startDistance / 2; const endDistanceX = endDistance / 2; const endDistanceY = endDistance / 2; const steps = 10; const stepDuration = duration / steps; // Start pinch await this.page.touchscreen.tap(centerX - startDistanceX, centerY - startDistanceY); await this.page.touchscreen.tap(centerX + startDistanceX, centerY + startDistanceY); for (let i = 1; i <= steps; i++) { const progress = i / steps; const currentDistanceX = startDistanceX + (endDistanceX - startDistanceX) * progress; const currentDistanceY = startDistanceY + (endDistanceY - startDistanceY) * progress; await this.page.touchscreen.touchMove(centerX - currentDistanceX, centerY - currentDistanceY); await this.page.touchscreen.touchMove(centerX + currentDistanceX, centerY + currentDistanceY); await this.page.waitForTimeout(stepDuration); } await this.page.touchscreen.touchEnd(); await this.page.touchscreen.touchEnd(); } ``` **Step 2: 编写测试用例** ```typescript test('should perform pinch zoom', async ({ page }) => { await page.goto('/products'); const simulator = new GestureSimulator(page); const image = page.locator('.product-image').first(); await image.click(); await simulator.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).toBeTruthy(); }); ``` **Step 3: 运行测试验证** ```bash cd e2e npm run test -- e2e/src/tests/utils/gesture-simulator.spec.ts ``` **Step 4: Commit** ```bash git add e2e/src/utils/GestureSimulator.ts e2e/src/tests/utils/gesture-simulator.spec.ts git commit -m "feat: implement pinch gesture in GestureSimulator" ``` --- ### Task 2.4: 实现手势模拟器 - 长按和双击 **Files:** - Modify: `e2e/src/utils/GestureSimulator.ts` **Step 1: 实现长按方法** ```typescript async longPress(element: Locator, duration: number = 1000): Promise { const box = await element.boundingBox(); if (!box) throw new Error('Element not visible'); const x = box.x + box.width / 2; const y = box.y + box.height / 2; await this.page.touchscreen.tap(x, y); await this.page.waitForTimeout(duration); await this.page.touchscreen.touchEnd(); } ``` **Step 2: 实现双击方法** ```typescript async doubleTap(element: Locator): Promise { const box = await element.boundingBox(); if (!box) throw new Error('Element not visible'); const x = box.x + box.width / 2; const y = box.y + box.height / 2; await this.page.touchscreen.tap(x, y); await this.page.waitForTimeout(100); await this.page.touchscreen.tap(x, y); } ``` **Step 3: 编写测试用例** ```typescript test('should perform long press', async ({ page }) => { await page.goto('/'); const simulator = new GestureSimulator(page); const card = page.locator('.card').first(); await simulator.longPress(card, 1000); await expect(card).toBeVisible(); }); test('should perform double tap', async ({ page }) => { await page.goto('/products'); const simulator = new GestureSimulator(page); const image = page.locator('.product-image').first(); await simulator.doubleTap(image); await expect(image).toBeVisible(); }); ``` **Step 4: 运行测试验证** ```bash cd e2e npm run test -- e2e/src/tests/utils/gesture-simulator.spec.ts ``` **Step 5: Commit** ```bash git add e2e/src/utils/GestureSimulator.ts e2e/src/tests/utils/gesture-simulator.spec.ts git commit -m "feat: implement long press and double tap gestures" ``` --- ### Task 2.5: 实现手势模拟器 - 拖拽 **Files:** - Modify: `e2e/src/utils/GestureSimulator.ts` **Step 1: 实现拖拽方法** ```typescript async drag(options: DragOptions): Promise { const { source, target, duration } = options; const sourceBox = await source.boundingBox(); const targetBox = await target.boundingBox(); if (!sourceBox || !targetBox) { throw new Error('Source or target element not visible'); } const startX = sourceBox.x + sourceBox.width / 2; const startY = sourceBox.y + sourceBox.height / 2; const endX = targetBox.x + targetBox.width / 2; const endY = targetBox.y + targetBox.height / 2; await this.swipe({ startX, startY, endX, endY, duration, }); } ``` **Step 2: 编写测试用例** ```typescript test('should perform drag', async ({ page }) => { await page.goto('/products'); const simulator = new GestureSimulator(page); const firstCard = page.locator('.card').first(); const secondCard = page.locator('.card').nth(1); const firstCardInitialPosition = await firstCard.boundingBox(); await simulator.drag({ source: firstCard, target: secondCard, duration: 500, }); const firstCardFinalPosition = await firstCard.boundingBox(); if (firstCardInitialPosition && firstCardFinalPosition) { expect(firstCardFinalPosition.y).toBeGreaterThan(firstCardInitialPosition.y); } }); ``` **Step 3: 运行测试验证** ```bash cd e2e npm run test -- e2e/src/tests/utils/gesture-simulator.spec.ts ``` **Step 4: Commit** ```bash git add e2e/src/utils/GestureSimulator.ts e2e/src/tests/utils/gesture-simulator.spec.ts git commit -m "feat: implement drag gesture in GestureSimulator" ``` --- ### Task 2.6: 创建网络环境模拟器 - 基础结构 **Files:** - Create: `e2e/src/utils/NetworkSimulator.ts` **Step 1: 创建基础类结构** ```typescript import { BrowserContext, Page } from '@playwright/test'; import { NetworkConfig } from '../config/network-configs'; export interface NetworkRequest { url: string; method: string; status: number; duration: number; } export class NetworkSimulator { private requests: NetworkRequest[] = []; constructor(private context: BrowserContext) { this.setupRequestMonitoring(); } private setupRequestMonitoring(): void { this.context.on('request', (request) => { const startTime = Date.now(); request.on('response', (response) => { this.requests.push({ url: request.url(), method: request.method(), status: response.status(), duration: Date.now() - startTime, }); }); }); } } ``` **Step 2: 验证基础结构** ```bash cd e2e node -e "const { NetworkSimulator } = require('./src/utils/NetworkSimulator.ts'); console.log('NetworkSimulator class created');" ``` **Step 3: Commit** ```bash git add e2e/src/utils/NetworkSimulator.ts git commit -m "feat: create NetworkSimulator base structure" ``` --- ### Task 2.7: 实现网络环境模拟器 - 网络条件设置 **Files:** - Modify: `e2e/src/utils/NetworkSimulator.ts` **Step 1: 实现网络条件设置方法** ```typescript async setNetworkCondition(config: NetworkConfig): Promise { const cdpSession = await this.context.newCDPSession(this.context.pages()[0]!); if (config.offline) { await cdpSession.send('Network.emulateNetworkConditions', { offline: true, downloadThroughput: 0, uploadThroughput: 0, latency: 0, }); } else { await cdpSession.send('Network.emulateNetworkConditions', { offline: false, downloadThroughput: config.downloadThroughput, uploadThroughput: config.uploadThroughput, latency: config.latency, }); } } ``` **Step 2: 实现离线模式切换** ```typescript async goOffline(): Promise { const cdpSession = await this.context.newCDPSession(this.context.pages()[0]!); await cdpSession.send('Network.emulateNetworkConditions', { offline: true, downloadThroughput: 0, uploadThroughput: 0, latency: 0, }); } async goOnline(): Promise { const cdpSession = await this.context.newCDPSession(this.context.pages()[0]!); await cdpSession.send('Network.emulateNetworkConditions', { offline: false, downloadThroughput: -1, uploadThroughput: -1, latency: 0, }); } ``` **Step 3: 实现网络切换模拟** ```typescript async simulateNetworkSwitch(fromConfig: NetworkConfig, toConfig: NetworkConfig): Promise { await this.setNetworkCondition(fromConfig); await this.context.pages()[0]!.waitForTimeout(1000); await this.setNetworkCondition(toConfig); } ``` **Step 4: 实现重置网络条件** ```typescript async resetNetworkCondition(): Promise { const cdpSession = await this.context.newCDPSession(this.context.pages()[0]!); await cdpSession.send('Network.emulateNetworkConditions', { offline: false, downloadThroughput: -1, uploadThroughput: -1, latency: 0, }); } ``` **Step 5: 编写测试用例** 创建测试文件 `e2e/src/tests/utils/network-simulator.spec.ts`: ```typescript import { test, expect } from '@playwright/test'; import { NetworkSimulator } from '../../utils/NetworkSimulator'; import { networkConfigs } from '../../config/network-configs'; test.describe('NetworkSimulator', () => { test('should set 3G network condition', async ({ page, context }) => { const simulator = new NetworkSimulator(context); await simulator.setNetworkCondition(networkConfigs['3g-fast']); await page.goto('/'); await expect(page.locator('header')).toBeVisible(); }); test('should switch to offline mode', async ({ page, context }) => { const simulator = new NetworkSimulator(context); await page.goto('/'); await simulator.goOffline(); await page.reload(); await expect(page.locator('header')).toBeVisible(); }); }); ``` **Step 6: 运行测试验证** ```bash cd e2e npm run test -- e2e/src/tests/utils/network-simulator.spec.ts ``` **Step 7: Commit** ```bash git add e2e/src/utils/NetworkSimulator.ts e2e/src/tests/utils/network-simulator.spec.ts git commit -m "feat: implement network condition simulation in NetworkSimulator" ``` --- ### Task 2.8: 创建移动端性能监控器 - 基础结构 **Files:** - Create: `e2e/src/utils/MobilePerformanceMonitor.ts` **Step 1: 创建基础类结构** ```typescript import { Page } from '@playwright/test'; export interface CoreWebVitals { FCP: number; // First Contentful Paint LCP: number; // Largest Contentful Paint CLS: number; // Cumulative Layout Shift FID: number; // First Input Delay TTI: number; // Time to Interactive } export interface LighthouseResult { score: number; audits: Record; } export class MobilePerformanceMonitor { constructor(private page: Page) {} } ``` **Step 2: 验证基础结构** ```bash cd e2e node -e "const { MobilePerformanceMonitor } = require('./src/utils/MobilePerformanceMonitor.ts'); console.log('MobilePerformanceMonitor class created');" ``` **Step 3: Commit** ```bash git add e2e/src/utils/MobilePerformanceMonitor.ts git commit -m "feat: create MobilePerformanceMonitor base structure" ``` --- ### Task 2.9: 实现移动端性能监控器 - Core Web Vitals **Files:** - Modify: `e2e/src/utils/MobilePerformanceMonitor.ts` **Step 1: 实现 Core Web Vitals 监控** ```typescript async getCoreWebVitals(): Promise { const vitals = await this.page.evaluate(() => { return new Promise((resolve) => { const metrics: any = {}; const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.entryType === 'paint') { if (entry.name === 'first-contentful-paint') { metrics.FCP = entry.startTime; } } else if (entry.entryType === 'largest-contentful-paint') { metrics.LCP = entry.startTime; } else if (entry.entryType === 'layout-shift') { if (!metrics.CLS) metrics.CLS = 0; metrics.CLS += (entry as any).value; } } }); observer.observe({ entryTypes: ['paint', 'largest-contentful-paint', 'layout-shift'] }); setTimeout(() => { observer.disconnect(); resolve(metrics); }, 5000); }); }); return { FCP: vitals.FCP || 0, LCP: vitals.LCP || 0, CLS: vitals.CLS || 0, FID: 0, TTI: 0, }; } ``` **Step 2: 编写测试用例** 创建测试文件 `e2e/src/tests/utils/mobile-performance-monitor.spec.ts`: ```typescript import { test, expect } from '@playwright/test'; import { MobilePerformanceMonitor } from '../../utils/MobilePerformanceMonitor'; test.describe('MobilePerformanceMonitor', () => { test('should get Core Web Vitals', async ({ page }) => { await page.goto('/'); const monitor = new MobilePerformanceMonitor(page); const vitals = await monitor.getCoreWebVitals(); expect(vitals.FCP).toBeGreaterThan(0); expect(vitals.LCP).toBeGreaterThan(0); expect(vitals.CLS).toBeGreaterThanOrEqual(0); }); }); ``` **Step 3: 运行测试验证** ```bash cd e2e npm run test -- e2e/src/tests/utils/mobile-performance-monitor.spec.ts ``` **Step 4: Commit** ```bash git add e2e/src/utils/MobilePerformanceMonitor.ts e2e/src/tests/utils/mobile-performance-monitor.spec.ts git commit -m "feat: implement Core Web Vitals monitoring in MobilePerformanceMonitor" ``` --- ### Task 2.10: 实现移动端性能监控器 - Lighthouse 集成 **Files:** - Modify: `e2e/src/utils/MobilePerformanceMonitor.ts` **Step 1: 安装 Lighthouse 依赖** ```bash cd e2e npm install --save-dev lighthouse chrome-launcher npm install --save-dev @types/lighthouse ``` **Step 2: 实现 Lighthouse 审计方法** ```typescript import lighthouse from 'lighthouse'; import * as chromeLauncher from 'chrome-launcher'; async runLighthouseAudit(url: string): Promise { const chrome = await chromeLauncher.launch({ chromeFlags: ['--headless'] }); const options = { logLevel: 'info' as const, output: 'json' as const, onlyCategories: ['performance'], port: chrome.port, }; const runnerResult = await lighthouse(url, options); await chrome.kill(); if (!runnerResult) { throw new Error('Lighthouse audit failed'); } return { score: runnerResult.lhr.categories.performance.score * 100, audits: runnerResult.lhr.audits, }; } ``` **Step 3: 编写测试用例** ```typescript test('should run Lighthouse audit', async ({ page }) => { await page.goto('/'); const monitor = new MobilePerformanceMonitor(page); const result = await monitor.runLighthouseAudit(page.url()); expect(result.score).toBeGreaterThan(0); expect(result.audits).toBeDefined(); }); ``` **Step 4: 运行测试验证** ```bash cd e2e npm run test -- e2e/src/tests/utils/mobile-performance-monitor.spec.ts ``` **Step 5: Commit** ```bash git add e2e/src/utils/MobilePerformanceMonitor.ts e2e/src/tests/utils/mobile-performance-monitor.spec.ts e2e/package.json e2e/package-lock.json git commit -m "feat: integrate Lighthouse in MobilePerformanceMonitor" ``` --- ## Phase 3: 测试用例开发(3-4 周) ### Task 3.1: 创建移动端性能测试套件 **Files:** - Create: `e2e/src/tests/mobile/performance/mobile-performance.spec.ts` **Step 1: 创建测试套件基础结构** ```typescript import { test, expect } from '../../../fixtures/base.fixture'; import { getMobileDevices } from '../../../utils/devices'; import { networkConfigs } from '../../../config/network-configs'; import { MobilePerformanceMonitor } from '../../../utils/MobilePerformanceMonitor'; import { MobileTestDataGenerator } from '../../../utils/MobileTestDataGenerator'; test.describe('移动端性能测试 @mobile @performance', () => { const devices = getMobileDevices(); const networkTypes = ['wifi-fast', '4g-lte', '3g-fast', '2g-slow'] as const; }); ``` **Step 2: 添加首屏加载性能测试** ```typescript for (const device of devices.slice(0, 3)) { for (const network of networkTypes) { test(`${device.name} - ${networkConfigs[network].name} - 首屏加载性能`, async ({ page, context }) => { await page.setViewportSize(device.viewport); const networkSimulator = new (await import('../../../utils/NetworkSimulator')).NetworkSimulator(context); await networkSimulator.setNetworkCondition(networkConfigs[network]); await page.goto('/'); await page.waitForLoadState('networkidle'); const monitor = new MobilePerformanceMonitor(page); const vitals = await monitor.getCoreWebVitals(); const baseline = MobileTestDataGenerator.generatePerformanceBaseline(device.name, network); expect(vitals.LCP).toBeLessThan(baseline.LCP); expect(vitals.FCP).toBeLessThan(baseline.FCP); expect(vitals.CLS).toBeLessThan(baseline.CLS); }); } } ``` **Step 3: 添加交互响应性能测试** ```typescript 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); }); ``` **Step 4: 运行测试验证** ```bash cd e2e npm run test -- e2e/src/tests/mobile/performance/mobile-performance.spec.ts ``` **Step 5: Commit** ```bash git add e2e/src/tests/mobile/performance/mobile-performance.spec.ts git commit -m "feat: add mobile performance test suite" ``` --- ### Task 3.2: 创建移动端兼容性测试套件 **Files:** - Create: `e2e/src/tests/mobile/compatibility/device-compatibility.spec.ts` **Step 1: 创建测试套件基础结构** ```typescript import { test, expect } from '../../../fixtures/base.fixture'; import { getMobileDevices } from '../../../utils/devices'; test.describe('移动端兼容性测试 @mobile @compatibility', () => { const devices = getMobileDevices(); }); ``` **Step 2: 添加布局适配测试** ```typescript for (const device of devices) { test(`${device.name} - 布局适配正确`, 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); expect(bodyWidth).toBeLessThanOrEqual(device.viewport.width); }); } ``` **Step 3: 添加导航功能测试** ```typescript for (const device of devices.slice(0, 3)) { test(`${device.name} - 导航功能正常`, 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.*/); }); } ``` **Step 4: 运行测试验证** ```bash cd e2e npm run test -- e2e/src/tests/mobile/compatibility/device-compatibility.spec.ts ``` **Step 5: Commit** ```bash git add e2e/src/tests/mobile/compatibility/device-compatibility.spec.ts git commit -m "feat: add mobile compatibility test suite" ``` --- ### Task 3.3: 创建移动端手势交互测试套件 **Files:** - Create: `e2e/src/tests/mobile/gesture/gesture-interaction.spec.ts` **Step 1: 创建测试套件基础结构** ```typescript import { test, expect } from '../../../fixtures/base.fixture'; import { GestureSimulator } from '../../../utils/GestureSimulator'; test.describe('移动端手势交互测试 @mobile @gesture', () => { let gestureSimulator: GestureSimulator; test.beforeEach(async ({ page }) => { await page.setViewportSize({ width: 375, height: 667 }); gestureSimulator = new GestureSimulator(page); }); }); ``` **Step 2: 添加单指滑动测试** ```typescript test('单指滑动 - 页面滚动', async ({ page }) => { await page.goto('/'); const initialScrollY = await page.evaluate(() => window.scrollY); expect(initialScrollY).toBe(0); await gestureSimulator.slowSwipeUp(); const afterScrollY = await page.evaluate(() => window.scrollY); expect(afterScrollY).toBeGreaterThan(0); }); ``` **Step 3: 添加长按测试** ```typescript test('长按 - 元素长按', async ({ page }) => { await page.goto('/'); const card = page.locator('.card').first(); await gestureSimulator.longPress(card, 1000); await expect(card).toBeVisible(); }); ``` **Step 4: 添加双击测试** ```typescript test('双击 - 元素双击', async ({ page }) => { await page.goto('/products'); const image = page.locator('.product-image').first(); await gestureSimulator.doubleTap(image); await expect(image).toBeVisible(); }); ``` **Step 5: 运行测试验证** ```bash cd e2e npm run test -- e2e/src/tests/mobile/gesture/gesture-interaction.spec.ts ``` **Step 6: Commit** ```bash git add e2e/src/tests/mobile/gesture/gesture-interaction.spec.ts git commit -m "feat: add mobile gesture interaction test suite" ``` --- ### Task 3.4: 创建移动端 PWA 功能测试套件 **Files:** - Create: `e2e/src/tests/mobile/pwa/pwa-functionality.spec.ts` **Step 1: 创建测试套件基础结构** ```typescript import { test, expect } from '../../../fixtures/base.fixture'; test.describe('移动端 PWA 功能测试 @mobile @pwa', () => { test.use({ serviceWorkers: 'allow' }); }); ``` **Step 2: 添加 Service Worker 注册测试** ```typescript test('Service Worker 注册成功', async ({ page }) => { await page.goto('/'); const swRegistration = await page.evaluate(() => { return navigator.serviceWorker.getRegistration(); }); expect(swRegistration).toBeTruthy(); }); ``` **Step 3: 添加离线缓存功能测试** ```typescript 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); }); ``` **Step 4: 运行测试验证** ```bash cd e2e npm run test -- e2e/src/tests/mobile/pwa/pwa-functionality.spec.ts ``` **Step 5: Commit** ```bash git add e2e/src/tests/mobile/pwa/pwa-functionality.spec.ts git commit -m "feat: add mobile PWA functionality test suite" ``` --- ## Phase 4: 高级功能和优化(2-3 周) ### Task 4.1: 创建移动端测试报告系统 **Files:** - Create: `e2e/src/utils/MobileTestReporter.ts` **Step 1: 创建基础类结构** ```typescript import { FullConfig, FullResult, Suite, TestCase, TestResult } from '@playwright/test'; export interface TestOverview { total: number; passed: number; failed: number; skipped: number; duration: number; } export interface DeviceTestResult { device: string; passed: number; failed: number; duration: number; } export class MobileTestReporter { constructor(private config: FullConfig) {} generateOverview(results: FullResult): TestOverview { const total = results.suites.reduce((sum, suite) => { return sum + suite.suites.reduce((suiteSum, subSuite) => { return suiteSum + subSuite.cases.length; }, 0); }, 0); const passed = results.suites.reduce((sum, suite) => { return sum + suite.suites.reduce((suiteSum, subSuite) => { return suiteSum + subSuite.cases.filter(c => c.results[0]?.status === 'passed').length; }, 0); }, 0); const failed = results.suites.reduce((sum, suite) => { return sum + suite.suites.reduce((suiteSum, subSuite) => { return suiteSum + subSuite.cases.filter(c => c.results[0]?.status === 'failed').length; }, 0); }, 0); return { total, passed, failed, skipped: total - passed - failed, duration: results.duration, }; } } ``` **Step 2: 验证基础结构** ```bash cd e2e node -e "const { MobileTestReporter } = require('./src/utils/MobileTestReporter.ts'); console.log('MobileTestReporter class created');" ``` **Step 3: Commit** ```bash git add e2e/src/utils/MobileTestReporter.ts git commit -m "feat: create MobileTestReporter base structure" ``` --- ### Task 4.2: 实现移动端测试报告 - HTML 报告 **Files:** - Modify: `e2e/src/utils/MobileTestReporter.ts` **Step 1: 实现 HTML 报告生成** ```typescript generateHtmlReport(results: FullResult): string { const overview = this.generateOverview(results); return ` 移动端测试报告

移动端测试报告

${overview.total}
总测试数
${overview.passed}
通过
${overview.failed}
失败
${(overview.duration / 1000).toFixed(2)}s
执行时间
`; } ``` **Step 2: 添加报告保存方法** ```typescript async saveReport(report: string, outputPath: string): Promise { const fs = await import('fs/promises'); await fs.writeFile(outputPath, report, 'utf-8'); } ``` **Step 3: 编写测试用例** ```typescript import { test, expect } from '@playwright/test'; import { MobileTestReporter } from '../../utils/MobileTestReporter'; test('MobileTestReporter should generate HTML report', async () => { const reporter = new MobileTestReporter({} as any); const html = reporter.generateHtmlReport({ suites: [], duration: 10000, status: 'passed', } as any); expect(html).toContain('移动端测试报告'); expect(html).toContain('总测试数'); }); ``` **Step 4: 运行测试验证** ```bash cd e2e npm run test -- e2e/src/tests/utils/mobile-test-reporter.spec.ts ``` **Step 5: Commit** ```bash git add e2e/src/utils/MobileTestReporter.ts e2e/src/tests/utils/mobile-test-reporter.spec.ts git commit -m "feat: implement HTML report generation in MobileTestReporter" ``` --- ### Task 4.3: 优化测试执行效率 - 并行测试 **Files:** - Modify: `e2e/playwright.config.ts` **Step 1: 优化 workers 配置** ```typescript workers: process.env.CI ? 4 : '50%', ``` **Step 2: 添加测试超时配置** ```typescript timeout: 60000, expect: { timeout: 30000, } ``` **Step 3: 验证配置** ```bash cd e2e npm run test -- --list ``` **Step 4: Commit** ```bash git add e2e/playwright.config.ts git commit -m "feat: optimize parallel test execution" ``` --- ### Task 4.4: 创建测试文档 **Files:** - Create: `e2e/docs/mobile-testing-guide.md` **Step 1: 创建测试指南文档** ```markdown # 移动端测试指南 ## 概述 本文档介绍如何使用移动端测试框架进行测试。 ## 手势模拟器 ### 基本用法 \`\`\`typescript import { GestureSimulator } from '../utils/GestureSimulator'; const simulator = new GestureSimulator(page); // 单指滑动 await simulator.swipe({ startX: 200, startY: 600, endX: 200, endY: 200, duration: 500, }); // 长按 await simulator.longPress(element, 1000); // 双击 await simulator.doubleTap(element); \`\`\` ## 网络模拟器 ### 基本用法 \`\`\`typescript import { NetworkSimulator } from '../utils/NetworkSimulator'; import { networkConfigs } from '../config/network-configs'; const simulator = new NetworkSimulator(context); // 设置网络条件 await simulator.setNetworkCondition(networkConfigs['3g-fast']); // 切换到离线模式 await simulator.goOffline(); // 恢复在线 await simulator.goOnline(); \`\`\` ## 性能监控器 ### 基本用法 \`\`\`typescript import { MobilePerformanceMonitor } from '../utils/MobilePerformanceMonitor'; const monitor = new MobilePerformanceMonitor(page); // 获取 Core Web Vitals const vitals = await monitor.getCoreWebVitals(); // 运行 Lighthouse 审计 const result = await monitor.runLighthouseAudit(page.url()); \`\`\` ## 运行测试 ### 运行所有移动端测试 \`\`\`bash npm run test -- --grep "@mobile" \`\`\` ### 运行特定设备测试 \`\`\`bash npm run test -- --project=mobile-iphone-13-pro \`\`\` ### 运行性能测试 \`\`\`bash npm run test -- --grep "@performance" \`\`\` ## 查看报告 测试完成后,可以在以下位置查看报告: - HTML 报告: `e2e/test-results/index.html` - Allure 报告: `e2e/allure-results/` \`\`\` ``` **Step 2: 验证文档** ```bash cd e2e cat docs/mobile-testing-guide.md ``` **Step 3: Commit** ```bash git add e2e/docs/mobile-testing-guide.md git commit -m "docs: add mobile testing guide" ``` --- ## 验收标准 ### Phase 1 验收 - [ ] 所有新设备配置正确 - [ ] 测试数据生成器功能完整 - [ ] Playwright 配置支持多设备并行测试 ### Phase 2 验收 - [ ] 手势模拟器支持所有基本手势 - [ ] 网络模拟器支持所有网络场景 - [ ] 性能监控器能准确测量 Core Web Vitals ### Phase 3 验收 - [ ] 所有测试用例通过 - [ ] 测试覆盖率达到预期目标 - [ ] 测试执行时间在可接受范围内 ### Phase 4 验收 - [ ] 测试报告系统生成完整的移动端报告 - [ ] 测试执行效率提升 30% 以上 - [ ] 文档完整且易于理解 --- ## 总结 本实施计划详细描述了移动端测试完善的四个阶段,每个阶段都包含具体的任务、步骤和验收标准。通过按照此计划执行,我们将建立一个全面完善的移动端测试体系。 **预计总时间**: 8-12 周 **预计总任务数**: 40+ 个任务 **预计提交次数**: 40+ 次