This skill should be used when the user asks to "debug E2E tests", "fix Playwright failures", "fix Cypress tests", "analyze timeout errors", or mentions keywords like "Playwright", "Cypress", "Timeout exceeded", "locator", "selector", "flaky test". It provides the complete bugfix workflow knowledge including error classification, confidence scoring, and E2E-specific debugging techniques.
Detects E2E test failures from Playwright/Cypress error patterns and provides targeted debugging workflows. Triggers on keywords like "debug E2E tests", "fix Playwright failures", or specific errors such as "Timeout exceeded" and "locator" issues.
/plugin marketplace add penkzhou/swiss-army-knife-plugin/plugin install swiss-army-knife@swiss-army-knife-pluginThis skill inherits all available tools. When active, it can use any tool Claude has access to.
本 skill 提供端到端测试 bugfix 的完整工作流知识,包括错误分类体系、置信度评分系统和 E2E 特有的调试技巧。
E2E 测试失败主要分为以下类型(按频率排序):
症状:元素等待超时、操作超时
识别特征:
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();
常见原因:
症状:找不到元素、选择器匹配多个元素
识别特征:
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() - 用户可见症状:期望值与实际值不匹配
识别特征:
expect(...).toHave*Expected: vs 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();
症状:API 请求失败、网络拦截问题
识别特征:
Route handler 错误net::ERR_*request failed解决策略:正确配置网络拦截
// 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);
症状:页面导航失败、URL 不匹配
识别特征:
page.goto: ErrorERR_NAME_NOT_RESOLVEDnavigation timeout解决策略:正确处理导航
// 等待导航完成
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/);
症状:浏览器启动失败、测试环境问题
识别特征:
browser.launch 失败Target closedcontext 错误解决策略:检查环境配置
// 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',
},
});
| 分数 | 级别 | 行为 |
|---|---|---|
| 80+ | 高 | 自动执行 |
| 60-79 | 中 | 标记验证后继续 |
| 40-59 | 低 | 暂停询问用户 |
| <40 | 不确定 | 停止收集信息 |
置信度 = 证据质量(40%) + 模式匹配(30%) + 上下文完整性(20%) + 可复现性(10%)
证据质量:
模式匹配:
上下文完整性:
可复现性:
# 运行测试并收集 trace
npx playwright test --trace on
# 查看 trace
npx playwright show-trace trace.zip
# 启动 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',
}
// 重试不稳定的测试
test.describe.configure({ retries: 2 });
// 或在配置中设置
export default defineConfig({
retries: process.env.CI ? 2 : 0,
});
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');
});
// 只实现让测试通过的最小功能
// 不要优化,不要添加额外功能
// 改善测试结构
// 提取 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.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),使用以下关键词搜索:
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.