Back to list
gr8monk3ys

webapp-testing

by gr8monk3ys

An AI content generation tool that to automatically create blog posts and books, made for writers.

51🍴 23📅 Jan 22, 2026

SKILL.md


name: webapp-testing description: Use this skill for browser automation, E2E testing, visual regression testing, and web application testing with Playwright or similar tools.

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

  1. Use data-testid - Stable selectors that survive refactoring
  2. Test user flows - Not implementation details
  3. Isolate tests - Each test should be independent
  4. Mock external services - Don't depend on third parties
  5. Run in CI - Automated testing on every PR

Score

Total Score

70/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

+5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon