Claude-skill-registry e2e-bugfix
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/e2e-bugfix" ~/.claude/skills/majiayu000-claude-skill-registry-e2e-bugfix && rm -rf "$T"
manifest:
skills/data/e2e-bugfix/SKILL.mdsource content
E2E Bugfix Workflow Skill
本 skill 提供端到端测试 bugfix 的完整工作流知识,包括错误分类体系、置信度评分系统和 E2E 特有的调试技巧。
错误分类体系
E2E 测试失败主要分为以下类型(按频率排序):
1. 超时错误(35%)
症状:元素等待超时、操作超时
识别特征:
Timeout 30000ms exceededwaiting for locatorwaiting for elementTimeoutError
解决策略:使用显式等待和合理超时
// Before - 硬编码等待 await page.waitForTimeout(5000); await page.click('.submit-button'); // After - 等待特定条件 await page.waitForSelector('.submit-button', { state: 'visible' }); await page.click('.submit-button'); // 或使用 Playwright 的自动等待 await page.getByRole('button', { name: 'Submit' }).click();
常见原因:
- 页面加载慢
- 动态内容未渲染
- 网络请求延迟
- 元素被遮挡或不可见
2. 选择器错误(25%)
症状:找不到元素、选择器匹配多个元素
识别特征:
strict mode violationresolved to X elementselement not foundlocator.click: Error
解决策略:使用更精确的选择器
// Before - 模糊选择器 await page.click('button'); // 可能匹配多个 // After - 精确选择器 // 方法 1:使用 data-testid await page.click('[data-testid="submit-button"]'); // 方法 2:使用角色和文本 await page.getByRole('button', { name: 'Submit' }).click(); // 方法 3:使用组合选择器 await page.locator('.form-container').getByRole('button').click();
Playwright 推荐选择器优先级:
- 最语义化getByRole()
- 最稳定getByTestId()
- 用户可见getByText()- CSS/XPath - 最后手段
3. 断言错误(15%)
症状:期望值与实际值不匹配
识别特征:
expect(...).toHave*
vsExpected:Received:AssertionError
解决策略:使用正确的断言和等待
// Before - 立即断言 expect(await page.textContent('.message')).toBe('Success'); // After - 使用自动重试的断言 await expect(page.locator('.message')).toHaveText('Success'); // 异步内容断言 await expect(page.locator('.user-list')).toContainText('John'); // 可见性断言 await expect(page.locator('.modal')).toBeVisible();
4. 网络错误(12%)
症状:API 请求失败、网络拦截问题
识别特征:
错误Route handlernet::ERR_*request failed- Mock 数据不生效
解决策略:正确配置网络拦截
// Mock API 响应 await page.route('**/api/users', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ users: [{ id: 1, name: 'Test' }] }), }); }); // 等待网络请求完成 const responsePromise = page.waitForResponse('**/api/users'); await page.click('.load-users'); const response = await responsePromise; expect(response.status()).toBe(200);
5. 导航错误(8%)
症状:页面导航失败、URL 不匹配
识别特征:
page.goto: ErrorERR_NAME_NOT_RESOLVEDnavigation timeout- URL 重定向问题
解决策略:正确处理导航
// 等待导航完成 await page.goto('http://localhost:3000/login'); await page.waitForURL('**/dashboard'); // 处理重定向 await Promise.all([ page.waitForNavigation(), page.click('.login-button'), ]); // 验证 URL await expect(page).toHaveURL(/.*dashboard/);
6. 环境错误(3%)
症状:浏览器启动失败、测试环境问题
识别特征:
失败browser.launchTarget closed
错误context- 端口冲突
解决策略:检查环境配置
// playwright.config.ts export default defineConfig({ webServer: { command: 'npm run start', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, timeout: 120 * 1000, }, use: { baseURL: 'http://localhost:3000', trace: 'on-first-retry', screenshot: 'only-on-failure', }, });
置信度评分系统
评分标准(0-100)
| 分数 | 级别 | 行为 |
|---|---|---|
| 80+ | 高 | 自动执行 |
| 60-79 | 中 | 标记验证后继续 |
| 40-59 | 低 | 暂停询问用户 |
| <40 | 不确定 | 停止收集信息 |
置信度计算
置信度 = 证据质量(40%) + 模式匹配(30%) + 上下文完整性(20%) + 可复现性(10%)
证据质量:
- 高:有截图、trace、完整堆栈
- 中:有错误信息但缺上下文
- 低:仅有失败描述
模式匹配:
- 高:完全匹配已知错误模式
- 中:部分匹配
- 低:未知错误类型
上下文完整性:
- 高:测试代码 + 页面代码 + trace + 截图
- 中:只有测试代码
- 低:只有错误信息
可复现性:
- 高:每次运行都复现
- 中:偶发(flaky test)
- 低:仅在特定环境失败
E2E 调试技巧
使用 Trace Viewer
# 运行测试并收集 trace npx playwright test --trace on # 查看 trace npx playwright show-trace trace.zip
使用 UI 模式调试
# 启动 UI 模式 npx playwright test --ui # 或使用调试模式 npx playwright test --debug
截图和录像
// 测试失败时自动截图 test.afterEach(async ({ page }, testInfo) => { if (testInfo.status !== 'passed') { await page.screenshot({ path: `screenshots/${testInfo.title}.png` }); } }); // 录制视频 // playwright.config.ts use: { video: 'on-first-retry', }
处理 Flaky Tests
// 重试不稳定的测试 test.describe.configure({ retries: 2 }); // 或在配置中设置 export default defineConfig({ retries: process.env.CI ? 2 : 0, });
TDD 流程
RED Phase(写失败测试)
import { test, expect } from '@playwright/test'; test('should display error message on invalid login', async ({ page }) => { // 1. 导航到页面 await page.goto('/login'); // 2. 执行操作 await page.fill('[data-testid="email"]', 'invalid@email'); await page.fill('[data-testid="password"]', 'wrong'); await page.click('[data-testid="submit"]'); // 3. 断言期望结果 await expect(page.locator('.error-message')).toHaveText('Invalid credentials'); });
GREEN Phase(最小实现)
// 只实现让测试通过的最小功能 // 不要优化,不要添加额外功能
REFACTOR Phase(重构)
// 改善测试结构 // 提取 Page Object // 复用测试辅助函数
Page Object 模式
// pages/LoginPage.ts export class LoginPage { constructor(private page: Page) {} async goto() { await this.page.goto('/login'); } async login(email: string, password: string) { await this.page.fill('[data-testid="email"]', email); await this.page.fill('[data-testid="password"]', password); await this.page.click('[data-testid="submit"]'); } async getErrorMessage() { return this.page.locator('.error-message'); } } // 使用 Page Object test('login with invalid credentials', async ({ page }) => { const loginPage = new LoginPage(page); await loginPage.goto(); await loginPage.login('invalid@email', 'wrong'); await expect(loginPage.getErrorMessage()).toHaveText('Invalid credentials'); });
质量门禁
| 检查项 | 标准 |
|---|---|
| 测试通过率 | 100% |
| 代码覆盖率 | >= 90%(如适用) |
| Lint | 无错误 |
| Flaky Rate | < 5% |
常用命令
# 运行所有 E2E 测试 make test TARGET=e2e # 或使用 Playwright 直接运行 npx playwright test # 运行特定测试文件 npx playwright test tests/login.spec.ts # 运行带标签的测试 npx playwright test --grep @smoke # 运行 UI 模式 npx playwright test --ui # 生成测试代码 npx playwright codegen localhost:3000 # 查看测试报告 npx playwright show-report
Playwright 配置示例
// playwright.config.ts import { defineConfig, devices } from '@playwright/test'; export default defineConfig({ testDir: './tests', fullyParallel: true, forbidOnly: !!process.env.CI, retries: process.env.CI ? 2 : 0, workers: process.env.CI ? 1 : undefined, reporter: 'html', use: { baseURL: 'http://localhost:3000', trace: 'on-first-retry', screenshot: 'only-on-failure', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, { name: 'firefox', use: { ...devices['Desktop Firefox'] } }, { name: 'webkit', use: { ...devices['Desktop Safari'] } }, ], webServer: { command: 'npm run start', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, }, });
相关文档
文档路径由配置指定(
best_practices_dir),使用以下关键词搜索:
- 选择器策略:关键词 "selector", "locator", "data-testid"
- 等待策略:关键词 "wait", "timeout", "retry"
- 网络拦截:关键词 "intercept", "mock", "route"
- 问题诊断:关键词 "troubleshooting", "debugging", "flaky"