web-testing
SKILL.md
Web Application Testing & Debugging
Comprehensive toolkit for testing and debugging web applications using Playwright automation and Chrome DevTools.
Skill Paths
- Workspace skills:
.github/skills/ - Global skills:
C:/Users/LOQ/.agents/skills/
Activation Conditions
Playwright Testing:
- Testing frontend functionality in a real browser
- Verifying UI behavior and interactions
- Debugging web application issues
- Capturing screenshots for documentation or debugging
- Inspecting browser console logs
- Validating form submissions and user flows
- Checking responsive design across viewports
Chrome DevTools Debugging:
- Interacting with web pages through automated controls
- Taking screenshots, and analyzing network traffic
- Navigating pages, clicking elements, filling forms, handling dialogs
- Emulating network conditions or devices
- Running JavaScript in page context, capturing console messages
- Performance profiling and identifying bottlenecks
Part 1: Playwright Testing
Core Capabilities
Browser Automation
import { test, expect, Page, Browser } from '@playwright/test';
// Navigate to URLs
await page.goto('https://example.com');
await page.waitForLoadState('networkidle');
// Click buttons and links
await page.click('#submit-button');
await page.click('text=Continue');
// Fill form fields
await page.fill('#email', 'user@example.com');
await page.fill('#password', 'securepassword');
// Select dropdowns
await page.selectOption('#country', 'United States');
// Handle dialogs and alerts
page.on('dialog', async dialog => {
await dialog.accept(); // or dialog.dismiss()
});
User Flow Testing
test('complete checkout flow', async ({ page }) => {
// Add to cart
await page.goto('/products');
await page.click('text=Add to Cart');
// Navigate to cart
await page.goto('/cart');
// Verify item in cart
await expect(page.locator('.cart-item')).toHaveCount(1);
// Checkout
await page.click('text=Checkout');
await page.fill('#email', 'test@example.com');
await page.fill('#shipping-address', '123 Main St');
await page.click('text=Place Order');
// Verify success
await expect(page.locator('.success-message')).toBeVisible();
});
Form Validation Testing
test('form validation', async ({ page }) => {
await page.goto('/register');
// Submit empty form - should show errors
await page.click('text=Submit');
// Check error messages
await expect(page.locator('.error-email')).toBeVisible();
await expect(page.locator('.error-password')).toBeVisible();
// Fill valid data
await page.fill('#email', 'valid@example.com');
await page.fill('#password', 'securePass123!');
await page.click('text=Submit');
// Verify no errors and success
await expect(page.locator('.error-email')).not.toBeVisible();
await expect(page.locator('.success-message')).toBeVisible();
});
Responsive Testing
test.describe('Responsive Design', () => {
const viewports = [
{ name: 'Mobile', width: 375, height: 667 },
{ name: 'Tablet', width: 768, height: 1024 },
{ name: 'Desktop', width: 1280, height: 720 },
];
viewports.forEach(({ name, width, height }) => {
test(`layout on ${name} (${width}x${height})`, async ({ page }) => {
await page.setViewportSize({ width, height });
await page.goto('/');
// Check navigation is visible and accessible
const nav = page.locator('nav');
await expect(nav).toBeVisible();
// On mobile, check hamburger menu is present
if (width < 768) {
await expect(page.locator('.mobile-menu-toggle')).toBeVisible();
} else {
await expect(page.locator('.mobile-menu-toggle')).not.toBeVisible();
}
// Screenshot for comparison
await page.screenshot({
path: `screenshots/${name.toLowerCase()}-layout.png`,
fullPage: true,
});
});
});
});
Console & Network Inspection
test('console errors and warnings', async ({ page, context }) => {
const errors: string[] = [];
// Listen for console errors
page.on('console', msg => {
if (msg.type() === 'error') {
errors.push(msg.text());
}
});
await page.goto('/');
// Assertions
expect(errors).toEqual([]);
});
test('network requests monitoring', async ({ page }) => {
const requests: string[] = [];
page.on('request', request => {
requests.push(request.url());
});
await page.goto('/');
// Verify API calls
const apiRequests = requests.filter(url => url.includes('/api/'));
expect(apiRequests.length).toBeGreaterThan(0);
// Verify no 404s
const failedResponses: any[] = [];
page.on('response', response => {
if (response.status() === 404) {
failedResponses.push(response.url());
}
});
await page.click('a[href="/about"]');
expect(failedResponses).toEqual([]);
});
Accessibility Testing
test('basic accessibility checks', async ({ page }) => {
// Check heading hierarchy
const headings = await page.locator('h1, h2, h3').all();
expect(headings[0]).toHaveText('Main Heading'); // h1 should be first
// Check images have alt text
const imagesWithoutAlt = await page.locator('img:not([alt])').count();
expect(imagesWithoutAlt).toBe(0);
// Check form labels
const inputs = await page.locator('input, select, textarea').all();
for (const input of inputs) {
const hasLabel = await input.evaluate(el => {
return el.labels.length > 0 || el.getAttribute('aria-label');
});
expect(hasLabel).toBeTruthy();
}
// Check keyboard navigation
await page.keyboard.press('Tab');
const focusedElement = await page.evaluate(() => document.activeElement?.tagName);
expect(['INPUT', 'BUTTON', 'A']).toContain(focusedElement);
});
Visual Regression Testing
import { compareScreenshots } from './visual-utils';
test('visual regression - home page', async ({ page }) => {
await page.goto('/');
// Wait for all images and fonts to load
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
// Take screenshot
const screenshot = await page.screenshot({
fullPage: true,
});
// Compare with baseline
const diff = await compareScreenshots(screenshot, 'baseline/home.png');
expect(diff.pixelDifference).toBeLessThan(100); // Threshold
});
Part 2: Chrome DevTools Integration
Tool Categories
Navigation & Page Management
// Open new page
await chrome.newPage();
// Navigate to URL
await chrome.navigatePage('https://example.com');
// Reload current page
await chrome.navigatePage({ action: 'reload' });
// Navigate history
await chrome.navigatePage({ action: 'back' });
await chrome.navigatePage({ action: 'forward' });
// List all open pages
const pages = await chrome.listPages();
await chrome.selectPage(pages[0].id);
// Close specific page
await chrome.closePage('page-id-here');
// Wait for text to appear
await chrome.waitFor('Welcome to the site');
Input & Interaction
// Take snapshot to get element IDs
const snapshot = await chrome.takeSnapshot();
// Find element by uid
const submitButton = snapshot.elements.find(el => el.text === 'Submit');
// Click element
await chrome.click(submitButton.uid);
// Fill single field
await chrome.fill(inputUid, 'value@example.com');
// Fill multiple fields at once
await chrome.fillForm([
{ uid: emailInputUid, value: 'test@example.com' },
{ uid: passwordInputUid, value: 'password123' },
{ uid: nameInputUid, value: 'John Doe' },
]);
// Hover over element
await chrome.hover(buttonUid);
// Press keyboard shortcuts
await chrome.pressKey('Enter');
await chrome.pressKey('Control+C');
// Drag and drop
await chrome.drag(sourceUid, targetUid);
// Handle browser dialogs
await chrome.handleDialog('accept');
await chrome.handleDialog('dismiss');
Debugging & Inspection
// Get accessibility tree (best for finding elements)
const snapshot = await chrome.takeSnapshot();
// Take visual screenshot
const screenshot = await chrome.takeScreenshot();
// List all console messages
const messages = await chrome.listConsoleMessages();
// Get messages by level
const errors = await chrome.listConsoleMessages('error');
const warnings = await chrome.listConsoleMessages('warning');
// Get specific message details
const message = await chrome.getConsoleMessage(messageId);
// Evaluate JavaScript in page context
const result = await chrome.evaluateScript('document.title');
const userInfo = await chrome.evaluateScript(`
JSON.parse(localStorage.getItem('user'))
`);
// List network requests
const requests = await chrome.listNetworkRequests();
// Get failed requests
const failedRequests = requests.filter(req =>
req.status >= 400 || req.status === 0
);
// Get specific request details
const requestDetails = await chrome.getNetworkRequest(requestId);
Emulation & Performance
// Resize viewport
await chrome.resizePage({ width: 375, height: 667 }); // Mobile
// Emulate network conditions
await chrome.emulate({
network: 'offline' // 'slow-3g', 'fast-3g', 'online'
});
// Emulate geolocation
await chrome.emulate({
geolocation: { lat: 40.7128, lon: -74.0060 }
});
// Start performance trace
await chrome.performanceStartTrace({ reload: true });
// Stop trace after navigation
await chrome.waitFor('Page loaded');
const trace = await chrome.performanceStopTrace();
// Get insights
const insights = await chrome.performanceAnalyzeInsight();
console.log('LCP:', insights.largestContentfulPaint);
console.log('CLS:', insights.cumulativeLayoutShift);
Common Debugging Patterns
Pattern A: Identifying Elements (Snapshot-First)
Always prefer snapshot over screenshot for finding elements:
// 1. Get current page structure
const snapshot = await chrome.takeSnapshot();
// 2. Find the target element by its uid
const element = snapshot.elements.find(el => el.text === 'Continue');
// 3. Use the uid for interaction
await chrome.click(element.uid);
Pattern B: Troubleshooting Errors
When a page is failing, check both console and network:
// 1. Check console messages for JavaScript errors
const errors = await chrome.listConsoleMessages('error');
console.log('JavaScript Errors:', errors);
// 2. Check network requests for failures
const requests = await chrome.listNetworkRequests();
const failed = requests.filter(r => r.status >= 400);
console.log('Failed Requests:', failed);
// 3. Check specific values via JavaScript
const apiResponse = await chrome.evaluateScript(`
window.lastApiResponse
`);
console.log('Last API Response:', apiResponse);
Pattern C: Performance Profiling
Identify why a page is slow:
// 1. Start performance trace with reload
await chrome.performanceStartTrace({ reload: true, autoStop: true });
// 2. Wait for trace to complete
const timeout = 10000;
await new Promise(resolve => setTimeout(resolve, timeout));
// 3. Get performance insights
const insights = await chrome.performanceAnalyzeInsight();
console.log('Performance Issues:', insights.issues);
console.log('LCP:', insights.largestContentfulPaint);
console.log('CLS:', insights.cumulativeLayoutShift);
console.log('FID:', insights.firstInputDelay);
// 4. Identify bottlenecks
if (insights.largestContentfulPaint > 2500) {
console.warn('LCP is slow - consider optimizing images and CSS');
}
if (insights.cumulativeLayoutShift > 0.1) {
console.warn('CLS is high - avoid layout shifts');
}
Workflow Examples
Testing Login Flow
// Snapshot-first approach
await chrome.navigatePage('https://example.com/login');
const snapshot = await chrome.takeSnapshot();
// Find and fill email input
const emailInput = snapshot.elements.find(el => el.attributes.id === 'email');
await chrome.fill(emailInput.uid, 'user@example.com');
// Find and fill password input
const passwordInput = snapshot.elements.find(el => el.attributes.type === 'password');
await chrome.fill(passwordInput.uid, 'password123');
// Find and click submit button
const submitButton = snapshot.elements.find(el => el.text === 'Sign In');
await chrome.click(submitButton.uid);
// Wait for successful login indication
await chrome.waitFor('Dashboard');
// Verify with screenshot
await chrome.takeScreenshot('login-success.png');
Debugging API Issues
// Navigate to page with API calls
await chrome.navigatePage('https://example.com/data-page');
// Clear and monitor network
const apiCalls = [];
chrome.on('networkRequest', (request) => {
if (request.url.includes('/api/')) {
apiCalls.push({
url: request.url,
method: request.method,
});
}
});
// Trigger API call
await chrome.click(submitButtonUid);
// Check what was called
console.log('API Calls:', apiCalls);
// Get detailed response from browser
const responseData = await chrome.evaluateScript(`
window.lastApiResponse
`);
console.log('Response Data:', responseData);
Part 3: Testing Best Practices
Test Structure
test.describe('User Authentication', () => {
test.beforeEach(async ({ page }) => {
// Setup: login fresh each test
await page.goto('/login');
});
test('successful login with valid credentials', async ({ page }) => {
await test.step('Enter credentials', async () => {
await page.fill('#email', 'valid@example.com');
await page.fill('#password', 'correct-password');
});
await test.step('Submit form', async () => {
await page.click('text=Login');
});
await test.step('Verify redirected to dashboard', async () => {
await expect(page).toHaveURL('/dashboard');
});
});
test('shows error for invalid credentials', async ({ page }) => {
await page.fill('#email', 'invalid@example.com');
await page.fill('#password', 'wrong-password');
await page.click('text=Login');
await expect(page.locator('.error-message')).toBeVisible();
await expect(page).toHaveURL('/login');
});
});
Page Object Model
// pages/LoginPage.ts
export class LoginPage {
constructor(private page: Page) {}
async goto() {
await this.page.goto('/login');
}
async login(email: string, password: string) {
await this.page.fill('#email', email);
await this.page.fill('#password', password);
await this.page.click('text=Login');
}
async getErrorMessage() {
return await this.page.locator('.error-message').textContent();
}
assertVisible() {
expect(this.page.locator('h1')).toHaveText('Login');
}
}
// Tests
test('login flow', async ({ page }) => {
const loginPage = new LoginPage(page);
loginPage.goto();
await loginPage.login('user@example.com', 'password');
await expect(page).toHaveURL('/dashboard');
});
Parallel Testing
// playwright.config.ts
export default defineConfig({
workers: process.env.CI ? 2 : 4, // Parallel execution
projects: [
{
name: 'chromium',
use: { browserName: 'chromium' },
},
{
name: 'firefox',
use: { browserName: 'firefox' },
},
{
name: 'webkit',
use: { browserName: 'webkit' },
},
],
});
Part 4: Debugging Toolset
Quick Reference
| Task | Playwright | Chrome DevTools |
|---|---|---|
| Browser Automation | Yes | Yes |
| Page Navigation | page.goto() |
navigate_page() |
| Click Elements | page.click() |
click(uid) |
| Fill Forms | page.fill() |
fill(uid, value) |
| Screenshots | page.screenshot() |
take_screenshot() |
| Console Logs | page.on('console') |
list_console_messages() |
| Network Requests | page.on('request') |
list_network_requests() |
| JavaScript Eval | page.evaluate() |
evaluate_script() |
| Viewport Resize | page.setViewportSize() |
resize_page() |
| Performance | Trace API | performance_* tools |
| Device Emulation | deviceDescriptor |
emulate() |
Common Debugging Commands
# Run Playwright tests
npx playwright test
# Run tests with UI (helps debugging)
npx playwright test --ui
# Run tests in headed mode (watch browser)
npx playwright test --headed
# Debug specific test
npx playwright test tests/login.spec.ts --debug
# Generate codegen from browser actions
npx playwright codegen https://example.com
Testing Checklist
Functionality
- All user flows work end-to-end
- Form validation tested for success and failure cases
- Error handling verified
- Edge cases covered
Responsive Design
- Tested on mobile (375px, 414px)
- Tested on tablet (768px, 1024px)
- Tested on desktop (1280px, 1440px)
- Navigation accessible on mobile
- No horizontal scrollbars
Cross-Browser
- Tested in Chrome
- Tested in Firefox
- Tested in Safari/WebKit
- Tested in Edge
Accessibility
- All interactive elements keyboard accessible
- Focus states visible
- ARIA labels on icon-only buttons
- Form fields have labels
- Images have alt text (except decorative)
- Touch targets ≥ 44x44px on mobile
Performance
- Page load time < 3 seconds
- LCP < 2.5 seconds
- CLS < 0.1
- No layout shifts on interaction
- Images optimized (WebP, lazy load)
Error Handling
- Console errors logged and reviewed
- Failed network requests identified
- 404s checked and fixed
- 500 errors investigated
- User-friendly error messages shown
References & Resources
Documentation
- Playwright Selectors — All selector types with decision tree and priority order
- Test Patterns — Page Object Model, fixtures, auth reuse, API mocking, and accessibility patterns
Scripts
- Test Scaffold — PowerShell Playwright test file generator for e2e, visual, and accessibility tests
Examples
- E2E Recipe App Tests — Kitchen Odyssey test suite with Page Objects and CI configuration
Related Skills
| Skill | Relationship |
|---|---|
| web-design-reviewer | Visual design review with browser tools |
| javascript-development | JS apps being tested |
| react-development | Test React components and user flows |
Weekly Installs
6
Repository
practicalswan/a…t-skillsGitHub Stars
2
First Seen
Feb 26, 2026
Security Audits
Installed on
opencode6
gemini-cli6
claude-code6
github-copilot6
amp6
cline6