webapp-testing
Use this skill for browser automation, E2E testing, visual regression testing, and web application testing with Playwright or similar tools.
Installation
Suivez les instructions du dépôt pour votre agent. Consultez les fichiers et dépendances avant l’installation.
Voir la source ↗GuidesInstructions
Web Application Testing Skill
You have expertise in end-to-end testing, browser automation, and web application quality assurance.
When to Use
This skill activates for:
- Writing Playwright/Puppeteer tests
- E2E testing strategies
- Visual regression testing
- Cross-browser testing
- Accessibility testing automation
- Performance testing with browsers
Playwright Test Patterns
Basic Test Structure
// 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();
});
});
Page Object Model
// 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');
});
API Mocking
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' }] }),
});
});
Visual Regression Testing
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');
});
Accessibility Testing
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([]);
});
Configuration
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'],
['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,
},
});
Test Utilities
Waiting Strategies
// 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;
});
Authentication State
// Save auth state
await page.context().storageState({ path: 'auth.json' });
// Reuse auth state
test.use({ storageState: 'auth.json' });
Best Practices
- Use data-testid - Stable selectors that survive refactoring
- Test user flows - Not implementation details
- Isolate tests - Each test should be independent
- Mock external services - Don't depend on third parties
- Run in CI - Automated testing on every PR
Exemples
Consultez les exemples du dépôt source.
Voir la source ↗Historique source
- Date d’indexation
- 2026-01-20
- Mise à jour de l’index
- 2026-01-26
- Branch
- main
Les dates concernent ce répertoire. Consultez le dépôt pour l’historique du code.
GitHub ↗chat Comments (0)
Sign in to join the discussion and leave a comment.
No comments yet. Be the first to share your thoughts!