Awesome-omni-skill webapp-testing
Test local web applications using Playwright for UI verification, debugging, screenshot capture, and end-to-end testing. Use when testing web apps, verifying UI behavior, or capturing screenshots of running applications.
install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/webapp-testing-mohamedgad1983" ~/.claude/skills/diegosouzapw-awesome-omni-skill-webapp-testing-153b6d && rm -rf "$T"
manifest:
skills/development/webapp-testing-mohamedgad1983/SKILL.mdsource content
Webapp Testing Skill
Purpose
Test local and deployed web applications using Playwright for comprehensive UI verification, debugging, and automated testing.
Setup
Install Playwright
npm init playwright@latest # or pnpm create playwright
Project Structure
tests/ ├── e2e/ │ ├── auth.spec.ts │ ├── dashboard.spec.ts │ └── orders.spec.ts ├── fixtures/ │ └── test-data.ts └── playwright.config.ts
Configuration
// playwright.config.ts 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', 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 14'] } }, ], webServer: { command: 'npm run dev', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI, }, });
Test Patterns
Authentication Tests
import { test, expect } from '@playwright/test'; test.describe('Authentication', () => { test('should login successfully', async ({ page }) => { await page.goto('/login'); await page.getByLabel('Email').fill('user@example.com'); await page.getByLabel('Password').fill('password123'); await page.getByRole('button', { name: 'Sign In' }).click(); await expect(page).toHaveURL('/dashboard'); await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible(); }); test('should show error for invalid credentials', async ({ page }) => { await page.goto('/login'); await page.getByLabel('Email').fill('invalid@example.com'); await page.getByLabel('Password').fill('wrongpassword'); await page.getByRole('button', { name: 'Sign In' }).click(); await expect(page.getByText('Invalid credentials')).toBeVisible(); }); test('should logout successfully', async ({ page }) => { // Login first await page.goto('/login'); await page.getByLabel('Email').fill('user@example.com'); await page.getByLabel('Password').fill('password123'); await page.getByRole('button', { name: 'Sign In' }).click(); // Logout await page.getByRole('button', { name: 'Logout' }).click(); await expect(page).toHaveURL('/login'); }); });
Dashboard Tests
test.describe('Dashboard', () => { test.beforeEach(async ({ page }) => { // Login before each test await page.goto('/login'); await page.getByLabel('Email').fill('user@example.com'); await page.getByLabel('Password').fill('password123'); await page.getByRole('button', { name: 'Sign In' }).click(); }); test('should display stats cards', async ({ page }) => { await expect(page.getByTestId('stats-card-sales')).toBeVisible(); await expect(page.getByTestId('stats-card-orders')).toBeVisible(); await expect(page.getByTestId('stats-card-customers')).toBeVisible(); }); test('should filter data by date range', async ({ page }) => { await page.getByRole('button', { name: 'Date Range' }).click(); await page.getByRole('option', { name: 'Last 7 days' }).click(); // Wait for data to reload await page.waitForResponse(resp => resp.url().includes('/api/stats')); await expect(page.getByTestId('stats-card-sales')).toContainText('$'); }); test('should navigate to orders page', async ({ page }) => { await page.getByRole('link', { name: 'Orders' }).click(); await expect(page).toHaveURL('/orders'); }); });
Form Tests
test.describe('Order Form', () => { test('should create a new order', async ({ page }) => { await page.goto('/orders/new'); // Fill form await page.getByLabel('Customer Name').fill('John Doe'); await page.getByLabel('Phone').fill('+1234567890'); await page.getByRole('combobox', { name: 'Product' }).click(); await page.getByRole('option', { name: 'Pizza Margherita' }).click(); await page.getByLabel('Quantity').fill('2'); // Submit await page.getByRole('button', { name: 'Create Order' }).click(); // Verify success await expect(page.getByText('Order created successfully')).toBeVisible(); }); test('should validate required fields', async ({ page }) => { await page.goto('/orders/new'); await page.getByRole('button', { name: 'Create Order' }).click(); await expect(page.getByText('Customer name is required')).toBeVisible(); }); });
Table Tests
test.describe('Orders Table', () => { test('should sort by column', async ({ page }) => { await page.goto('/orders'); // Click on Date column to sort await page.getByRole('columnheader', { name: 'Date' }).click(); // Verify sort indicator await expect(page.getByRole('columnheader', { name: 'Date' })) .toHaveAttribute('aria-sort', 'ascending'); }); test('should paginate results', async ({ page }) => { await page.goto('/orders'); await page.getByRole('button', { name: 'Next' }).click(); await expect(page.getByText('Page 2')).toBeVisible(); }); test('should search orders', async ({ page }) => { await page.goto('/orders'); await page.getByPlaceholder('Search orders...').fill('ORD-001'); await page.keyboard.press('Enter'); await expect(page.getByText('ORD-001')).toBeVisible(); }); });
Screenshot Capture
test('capture dashboard screenshot', async ({ page }) => { await page.goto('/dashboard'); await page.waitForLoadState('networkidle'); // Full page screenshot await page.screenshot({ path: 'screenshots/dashboard-full.png', fullPage: true }); // Element screenshot await page.getByTestId('sales-chart').screenshot({ path: 'screenshots/sales-chart.png' }); });
Visual Regression
test('visual regression - dashboard', async ({ page }) => { await page.goto('/dashboard'); await expect(page).toHaveScreenshot('dashboard.png', { maxDiffPixels: 100, }); });
Mobile Testing
test.describe('Mobile', () => { test.use({ viewport: { width: 375, height: 667 } }); test('should show mobile menu', async ({ page }) => { await page.goto('/dashboard'); await page.getByRole('button', { name: 'Menu' }).click(); await expect(page.getByRole('navigation')).toBeVisible(); }); });
API Mocking
test('should handle API errors gracefully', async ({ page }) => { // Mock API to return error await page.route('**/api/orders', route => { route.fulfill({ status: 500, body: JSON.stringify({ error: 'Server error' }), }); }); await page.goto('/orders'); await expect(page.getByText('Failed to load orders')).toBeVisible(); });
Running Tests
# Run all tests npx playwright test # Run specific test file npx playwright test tests/e2e/dashboard.spec.ts # Run in headed mode (see browser) npx playwright test --headed # Run with UI mode npx playwright test --ui # Generate report npx playwright show-report
Instructions
- Identify test scenarios: What user flows need testing?
- Set up test fixtures: Create reusable login, data setup
- Write tests: Use page object pattern for maintainability
- Add screenshots: Capture visual state for debugging
- Run in CI: Add to GitHub Actions or similar
- Review report: Analyze failures and fix