ワンクリックで
e2e-testing
Playwright E2E 测试模式:page object + 稳定选择器 + CI 集成。用户说'写 E2E / 端到端测试 / Playwright / 跑完整用户流程'时激活。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Playwright E2E 测试模式:page object + 稳定选择器 + CI 集成。用户说'写 E2E / 端到端测试 / Playwright / 跑完整用户流程'时激活。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Claude 用 codex CLI 做差异化对抗审查 + Layer 2 二审 + auto-fix 全链路。**v2.8.0 起流式 streaming**:codex 思考过程实时事件流(onEvent 回调每个 thread.started / item.completed / turn.completed),idle-timeout(90s 没新事件才算卡死)替代固定 totalTimeout。runCodexAudit 是 async,要 await。三层架构:Layer 1 codex (OpenAI) 流式产 finding → Layer 2 finding-validator subagent (Claude fresh) 独立复现 → Layer 3 通过自动 Edit 修不询问用户。触发:/plan 完成 / /implement 大改动 / /review 3 路并行 / /pr 4 路 PR 预检。codex 是不同模型 = 不同盲区,跟 Claude 互补不替代。**全自动 — 用户从不需要手动管 codex**。
Claude 自动接管用户级跨项目敏感配置(personal API key / 个人 PAT / git 全局身份 / 跨项目 SSH host)。触发:用户提到跨项目通用 secret、或 project-vault 检测到条目应提升到用户级、或用户问'跨项目共用 secret 放哪'。写 ~/.claude/USER_VAULT.md,hook 自动 sync 到 .user-env.{sh,ps1} + git --global + ~/.ssh/config。与 project-vault 分工:本 skill 跨项目,project-vault 单项目。
Claude 主动沉淀用户跨项目偏好到 ~/.claude/CLAUDE.md,post-hook 自动 commit+push 到 dotfiles repo 跨设备同步。触发:用户说'记下这个偏好 / 沉淀到 CLAUDE.md / 把这个习惯加进去'、用户连续多次重复同样反馈('又忘了')、或做了跨项目通用决策('以后所有项目都用 zod')。仅追加不覆盖。与 continuous-learning-v2 分工:本 skill 主动显式沉淀偏好,learning-v2 被动观察行为。
Claude 自动维护 docs/SCHEMA.md。触发:用户提到表结构('加 users 表,字段 email/password')、写改 ORM 模型(Prisma/SQLAlchemy/TypeORM)、写改 migration SQL、或问'数据库表结构在哪'。不存在自动建(从 ORM 抽骨架),存在直接 Edit 加表/字段;业务含义缺失填 '_TODO_' 占位。与 architecture-decision-records 分工:本 skill 记结构现状,ADR 记选型决策(如选 Postgres)。
Claude 自动维护开发实时流水日志 docs/CHANGELOG-DEV.md,记**正在发生**的需求/进度/坑/下一步。触发:用户讲新需求或改主意(◇)、Claude 完成 ≥30 行或跨文件改动(✓)、遇 blocker(⚠)、决定下一步(→)。与 git log(已落盘代码)/ ADR(终态决策)/ SCHEMA(结构)/ mistakes(bug 根因)分工——本 skill 记进行中状态。每条 ≤5 行倒序。
Claude 自动接管项目级敏感配置(API key / DB 密码 / 部署 IP / SSH / token)。触发:用户在对话里说出任何 secret 或 IP、或代码里硬编 secret、或问 '.env 怎么放 / token 存哪'。写 .claude/PROJECT_VAULT.md,hook 自动 sync 到 .env.local + .env.example + ~/.ssh/config + SECRETS-INDEX.md,强制 .gitignore。与 user-vault 分工:本 skill 单项目,跨项目用 user-vault。
| name | e2e-testing |
| description | Playwright E2E 测试模式:page object + 稳定选择器 + CI 集成。用户说'写 E2E / 端到端测试 / Playwright / 跑完整用户流程'时激活。 |
用户需要写/跑端到端测试时激活。默认用 Playwright(跨 Chromium / Firefox / WebKit)。
关键词:
用户问"怎么测这个页面"且场景涉及多步骤 UI 交互时,主动提议 E2E。
data-testid > role > text,不要用 CSS class(样式一改就挂)expect(locator).toBeVisible() 而非 page.waitForTimeout(2000)test.beforeEach 登录/重置状态,不依赖上一个测试tests/e2e/
├── pages/
│ ├── login.page.ts ← Page Object
│ ├── dashboard.page.ts
│ └── checkout.page.ts
├── fixtures/
│ └── test-users.ts
├── specs/
│ ├── auth.spec.ts ← 登录/登出/注册
│ ├── checkout.spec.ts ← 下单核心流程
│ └── dashboard.spec.ts
└── playwright.config.ts
// tests/e2e/pages/login.page.ts
import { Page, expect } from '@playwright/test';
export class LoginPage {
constructor(private page: Page) {}
async goto() {
await this.page.goto('/login');
}
async login(email: string, password: string) {
await this.page.getByTestId('email-input').fill(email);
await this.page.getByTestId('password-input').fill(password);
await this.page.getByTestId('submit-btn').click();
}
async expectLoggedIn() {
await expect(this.page.getByTestId('user-avatar')).toBeVisible();
}
async expectError(message: string) {
await expect(this.page.getByRole('alert')).toContainText(message);
}
}
// tests/e2e/specs/auth.spec.ts
import { test } from '@playwright/test';
import { LoginPage } from '../pages/login.page';
import { testUsers } from '../fixtures/test-users';
test.describe('登录流程', () => {
let loginPage: LoginPage;
test.beforeEach(async ({ page }) => {
loginPage = new LoginPage(page);
await loginPage.goto();
});
test('有效凭证登录成功', async () => {
await loginPage.login(testUsers.valid.email, testUsers.valid.password);
await loginPage.expectLoggedIn();
});
test('无效密码显示错误', async () => {
await loginPage.login(testUsers.valid.email, 'wrong-password');
await loginPage.expectError('密码错误');
});
});
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/e2e/specs',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [['html'], ['list']],
use: {
baseURL: process.env.BASE_URL || '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: 'mobile', use: { ...devices['Pixel 5'] } },
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});
# .github/workflows/e2e.yml
name: E2E Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: npm }
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npm run test:e2e
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/
retention-days: 7
page.waitForTimeout(N)(除非真的等动画 ≤200ms)—— 用 expect().toBeVisible() 替代.btn-primary 样式一改就挂)不要一上来就写 10 条 spec。
// 在关键页面稳定后加截图锁定
test('dashboard 视觉回归', async ({ page }) => {
await page.goto('/dashboard');
await expect(page).toHaveScreenshot('dashboard.png', {
maxDiffPixels: 100,
threshold: 0.2,
});
});
首次跑会生成 baseline,CI 自动比对。慎用:需要稳定的设计系统,否则噪音多。
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('首页 WCAG 2.1 AA', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa'])
.analyze();
expect(results.violations).toEqual([]);
});
需要 npm i -D @axe-core/playwright。每个核心页面至少跑一次。
test('首页 LCP < 2.5s', async ({ page }) => {
await page.goto('/');
const lcp = await page.evaluate(() => new Promise<number>(r => {
new PerformanceObserver(list => {
const entries = list.getEntries();
r((entries[entries.length - 1] as any).startTime);
}).observe({ type: 'largest-contentful-paint', buffered: true });
}));
expect(lcp).toBeLessThan(2500);
});
Lighthouse CI(@lhci/cli)比手写 PerformanceObserver 更完整,推荐用在主要 landing page。