一键导入
webapp-testing
Use this skill for browser automation, E2E testing, visual regression testing, and web application testing with Playwright or similar tools.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use this skill for browser automation, E2E testing, visual regression testing, and web application testing with Playwright or similar tools.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use this skill when creating, testing, or securing API endpoints. Activates for REST API routes, authentication, rate limiting, and API documentation tasks.
Use this skill for code review, testing, refactoring, and optimization. Activates for security review, test generation, performance analysis, and code cleanup tasks.
Use this skill for database schema design, migrations, queries, and optimization. Activates for Supabase, Prisma, Drizzle, and PostgreSQL tasks.
Use this skill for deployment, CI/CD, Docker, and infrastructure tasks. Activates for GitHub Actions, Vercel, containerization, and environment management.
Use this skill when creating UI components, pages, or frontend features. Activates for React components, Next.js pages, styling, state management, and accessibility tasks.
Use this skill when creating Model Context Protocol (MCP) servers, adding tools to extend Claude's capabilities, or integrating external APIs as MCP tools.
| name | webapp-testing |
| description | Use this skill for browser automation, E2E testing, visual regression testing, and web application testing with Playwright or similar tools. |
You have expertise in end-to-end testing, browser automation, and web application quality assurance.
This skill activates for:
// tests/example.spec.ts
import { test, expect } from '@playwright/test';
test.describe('Feature: User Authentication', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/login');
});
test('should login with valid credentials', async ({ page }) => {
await page.fill('[data-testid="email"]', 'user@example.com');
await page.fill('[data-testid="password"]', 'password123');
await page.click('[data-testid="submit"]');
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('[data-testid="welcome"]')).toContainText('Welcome');
});
test('should show error for invalid credentials', async ({ page }) => {
await page.fill('[data-testid="email"]', 'wrong@example.com');
await page.fill('[data-testid="password"]', 'wrongpassword');
await page.click('[data-testid="submit"]');
await expect(page.locator('[data-testid="error"]')).toBeVisible();
});
});
// pages/LoginPage.ts
import { Page, Locator } from '@playwright/test';
export class LoginPage {
readonly page: Page;
readonly emailInput: Locator;
readonly passwordInput: Locator;
readonly submitButton: Locator;
readonly errorMessage: Locator;
constructor(page: Page) {
this.page = page;
this.emailInput = page.locator('[data-testid="email"]');
this.passwordInput = page.locator('[data-testid="password"]');
this.submitButton = page.locator('[data-testid="submit"]');
this.errorMessage = page.locator('[data-testid="error"]');
}
async goto() {
await this.page.goto('/login');
}
async login(email: string, password: string) {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.submitButton.click();
}
}
// Usage in test
test('login test', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('user@example.com', 'password');
});
test('should handle API errors gracefully', async ({ page }) => {
// Mock API to return error
await page.route('**/api/users', route => {
route.fulfill({
status: 500,
body: JSON.stringify({ error: 'Server error' }),
});
});
await page.goto('/users');
await expect(page.locator('[data-testid="error-state"]')).toBeVisible();
});
// Mock successful response
await page.route('**/api/data', route => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ items: [{ id: 1, name: 'Test' }] }),
});
});
test('visual regression: homepage', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveScreenshot('homepage.png', {
fullPage: true,
animations: 'disabled',
});
});
test('visual regression: component states', async ({ page }) => {
await page.goto('/components');
// Default state
await expect(page.locator('.button')).toHaveScreenshot('button-default.png');
// Hover state
await page.locator('.button').hover();
await expect(page.locator('.button')).toHaveScreenshot('button-hover.png');
});
import AxeBuilder from '@axe-core/playwright';
test('should pass accessibility audit', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa'])
.analyze();
expect(results.violations).toEqual([]);
});
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'],
['json', { outputFile: 'test-results.json' }],
],
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', use: { ...devices['iPhone 13'] } },
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});
// Wait for network idle
await page.waitForLoadState('networkidle');
// Wait for specific response
const response = await page.waitForResponse('**/api/data');
// Wait for element state
await page.locator('.spinner').waitFor({ state: 'hidden' });
// Custom wait
await page.waitForFunction(() => {
return document.querySelectorAll('.item').length > 0;
});
// Save auth state
await page.context().storageState({ path: 'auth.json' });
// Reuse auth state
test.use({ storageState: 'auth.json' });