AgentSkillsCN

Webapp Testing

Web 应用测试

SKILL.md

Webapp Testing

Toolkit for testing web applications using Playwright - frontend verification, screenshots, browser logs.

When to Use

Use this skill when:

  • Testing web applications end-to-end
  • Verifying frontend functionality
  • Taking screenshots of web pages
  • Monitoring browser logs and errors
  • Testing user flows and interactions
  • Validating responsive design
  • Testing authentication and authorization
  • Performance testing web applications

Key Concepts

Playwright Features

  • Cross-browser: Chrome, Firefox, Safari (WebKit)
  • Multi-language: JavaScript, TypeScript, Python, Java, .NET
  • Headless mode: Fast, no UI
  • Network interception: Mock and inspect network traffic
  • Screenshots/Video: Visual regression testing
  • Trace viewer: Debug test execution

Test Types

  • E2E Testing: Complete user flows
  • Integration Testing: Multiple components together
  • Visual Regression: UI appearance validation
  • API Testing: Network request/response validation
  • Accessibility Testing: WCAG compliance

Common Patterns

Page Object Model

javascript
// pages/LoginPage.js
class LoginPage {
  constructor(page) {
    this.page = page;
    this.usernameInput = page.locator('#username');
    this.passwordInput = page.locator('#password');
    this.loginButton = page.locator('button[type="submit"]');
  }

  async login(username, password) {
    await this.usernameInput.fill(username);
    await this.passwordInput.fill(password);
    await this.loginButton.click();
  }
}

module.exports = { LoginPage };

Basic Test

javascript
// tests/login.spec.js
const { test, expect } = require('@playwright/test');
const { LoginPage } = require('../pages/LoginPage');

test('user can login', async ({ page }) => {
  await page.goto('https://example.com/login');

  const loginPage = new LoginPage(page);
  await loginPage.login('user@example.com', 'password');

  await expect(page).toHaveURL('https://example.com/dashboard');
});

Screenshots

javascript
test('take screenshot', async ({ page }) => {
  await page.goto('https://example.com');

  // Full page screenshot
  await page.screenshot({ path: 'full-page.png', fullPage: true });

  // Element screenshot
  await page.locator('.hero').screenshot({ path: 'hero.png' });

  // Screenshot on failure
  test.slow();
  await page.screenshot({ path: 'failure.png' });
});

Browser Logs

javascript
test('monitor browser logs', async ({ page }) => {
  const errors = [];

  page.on('console', msg => {
    if (msg.type() === 'error') {
      errors.push(msg.text());
    }
  });

  page.on('pageerror', error => {
    errors.push(error.toString());
  });

  await page.goto('https://example.com');

  expect(errors.length).toBe(0);
});

Network Interception

Mocking API Responses

javascript
test('mock API response', async ({ page }) => {
  await page.route('**/api/users', route => {
    route.fulfill({
      status: 200,
      body: JSON.stringify([
        { id: 1, name: 'User 1' },
        { id: 2, name: 'User 2' }
      ])
    });
  });

  await page.goto('https://example.com');
  // Verify UI uses mocked data
});

Monitoring Network Traffic

javascript
test('monitor network requests', async ({ page, context }) => {
  const requests = [];

  page.on('request', request => {
    requests.push(request.url());
  });

  page.on('response', response => {
    if (response.status() >= 400) {
      console.log(`Failed request: ${response.url()}`);
    }
  });

  await page.goto('https://example.com');
});

Testing User Flows

Shopping Cart Flow

javascript
test('complete purchase flow', async ({ page }) => {
  // Navigate to product
  await page.goto('https://example.com/products/1');
  await page.click('button:has-text("Add to Cart")');

  // Verify cart
  await page.click('.cart-icon');
  await expect(page.locator('.cart-item')).toHaveCount(1);

  // Checkout
  await page.click('button:has-text("Checkout")');
  await page.fill('#email', 'test@example.com');
  await page.fill('#address', '123 Test St');
  await page.click('button:has-text("Place Order")');

  // Confirm
  await expect(page).toHaveURL(/.*order-confirmation/);
});

Responsive Design Testing

javascript
const devices = ['Desktop Chrome', 'iPhone 12', 'iPad Pro'];

for (const device of devices) {
  test(`test on ${device}`, async ({ page }) => {
    await page.goto('https://example.com');

    const viewport = page.viewportSize();
    console.log(`Testing at ${viewport.width}x${viewport.height}`);

    await expect(page.locator('.hero')).toBeVisible();
    await expect(page.locator('.mobile-menu')).toBeVisible(
      viewport.width < 768 ? true : false
    );
  });
}

Accessibility Testing

javascript
test('check accessibility', async ({ page }) => {
  await page.goto('https://example.com');

  const violations = await page.accessibility.snapshot();

  violations.forEach(violation => {
    console.log(`Accessibility violation: ${violation.id}`);
    console.log(`  Description: ${violation.description}`);
  });

  expect(violations.length).toBe(0);
});

Visual Regression Testing

javascript
test('visual regression', async ({ page }) => {
  await page.goto('https://example.com');

  // Take screenshot and compare
  await expect(page).toHaveScreenshot('homepage.png', {
    maxDiffPixels: 100,
    threshold: 0.2
  });
});

Performance Testing

javascript
test('measure performance', async ({ page }) => {
  const startTime = Date.now();

  await page.goto('https://example.com');

  const loadTime = Date.now() - startTime;
  console.log(`Page load time: ${loadTime}ms`);

  const metrics = await page.evaluate(() => {
    const navigation = performance.getEntriesByType('navigation')[0];
    return {
      domContentLoaded: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,
      loadComplete: navigation.loadEventEnd - navigation.loadEventStart
    };
  });

  console.log('Metrics:', metrics);
});

Configuration

playwright.config.js

javascript
module.exports = {
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: 'html',
  use: {
    baseURL: 'https://example.com',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
    headless: true
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } }
  ]
};

Best Practices

Test Design

  • Use descriptive test names
  • Test one behavior per test
  • Use page object model for maintainability
  • Avoid hard-coded waits (use waitForSelector instead)
  • Isolate tests (no dependencies between tests)

Maintenance

  • Keep tests simple and focused
  • Use data-driven tests for similar scenarios
  • Regularly update selectors
  • Run tests in CI/CD pipeline
  • Review flaky tests and fix underlying issues

File Patterns

Look for:

  • **/tests/**/*.{js,ts,py}
  • **/e2e/**/*.{js,ts,py}
  • **/playwright/**/*.{js,ts,py}
  • **/screenshots/**/*.{png,jpg}

Keywords

Playwright, webapp testing, E2E testing, frontend testing, screenshot, browser logs, visual regression, network interception, accessibility testing, cross-browser testing, headless testing, page object model