Notre avis
Ce skill fournit un modèle structuré pour écrire des tests de bout en bout (E2E) avec Playwright, en utilisant le pattern Page Object et une classe de base BasePage.
Points forts
- Centralisation des interactions UI communes via BasePage (modales, toasts, formulaires)
- Gestion simplifiée des utilisateurs et du nettoyage avec TestContext
- Fixtures Playwright étendues pour une configuration reproductible
Limites
- Nécessite une familiarité préalable avec Playwright et le pattern Page Object
- Les méthodes BasePage peuvent ne pas couvrir tous les cas UI spécifiques
- Dépend d'une structure de répertoire et de conventions précises
À utiliser lorsque vous devez écrire des tests E2E fiables et maintenables pour une application web avec des patterns UI récurrents.
À éviter pour des tests unitaires ou d'intégration, ou si le projet n'utilise pas Playwright comme framework de test.
Analyse de sécurité
SûrThe skill provides documentation for writing E2E tests using Playwright and a BasePage pattern. It contains no executable code, destructive commands, or instructions that could compromise security.
Aucun point d'attention détecté
Exemples
Write an E2E test for the login flow following our BasePage pattern. Create a LoginPage extending BasePage with locators for email and password inputs, and write a test that opens the login modal, fills in credentials, submits, and asserts successful login.Create an E2E test for user signup using TestContext to create a user and verify they can sign up. Use the user factory to generate a test user with a 'pro' subscription.Write an E2E test that clicks a button to open a modal, waits for the modal to appear using BasePage.waitForModal(), fills a form inside the modal, and submits via clickModalButton().E2E Testing Skill
When writing E2E/client tests for this codebase, follow the established patterns using Playwright and the BasePage pattern.
Overview
- Framework: Playwright E2E tests (
tests/e2e/*.e2e.spec.ts) - Pattern: Enhanced Page Object Model with
BasePageclass - Test Context:
TestContext- centralized resource management - Fixtures: Extended Playwright fixtures in
tests/test-fixtures.ts
Directory Structure
tests/
├── e2e/ # End-to-end browser tests (.e2e.spec.ts)
├── pages/ # Page object models
│ └── BasePage.ts # Base class with common UI patterns
├── helpers/
│ └── TestContext.ts # User factory and cleanup
└── test-fixtures.ts # Playwright fixture extensions
BasePage Pattern
All page objects extend BasePage for common functionality:
import { BasePage } from '@tests/pages/BasePage';
export class LoginPage extends BasePage {
// Locators
get googleSignInButton(): Locator {
return this.page.getByRole('button', { name: 'Continue with Google' });
}
get emailInput(): Locator {
return this.page.getByPlaceholder(/email/i);
}
// Actions
async openLoginModal(): Promise<void> {
await this.signInButton.click();
await this.waitForModal();
}
async fillLoginForm(email: string, password: string): Promise<void> {
await this.fillField(/email/i, email);
await this.fillField(/password/i, password);
}
async submitForm(): Promise<void> {
await this.clickButton('Sign In');
}
// Assertions
async assertModalVisible(): Promise<void> {
await expect(this.modal).toBeVisible();
}
}
BasePage Available Methods
Navigation
await page.goto('/'); // Navigate to path
await page.waitForURL('/dashboard'); // Wait for URL
Modal Handling
await this.waitForModal(); // Wait for modal to appear
await this.closeModal(); // Close active modal
await this.clickModalButton('Submit'); // Click button in modal
Toast/Notifications
await this.waitForToast('Success!'); // Wait for toast with text
await this.dismissToast(); // Dismiss active toast
Loading States
await this.waitForLoadingComplete(); // Wait for page load
await this.waitForAuthLoadingComplete(); // Wait for auth load
Network Handling
await this.waitForApiResponse('/api/user'); // Wait for API response
await this.waitForApiRequest('/api/upscale'); // Wait for API request
Form Helpers
await this.fillField(/email/i, 'test@example.com');
await this.selectOption(/country/i, 'United States');
Accessibility
await this.checkBasicAccessibility(); // Basic a11y checks
await this.checkAriaLabels(); // Verify ARIA labels
Writing E2E Tests
Basic Test Structure
import { test, expect } from '@tests/e2e/fixtures';
import { LoginPage } from '@tests/pages/LoginPage';
test.describe('Authentication', () => {
test('should show login form when clicking sign in', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto('/');
await loginPage.openLoginModal();
await loginPage.assertModalVisible();
});
});
TestContext Setup
import { TestContext } from '@tests/helpers/TestContext';
let ctx: TestContext;
test.beforeAll(async () => {
ctx = new TestContext();
});
test.afterAll(async () => {
await ctx.cleanup();
});
User Factory
// Simple user
const user = await ctx.createUser();
// Fluent builder API
const user = await ctx.createUser().withSubscription('active', 'pro').withCredits(500).build();
// Preset configurations
const proUser = await ctx.userFactory.proUser(500);
const businessUser = await ctx.userFactory.businessUser(1000);
Common E2E Test Patterns
Form Submission
test('should submit login form', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto('/');
await loginPage.openLoginModal();
await loginPage.fillLoginForm('user@example.com', 'password123');
await loginPage.submitForm();
await loginPage.waitForToast('Welcome back!');
});
Navigation
test('should navigate to dashboard', async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.goto('/');
await dashboardPage.clickNavLink('Dashboard');
await dashboardPage.waitForURL('/dashboard');
});
Loading States
test('should show loading state during upload', async ({ page }) => {
const uploadPage = new UploadPage(page);
await uploadPage.goto('/upload');
await uploadPage.uploadImage(testImage);
await uploadPage.waitForLoadingComplete();
await expect(uploadPage.resultPreview).toBeVisible();
});
Network Requests
test('should handle API errors gracefully', async ({ page }) => {
const uploadPage = new UploadPage(page);
// Intercept and mock API response
await page.route('**/api/upscale', route =>
route.fulfill({ status: 500, body: '{"error":"Server error"}' })
);
await uploadPage.goto('/upload');
await uploadPage.uploadImage(testImage);
await uploadPage.waitForToast('Something went wrong');
});
Keyboard Navigation
test('should handle keyboard navigation properly', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto('/');
await loginPage.openLoginModal();
await page.keyboard.press('Tab');
await expect(loginPage.emailInput).toBeFocused();
await page.keyboard.press('Tab');
await expect(loginPage.passwordInput).toBeFocused();
});
Accessibility Testing
Always include accessibility checks in E2E tests:
test('should have proper accessibility attributes', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto('/');
await loginPage.openLoginModal();
// Check basic accessibility
await loginPage.checkBasicAccessibility();
// Check ARIA labels
await loginPage.checkAriaLabels();
});
Manual Accessibility Checks
test('should be keyboard accessible', async ({ page }) => {
const page = new BasePage(page);
await page.goto('/');
// Test Tab navigation
await page.keyboard.press('Tab');
// Verify focus indicator visible
});
test('should have proper ARIA labels', async ({ page }) => {
await page.goto('/');
const button = page.getByRole('button', { name: /submit/i });
await expect(button).toHaveAttribute('aria-label', 'Submit form');
});
Mobile Testing
The Playwright config includes mobile device projects. Tests automatically run on:
- Desktop (chromium)
- Mobile iPhone (iPhone 12)
- Mobile Android (Pixel 5)
- Tablet (iPad)
Write mobile-specific tests:
test.describe('Mobile Navigation', () => {
test('should show hamburger menu on mobile', async ({ page }) => {
// This test runs on mobile devices
const nav = new Navigation(page);
await nav.goto('/');
await expect(nav.hamburgerMenu).toBeVisible();
await nav.openMobileMenu();
await expect(nav.mobileMenuItems).toBeVisible();
});
});
Common Pitfalls to Avoid
1. Fragile Selectors
Bad: Text-based selectors break easily
await page.click('Submit');
Good: Role-based selectors
await page.getByRole('button', { name: 'Submit' }).click();
Good: Test IDs (most stable)
await page.getByTestId('submit-button').click();
2. Hardcoded Waits
Bad: Fixed timeouts are flaky
await page.waitForTimeout(2000);
Good: Wait for specific condition
await page.waitForURL('/dashboard');
await expect(element).toBeVisible();
await this.waitForLoadingComplete();
3. Not Cleaning Up
Always clean up test data:
let ctx: TestContext;
test.beforeAll(async () => {
ctx = new TestContext();
});
test.afterAll(async () => {
await ctx.cleanup(); // Don't forget this!
});
4. Skipping Accessibility
Don't skip accessibility checks:
test('should work', async ({ page }) => {
// Missing accessibility checks!
await page.goto('/');
});
test('should work with accessibility', async ({ page }) => {
await page.goto('/');
await new BasePage(page).checkBasicAccessibility();
});
5. Ignoring Mobile
Don't only test desktop - tests run on all devices:
// This runs on desktop AND mobile/tablet
test('should work on all devices', async ({ page }) => {
await page.goto('/');
// Test works on all screen sizes
});
Best Practices
Test Naming
- Use descriptive names:
"should show login form when clicking sign in" - Group by feature:
test.describe('Authentication') - Test user flows:
"should complete checkout flow"
Page Object Structure
export class FeaturePage extends BasePage {
// Locators (getters for lazy evaluation)
get element(): Locator {
return this.page.locator('...');
}
// Actions (async methods)
async doSomething(): Promise<void> {}
// Assertions (async methods with 'assert' prefix)
async assertSomething(): Promise<void> {
await expect(this.element).toBeVisible();
}
}
Selector Priority
- Test IDs - Most stable:
getByTestId() - Role + Name - Semantic:
getByRole('button', { name }) - Labels/Placeholders - Form inputs:
getByPlaceholder(),getByLabel() - Text - Only when necessary:
getByText() - CSS - Last resort:
locator()
Testing Commands
# Run all E2E tests
yarn test:e2e
# Run specific test file
yarn test:e2e tests/e2e/auth.e2e.spec.ts
# Playwright UI mode (interactive)
yarn test:e2e:ui
# Debug mode with inspector
yarn test:e2e:debug
# Run on specific device
yarn test:e2e --project=mobile-iphone
# Full verification suite
yarn verify
Before Finishing
- Write tests for your changes following the patterns above
- Run tests:
yarn test:e2e - Check accessibility: Include
checkBasicAccessibility()in relevant tests - Run verify:
yarn verify(required before completing any task)
Key Files to Reference
tests/pages/BasePage.ts- Base class with common UI patternstests/helpers/TestContext.ts- User factory and cleanuptests/test-fixtures.ts- Playwright fixture extensionstests/e2e/fixtures.ts- Reusable test fixturesplaywright.config.ts- Playwright configuration (devices, projects)
TDD Red-Green-Refactor
Testing
Skill qui guide Claude a travers le cycle TDD complet.
Audit d'Accessibilité Web
Testing
Réalise un audit d'accessibilité web complet selon les normes WCAG.
Générateur de Tests UAT
Testing
Génère des cas de test d'acceptation utilisateur structurés et complets.