Loading...
Loading...
Use when the user needs Playwright-based web application testing — screenshots, browser log analysis, interaction verification, visual regression, accessibility, and network mocking. Triggers: E2E test setup, visual regression testing, accessibility audit, Playwright configuration, page object model creation, CI test pipeline.
npx skill4agent add pixel-process-ug/superkit-agents webapp-testing| Flow Type | Priority | Test Depth |
|---|---|---|
| Authentication (login/logout/register) | Critical | Full happy + error paths |
| Core business workflow (purchase, submit) | Critical | Full happy + error + edge cases |
| Navigation and routing | High | All major routes |
| Search and filtering | High | Common queries + empty state |
| Settings and profile | Medium | Happy path |
| Admin/back-office | Medium | Key operations only |
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', { open: 'never' }],
['junit', { outputFile: 'test-results/junit.xml' }],
],
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-chrome', use: { ...devices['Pixel 5'] } },
{ name: 'mobile-safari', use: { ...devices['iPhone 13'] } },
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});class LoginPage {
constructor(private page: Page) {}
readonly emailInput = this.page.getByLabel('Email');
readonly passwordInput = this.page.getByLabel('Password');
readonly submitButton = this.page.getByRole('button', { name: 'Sign in' });
readonly errorMessage = this.page.getByRole('alert');
async login(email: string, password: string) {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.submitButton.click();
}
async expectError(message: string) {
await expect(this.errorMessage).toContainText(message);
}
}| Locator Type | Priority | When to Use |
|---|---|---|
| 1st choice | Any element with ARIA role (button, link, heading) |
| 2nd choice | Form fields with labels |
| 3rd choice | Fields without visible labels |
| 4th choice | Non-interactive visible text |
| Last resort | When no accessible locator works |
| CSS selector / XPath | Never | Breaks with styling changes |
test('homepage renders correctly', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveScreenshot('homepage.png', {
fullPage: true,
maxDiffPixelRatio: 0.01,
});
});test('navigation bar matches design', async ({ page }) => {
await page.goto('/');
const nav = page.getByRole('navigation');
await expect(nav).toHaveScreenshot('navbar.png');
});test('dashboard layout', async ({ page }) => {
await page.goto('/dashboard');
await expect(page).toHaveScreenshot('dashboard.png', {
mask: [
page.locator('[data-testid="timestamp"]'),
page.locator('[data-testid="user-avatar"]'),
page.locator('.chart-container'),
],
animations: 'disabled',
});
});test('no console errors on page load', async ({ page }) => {
const consoleErrors: string[] = [];
page.on('console', msg => {
if (msg.type() === 'error') consoleErrors.push(msg.text());
});
page.on('pageerror', error => {
consoleErrors.push(error.message);
});
await page.goto('/');
await page.waitForLoadState('networkidle');
expect(consoleErrors).toEqual([]);
});import AxeBuilder from '@axe-core/playwright';
test('page has no accessibility violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.exclude('.third-party-widget')
.analyze();
expect(results.violations).toEqual([]);
});test('displays users from API', async ({ page }) => {
await page.route('**/api/users', async route => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]),
});
});
await page.goto('/users');
await expect(page.getByText('Alice')).toBeVisible();
});
test('handles API errors gracefully', async ({ page }) => {
await page.route('**/api/users', route =>
route.fulfill({ status: 500, body: 'Internal Server Error' })
);
await page.goto('/users');
await expect(page.getByText('Something went wrong')).toBeVisible();
});test.describe('mobile responsive', () => {
test.use({ viewport: { width: 375, height: 667 } });
test('hamburger menu works', async ({ page }) => {
await page.goto('/');
await expect(page.getByRole('navigation')).not.toBeVisible();
await page.getByRole('button', { name: 'Menu' }).click();
await expect(page.getByRole('navigation')).toBeVisible();
});
});tests/
e2e/
auth/
login.spec.ts
register.spec.ts
checkout/
cart.spec.ts
payment.spec.ts
fixtures/
test-data.ts
auth.setup.ts
pages/
login.page.ts
dashboard.page.ts
utils/
helpers.ts| Anti-Pattern | Why It Is Wrong | Correct Approach |
|---|---|---|
| CSS selectors or XPath | Break with styling changes | Use accessible locators (role, label, text) |
| Arbitrary delays, flaky | Use |
| Testing third-party components in detail | Not your code to test | Test your integration, not their internals |
| Hardcoded test data | Breaks across environments | Use fixtures and factories |
| Tests depending on execution order | Fragile, hard to debug | Each test must be independent |
| Ignoring flaky tests | Erodes trust in test suite | Fix root cause or quarantine |
| Screenshots without masking dynamic content | Always different, always failing | Mask timestamps, avatars, charts |
| No accessibility checks | Missing critical quality gate | axe-core on every page |
| Skill | Relationship |
|---|---|
| Frontend components are tested by E2E tests |
| E2E tests are the top of the testing pyramid |
| User flow tests serve as acceptance tests |
| Performance budgets can be verified in E2E |
| Review checks that tests use accessible locators |
| Security headers and auth flows tested in E2E |
waitForTimeout