一键导入
e2e-testing
Playwright E2E testing patterns, Page Object Model, configuration, CI/CD integration, artifact management, and flaky test strategies.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Playwright E2E testing patterns, Page Object Model, configuration, CI/CD integration, artifact management, and flaky test strategies.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Instinct-based learning system that observes sessions via hooks, creates atomic instincts with confidence scoring, and evolves them into skills/commands/agents. v2.1 adds project-scoped instincts to prevent cross-project contamination.
See, Understand, Act on video and audio. See- ingest from local files, URLs, RTSP/live feeds, or live record desktop; return realtime context and playable stream links. Understand- extract frames, build visual/semantic/temporal indexes, and search moments with timestamps and auto-clips. Act- transcode and normalize (codec, fps, resolution, aspect ratio), perform timeline edits (subtitles, text/image overlays, branding, audio overlays, dubbing, translation), generate media assets (image, audio, video), and create real time alerts for events from live streams or desktop capture.
Regression testing strategies for AI-assisted development. Sandbox-mode API testing without database dependencies, automated bug-check workflows, and patterns to catch AI blind spots where the same model writes and reviews code.
REST API design patterns including resource naming, status codes, pagination, filtering, error responses, versioning, and rate limiting for production APIs.
Patterns and architectures for autonomous Claude Code loops — from simple sequential pipelines to RFC-driven multi-agent DAG systems.
Backend architecture patterns, API design, database optimization, and server-side best practices for Node.js, Express, and Next.js API routes.
| name | e2e-testing |
| description | Playwright E2E testing patterns, Page Object Model, configuration, CI/CD integration, artifact management, and flaky test strategies. |
| origin | ECC |
Comprehensive Playwright patterns for building stable, fast, and maintainable E2E test suites.
tests/
├── e2e/
│ ├── auth/
│ │ ├── login.spec.ts
│ │ ├── logout.spec.ts
│ │ └── register.spec.ts
│ ├── features/
│ │ ├── browse.spec.ts
│ │ ├── search.spec.ts
│ │ └── create.spec.ts
│ └── api/
│ └── endpoints.spec.ts
├── fixtures/
│ ├── auth.ts
│ └── data.ts
└── playwright.config.ts
import { Page, Locator } from "@playwright/test";
export class ItemsPage {
readonly page: Page;
readonly searchInput: Locator;
readonly itemCards: Locator;
readonly createButton: Locator;
constructor(page: Page) {
this.page = page;
this.searchInput = page.locator('[data-testid="search-input"]');
this.itemCards = page.locator('[data-testid="item-card"]');
this.createButton = page.locator('[data-testid="create-btn"]');
}
async goto() {
await this.page.goto("/items");
await this.page.waitForLoadState("networkidle");
}
async search(query: string) {
await this.searchInput.fill(query);
await this.page.waitForResponse((resp) => resp.url().includes("/api/search"));
await this.page.waitForLoadState("networkidle");
}
async getItemCount() {
return await this.itemCards.count();
}
}
import { test, expect } from "@playwright/test";
import { ItemsPage } from "../../pages/ItemsPage";
test.describe("Item Search", () => {
let itemsPage: ItemsPage;
test.beforeEach(async ({ page }) => {
itemsPage = new ItemsPage(page);
await itemsPage.goto();
});
test("should search by keyword", async ({ page }) => {
await itemsPage.search("test");
const count = await itemsPage.getItemCount();
expect(count).toBeGreaterThan(0);
await expect(itemsPage.itemCards.first()).toContainText(/test/i);
await page.screenshot({ path: "artifacts/search-results.png" });
});
test("should handle no results", async ({ page }) => {
await itemsPage.search("xyznonexistent123");
await expect(page.locator('[data-testid="no-results"]')).toBeVisible();
expect(await itemsPage.getItemCount()).toBe(0);
});
});
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", { outputFolder: "playwright-report" }],
["junit", { outputFile: "playwright-results.xml" }],
["json", { outputFile: "playwright-results.json" }],
],
use: {
baseURL: process.env.BASE_URL || "http://localhost:3000",
trace: "on-first-retry",
screenshot: "only-on-failure",
video: "retain-on-failure",
actionTimeout: 10000,
navigationTimeout: 30000,
},
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"] } },
],
webServer: {
command: "npm run dev",
url: "http://localhost:3000",
reuseExistingServer: !process.env.CI,
timeout: 120000,
},
});
test("flaky: complex search", async ({ page }) => {
test.fixme(true, "Flaky - Issue #123");
// test code...
});
test("conditional skip", async ({ page }) => {
test.skip(process.env.CI, "Flaky in CI - Issue #123");
// test code...
});
npx playwright test tests/search.spec.ts --repeat-each=10
npx playwright test tests/search.spec.ts --retries=3
Race conditions:
// Bad: assumes element is ready
await page.click('[data-testid="button"]');
// Good: auto-wait locator
await page.locator('[data-testid="button"]').click();
Network timing:
// Bad: arbitrary timeout
await page.waitForTimeout(5000);
// Good: wait for specific condition
await page.waitForResponse((resp) => resp.url().includes("/api/data"));
Animation timing:
// Bad: click during animation
await page.click('[data-testid="menu-item"]');
// Good: wait for stability
await page.locator('[data-testid="menu-item"]').waitFor({ state: "visible" });
await page.waitForLoadState("networkidle");
await page.locator('[data-testid="menu-item"]').click();
await page.screenshot({ path: "artifacts/after-login.png" });
await page.screenshot({ path: "artifacts/full-page.png", fullPage: true });
await page.locator('[data-testid="chart"]').screenshot({ path: "artifacts/chart.png" });
await browser.startTracing(page, {
path: "artifacts/trace.json",
screenshots: true,
snapshots: true,
});
// ... test actions ...
await browser.stopTracing();
// In playwright.config.ts
use: {
video: 'retain-on-failure',
videosPath: 'artifacts/videos/'
}
# .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
- run: npm ci
- run: npx playwright install --with-deps
- run: npx playwright test
env:
BASE_URL: ${{ vars.STAGING_URL }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
# E2E Test Report
**Date:** YYYY-MM-DD HH:MM
**Duration:** Xm Ys
**Status:** PASSING / FAILING
## Summary
- Total: X | Passed: Y (Z%) | Failed: A | Flaky: B | Skipped: C
## Failed Tests
### test-name
**File:** `tests/e2e/feature.spec.ts:45`
**Error:** Expected element to be visible
**Screenshot:** artifacts/failed.png
**Recommended Fix:** [description]
## Artifacts
- HTML Report: playwright-report/index.html
- Screenshots: artifacts/\*.png
- Videos: artifacts/videos/\*.webm
- Traces: artifacts/\*.zip
test("wallet connection", async ({ page, context }) => {
// Mock wallet provider
await context.addInitScript(() => {
window.ethereum = {
isMetaMask: true,
request: async ({ method }) => {
if (method === "eth_requestAccounts") return ["0x1234567890123456789012345678901234567890"];
if (method === "eth_chainId") return "0x1";
},
};
});
await page.goto("/");
await page.locator('[data-testid="connect-wallet"]').click();
await expect(page.locator('[data-testid="wallet-address"]')).toContainText("0x1234");
});
test("trade execution", async ({ page }) => {
// Skip on production — real money
test.skip(process.env.NODE_ENV === "production", "Skip on production");
await page.goto("/markets/test-market");
await page.locator('[data-testid="position-yes"]').click();
await page.locator('[data-testid="trade-amount"]').fill("1.0");
// Verify preview
const preview = page.locator('[data-testid="trade-preview"]');
await expect(preview).toContainText("1.0");
// Confirm and wait for blockchain
await page.locator('[data-testid="confirm-trade"]').click();
await page.waitForResponse((resp) => resp.url().includes("/api/trade") && resp.status() === 200, {
timeout: 30000,
});
await expect(page.locator('[data-testid="trade-success"]')).toBeVisible();
});