关于 webapp-testing
webapp-testing 是使用 Playwright 测试 Web 应用的技能,面向开发者,涵盖端到端流程、视觉检查、无障碍审计、网络模拟和移动视口测试。
主要功能
- 01使用 Playwright 进行端到端测试
- 02截图与视觉回归检查
- 03分析浏览器控制台错误
- 04使用 axe-core 执行无障碍审计
- 05模拟网络请求
- 06测试移动视口
适用场景
- 01验证关键用户流程
- 02检查页面布局变化
- 03审计页面无障碍情况
- 04测试移动端响应行为
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ pixel-process-ug/ superkit-agents --skill webapp-testing安装后在 Claude Code 中输入/webapp-testing确认已加载。
详细介绍
Web 应用测试
概述
使用 Playwright 作为主要工具进行全面的 Web 应用测试。本技能涵盖端到端测试工作流,包括用于视觉验证的屏幕截图捕获、浏览器控制台日志分析、用户交互模拟、视觉回归测试、使用 axe-core 进行无障碍审计、网络请求模拟以及移动设备视口测试。
开始时请声明:“我正在使用 webapp-testing 技能进行基于 Playwright 的 Web 应用测试。”
阶段 1:测试规划
目标: 确定测试内容并搭建基础设施。
操作
- 确定需要测试的关键用户流程
- 定义测试环境和视口
- 设置测试 fixture 和数据
- 配置 Playwright 项目设置
- 建立视觉基准屏幕截图
用户流程优先级决策表
| 流程类型 | 优先级 | 测试深度 |
|---|---|---|
| 身份验证(登录/登出/注册) | 关键 | 完整的正常流程 + 错误路径 |
| 核心业务工作流(购买、提交) | 关键 | 完整的正常流程 + 错误 + 边界情况 |
| 导航和路由 | 高 | 所有主要路由 |
| 搜索和筛选 | 高 | 常见查询 + 空状态 |
| 设置和个人资料 | 中 | 正常流程 |
| 管理员/后台 | 中 | 仅关键操作 |
停止——在进入阶段 2 前,不要继续:
- 已确定关键用户流程并排定优先级
- 已定义测试环境和视口
- Playwright 配置已就绪
- 已定义测试数据策略
阶段 2:测试实现
目标: 使用页面对象模型和无障碍定位器编写测试。
操作
- 为关键页面编写页面对象模型
- 实现端到端测试场景
- 添加视觉回归快照
- 集成无障碍检查
- 为隔离测试配置网络模拟
Playwright 配置
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [
['html', { open: 'never' }],
['junit', { outputFile: 'test-results/junit.xml' }],
],
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
{ name: 'mobile-chrome', use: { ...devices['Pixel 5'] } },
{ name: 'mobile-safari', use: { ...devices['iPhone 13'] } },
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});
页面对象模型
class LoginPage {
constructor(private page: Page) {}
readonly emailInput = this.page.getByLabel('Email');
readonly passwordInput = this.page.getByLabel('Password');
readonly submitButton = this.page.getByRole('button', { name: 'Sign in' });
readonly errorMessage = this.page.getByRole('alert');
async login(email: string, password: string) {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.submitButton.click();
}
async expectError(message: string) {
await expect(this.errorMessage).toContainText(message);
}
}
定位器选择决策表
| 定位器类型 | 优先级 | 使用时机 |
|---|---|---|
getByRole | 首选 | 具有 ARIA 角色的任何元素(按钮、链接、标题) |
getByLabel | 次选 | 带有标签的表单字段 |
getByPlaceholder | 第三选择 | 没有可见标签的字段 |
getByText | 第四选择 | 非交互式可见文本 |
getByTestId | 最后选择 | 没有可用的无障碍定位器时 |
| CSS 选择器 / XPath | 永不使用 | 样式变更时会失效 |
停止——在进入阶段 3 前,不要继续:
- 已为关键页面创建页面对象模型
- 测试仅使用无障碍定位器
- 已建立视觉基准
- 已集成无障碍检查
- 已为隔离测试配置网络模拟
阶段 3:CI 集成
目标: 配置可靠、快速的 CI 测试执行。
操作
- 配置无头浏览器执行
- 设置屏幕截图产物收集
- 配置重试和不稳定测试检测
- 添加报告(HTML 报告、JUnit XML)
- 设置视觉差异审查流程
CI 配置检查清单
- 测试在 CI 中以无头模式运行
- 已启用重试(CI 中重试 2 次)
- 失败时收集屏幕截图和视频产物
- 输出用于 CI 集成的 JUnit XML
- 生成供手动审查的 HTML 报告
- 合并前审查视觉差异快照
停止——满足以下条件时,CI 集成完成:
- 测试在 CI 流水线中可靠运行
- 失败时会收集产物
- 已识别并修复不稳定测试(而非跳过)
屏幕截图捕获模式
全页
test('homepage renders correctly', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveScreenshot('homepage.png', {
fullPage: true,
maxDiffPixelRatio: 0.01,
});
});
元素级
test('navigation bar matches design', async ({ page }) => {
await page.goto('/');
const nav = page.getByRole('navigation');
await expect(nav).toHaveScreenshot('navbar.png');
});
动态内容遮罩
test('dashboard layout', async ({ page }) => {
await page.goto('/dashboard');
await expect(page).toHaveScreenshot('dashboard.png', {
mask: [
page.locator('[data-testid="timestamp"]'),
page.locator('[data-testid="user-avatar"]'),
page.locator('.chart-container'),
],
animations: 'disabled',
});
});
浏览器日志分析
test('no console errors on page load', async ({ page }) => {
const consoleErrors: string[] = [];
page.on('console', msg => {
if (msg.type() === 'error') consoleErrors.push(msg.text());
});
page.on('pageerror', error => {
consoleErrors.push(error.message);
});
await page.goto('/');
await page.waitForLoadState('networkidle');
expect(consoleErrors).toEqual([]);
});
使用 axe-core 进行无障碍测试
import AxeBuilder from '@axe-core/playwright';
test('page has no accessibility violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.exclude('.third-party-widget')
.analyze();
expect(results.violations).toEqual([]);
});
网络请求模拟
test('displays users from API', async ({ page }) => {
await page.route('**/api/users', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]),
});
});
await page.goto('/users');
await expect(page.getByText('Alice')).toBeVisible();
});
test('handles API errors gracefully', async ({ page }) => {
await page.route('**/api/users', route =>
route.fulfill({ status: 500, body: 'Internal Server Error' })
);
await page.goto('/users');
await expect(page.getByText('Something went wrong')).toBeVisible();
});
移动设备视口测试
test.describe('mobile responsive', () => {
test.use({ viewport: { width: 375, height: 667 } });
test('hamburger menu works', async ({ page }) => {
await page.goto('/');
await expect(page.getByRole('navigation')).not.toBeVisible();
await page.getByRole('button', { name: 'Menu' }).click();
await expect(page.getByRole('navigation')).toBeVisible();
});
});
测试组织
tests/
e2e/
auth/
login.spec.ts
register.spec.ts
checkout/
cart.spec.ts
payment.spec.ts
fixtures/
test-data.ts
auth.setup.ts
pages/
login.page.ts
dashboard.page.ts
utils/
helpers.ts
反模式 / 常见错误
| 反模式 | 错误原因 | 正确做法 |
|---|---|---|
| CSS 选择器或 XPath | 样式变更会导致失效 | 使用可访问的定位器(角色、标签、文本) |
page.waitForTimeout() | 延迟时间随意,测试不稳定 | 使用 expect().toBeVisible() 或类似方法 |
| 详细测试第三方组件 | 被测内容并非你的代码 | 测试集成,而不是第三方组件的内部实现 |
| 硬编码测试数据 | 在不同环境中会失效 | 使用夹具和工厂 |
| 测试依赖执行顺序 | 脆弱且难以调试 | 每个测试都必须独立 |
| 忽略不稳定的测试 | 会削弱对测试套件的信任 | 修复根本原因或将其隔离 |
| 截图未遮盖动态内容 | 每次截图都不同,测试总会失败 | 遮盖时间戳、头像和图表 |
| 不进行无障碍检查 | 缺少关键质量门槛 | 在每个页面上使用 axe-core |
集成点
| 技能 | 关联 |
|---|---|
senior-frontend | 通过端到端测试检验前端组件 |
testing-strategy | 端到端测试位于测试金字塔顶端 |
acceptance-testing | 用户流程测试可作为验收测试 |
performance-optimization | 可通过端到端测试验证性能预算 |
code-review | 检查测试是否使用可访问的定位器 |
security-review | 在端到端测试中测试安全标头和身份验证流程 |
质量检查清单
- 覆盖所有关键用户流程
- 测试使用可访问的定位器(角色、标签、文本)
- 对隔离测试进行网络模拟
- 已审核并批准视觉回归基线
- 对所有页面进行无障碍扫描
- 针对响应式功能进行移动视口测试
- 不使用
waitForTimeout(使用恰当的断言) - 已配置带重试的 CI 流水线
- 失败时收集截图产物
- 已识别并修复不稳定的测试(而非跳过)
技能类型
FLEXIBLE — 根据项目的关键路径调整测试深度。强烈建议采用页面对象模型模式和可访问的定位器。每个页面都必须进行无障碍检查。合并前必须审核视觉回归基线。
评论
支持 Markdown