Creates and maintains Playwright E2E tests
Creates and maintains Playwright E2E tests with visual regression and accessibility checks. Use it to write user flow tests, configure cross-browser testing, and run the mandatory post-change verification suite.
/plugin marketplace add IvanTorresEdge/molcajete.ai/plugin install react@Molcajete.aiCreates and maintains Playwright end-to-end tests following playwright-setup, visual-regression, and accessibility-testing skills.
MUST reference these skills for guidance:
playwright-setup skill:
visual-regression skill:
accessibility-testing skill:
post-change-verification skill:
<pkg> run format to format code
c. Run <pkg> run lint to lint code (ZERO warnings required)
d. Run <pkg> run type-check for type verification (ZERO errors required)
e. Run npx playwright test to run E2E tests
f. Document any pre-existing issues not caused by this changeimport { test, expect } from '@playwright/test';
test.describe('Home Page', () => {
test('displays welcome message', async ({ page }) => {
await page.goto('/');
await expect(page.getByRole('heading', { name: 'Welcome' })).toBeVisible();
});
test('navigates to about page', async ({ page }) => {
await page.goto('/');
await page.getByRole('link', { name: 'About' }).click();
await expect(page).toHaveURL('/about');
await expect(page.getByRole('heading', { name: 'About Us' })).toBeVisible();
});
});
// 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.getByLabel('Email');
this.passwordInput = page.getByLabel('Password');
this.submitButton = page.getByRole('button', { name: 'Sign in' });
this.errorMessage = page.getByRole('alert');
}
async goto(): Promise<void> {
await this.page.goto('/login');
}
async login(email: string, password: string): Promise<void> {
await this.emailInput.fill(email);
await this.passwordInput.fill(password);
await this.submitButton.click();
}
async expectError(message: string): Promise<void> {
await expect(this.errorMessage).toContainText(message);
}
}
// tests/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
test.describe('Login', () => {
test('logs in successfully with valid credentials', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('user@example.com', 'password123');
await expect(page).toHaveURL('/dashboard');
});
test('shows error with invalid credentials', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('user@example.com', 'wrongpassword');
await loginPage.expectError('Invalid credentials');
});
});
// fixtures/index.ts
import { test as base } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
import { DashboardPage } from '../pages/DashboardPage';
interface Pages {
loginPage: LoginPage;
dashboardPage: DashboardPage;
}
export const test = base.extend<Pages>({
loginPage: async ({ page }, use) => {
await use(new LoginPage(page));
},
dashboardPage: async ({ page }, use) => {
await use(new DashboardPage(page));
},
});
export { expect } from '@playwright/test';
// Usage
import { test, expect } from '../fixtures';
test('uses fixtures', async ({ loginPage, dashboardPage }) => {
await loginPage.goto();
await loginPage.login('user@example.com', 'password');
await dashboardPage.expectLoaded();
});
import { test, expect } from '@playwright/test';
test.describe('Visual Regression', () => {
test('home page matches snapshot', async ({ page }) => {
await page.goto('/');
// Full page screenshot
await expect(page).toHaveScreenshot('home-page.png', {
fullPage: true,
maxDiffPixelRatio: 0.01, // 1% tolerance
});
});
test('button component matches snapshot', async ({ page }) => {
await page.goto('/storybook/button');
// Component screenshot
const button = page.getByRole('button', { name: 'Primary' });
await expect(button).toHaveScreenshot('button-primary.png');
});
test('responsive layouts match snapshots', async ({ page }) => {
await page.goto('/');
// Mobile viewport
await page.setViewportSize({ width: 375, height: 667 });
await expect(page).toHaveScreenshot('home-mobile.png');
// Tablet viewport
await page.setViewportSize({ width: 768, height: 1024 });
await expect(page).toHaveScreenshot('home-tablet.png');
// Desktop viewport
await page.setViewportSize({ width: 1920, height: 1080 });
await expect(page).toHaveScreenshot('home-desktop.png');
});
});
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test.describe('Accessibility', () => {
test('home page has no accessibility violations', async ({ page }) => {
await page.goto('/');
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
test('form has proper labels', async ({ page }) => {
await page.goto('/contact');
const accessibilityScanResults = await new AxeBuilder({ page })
.include('form')
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
test('keyboard navigation works', async ({ page }) => {
await page.goto('/');
// Tab through interactive elements
await page.keyboard.press('Tab');
await expect(page.getByRole('link', { name: 'Home' })).toBeFocused();
await page.keyboard.press('Tab');
await expect(page.getByRole('link', { name: 'About' })).toBeFocused();
// Enter activates link
await page.keyboard.press('Enter');
await expect(page).toHaveURL('/about');
});
});
// 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'],
['json', { outputFile: 'test-results/results.json' }],
],
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'on-first-retry',
},
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 12'] },
},
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});
tests/
├── e2e/
│ ├── pages/ # Page Objects
│ │ ├── LoginPage.ts
│ │ └── DashboardPage.ts
│ ├── fixtures/ # Test fixtures
│ │ └── index.ts
│ ├── auth.spec.ts # Auth flow tests
│ ├── dashboard.spec.ts # Dashboard tests
│ └── visual.spec.ts # Visual regression
├── playwright.config.ts
└── package.json
# Run all tests
npx playwright test
# Run specific test file
npx playwright test auth.spec.ts
# Run with UI mode
npx playwright test --ui
# Update snapshots
npx playwright test --update-snapshots
# Show report
npx playwright show-report
You are an elite AI agent architect specializing in crafting high-performance agent configurations. Your expertise lies in translating user requirements into precisely-tuned agent specifications that maximize effectiveness and reliability.