/** * EXAMPLE TEST FILE * * This file demonstrates best practices for writing Playwright tests * Use this as a template when creating new tests */ import { test, expect } from '@playwright/test'; import { login, logout, TEST_USER } from './helpers/auth'; import { waitForLoadingToFinish, expectToastMessage, generateTestId, mockApiResponse } from './helpers/utils'; // ============================================================================ // BASIC TEST STRUCTURE // ============================================================================ test.describe('Feature Name', () => { // Use authenticated state for tests that require login test.use({ storageState: 'tests/.auth/user.json' }); // Setup that runs before each test test.beforeEach(async ({ page }) => { await page.goto('/your-page'); }); // Cleanup after each test (if needed) test.afterEach(async ({ page }) => { // Clean up test data if needed }); test('should display page correctly', async ({ page }) => { // Wait for page to load await waitForLoadingToFinish(page); // Verify page elements await expect(page.getByRole('heading', { name: /page title/i })).toBeVisible(); }); }); // ============================================================================ // AUTHENTICATION TESTS // ============================================================================ test.describe('Authentication Example', () => { test('should login manually', async ({ page }) => { // Use helper function await login(page, TEST_USER); // Verify login success await expect(page).toHaveURL(/\/app/); }); test('should logout', async ({ page }) => { // Login first await login(page, TEST_USER); // Logout await logout(page); // Verify logged out await expect(page).toHaveURL(/\/login/); }); }); // ============================================================================ // FORM INTERACTIONS // ============================================================================ test.describe('Form Submission Example', () => { test.use({ storageState: 'tests/.auth/user.json' }); test('should submit form successfully', async ({ page }) => { await page.goto('/app/form-page'); // Fill form fields await page.getByLabel(/name/i).fill('Test Name'); await page.getByLabel(/email/i).fill('test@example.com'); await page.getByLabel(/description/i).fill('Test description'); // Select from dropdown await page.getByLabel(/category/i).click(); await page.getByRole('option', { name: 'Option 1' }).click(); // Check checkbox await page.getByLabel(/agree to terms/i).check(); // Submit form await page.getByRole('button', { name: /submit/i }).click(); // Verify success await expectToastMessage(page, /success/i); await expect(page).toHaveURL(/\/success/); }); test('should show validation errors', async ({ page }) => { await page.goto('/app/form-page'); // Try to submit empty form await page.getByRole('button', { name: /submit/i }).click(); // Verify error messages await expect(page.getByText(/name.*required/i)).toBeVisible(); await expect(page.getByText(/email.*required/i)).toBeVisible(); }); }); // ============================================================================ // API MOCKING // ============================================================================ test.describe('API Mocking Example', () => { test.use({ storageState: 'tests/.auth/user.json' }); test('should handle API response', async ({ page }) => { // Mock successful API response await mockApiResponse( page, '**/api/products', { products: [ { id: 1, name: 'Product 1', price: 9.99 }, { id: 2, name: 'Product 2', price: 19.99 }, ], }, 200 ); await page.goto('/app/products'); // Verify mocked data is displayed await expect(page.getByText('Product 1')).toBeVisible(); await expect(page.getByText('Product 2')).toBeVisible(); }); test('should handle API error', async ({ page }) => { // Mock error response await mockApiResponse( page, '**/api/products', { error: 'Failed to fetch products' }, 500 ); await page.goto('/app/products'); // Verify error message is shown await expect(page.getByText(/error|failed/i)).toBeVisible(); }); }); // ============================================================================ // FILE UPLOAD // ============================================================================ test.describe('File Upload Example', () => { test.use({ storageState: 'tests/.auth/user.json' }); test('should upload file', async ({ page }) => { await page.goto('/app/upload'); // Upload file const fileInput = page.locator('input[type="file"]'); await fileInput.setInputFiles('tests/fixtures/sample-inventory.csv'); // Verify file uploaded await expect(page.getByText('sample-inventory.csv')).toBeVisible(); // Submit await page.getByRole('button', { name: /upload/i }).click(); // Verify success await expectToastMessage(page, /uploaded successfully/i); }); }); // ============================================================================ // NAVIGATION // ============================================================================ test.describe('Navigation Example', () => { test.use({ storageState: 'tests/.auth/user.json' }); test('should navigate between pages', async ({ page }) => { // Start at dashboard await page.goto('/app/dashboard'); // Click navigation link await page.getByRole('link', { name: /operations/i }).click(); // Verify navigation await expect(page).toHaveURL(/\/operations/); await expect(page.getByRole('heading', { name: /operations/i })).toBeVisible(); // Navigate back await page.goBack(); await expect(page).toHaveURL(/\/dashboard/); }); }); // ============================================================================ // MODALS AND DIALOGS // ============================================================================ test.describe('Modal Example', () => { test.use({ storageState: 'tests/.auth/user.json' }); test('should open and close modal', async ({ page }) => { await page.goto('/app/dashboard'); // Open modal await page.getByRole('button', { name: /open modal/i }).click(); // Verify modal is visible await expect(page.getByRole('dialog')).toBeVisible(); // Close modal await page.getByRole('button', { name: /close|cancel/i }).click(); // Verify modal is closed await expect(page.getByRole('dialog')).not.toBeVisible(); }); test('should submit modal form', async ({ page }) => { await page.goto('/app/dashboard'); // Open modal await page.getByRole('button', { name: /add item/i }).click(); // Fill modal form await page.getByLabel(/item name/i).fill('Test Item'); // Submit await page.getByRole('button', { name: /save/i }).click(); // Modal should close await expect(page.getByRole('dialog')).not.toBeVisible(); // Verify item added await expect(page.getByText('Test Item')).toBeVisible(); }); }); // ============================================================================ // MOBILE VIEWPORT // ============================================================================ test.describe('Mobile Viewport Example', () => { test.use({ storageState: 'tests/.auth/user.json' }); test('should work on mobile', async ({ page }) => { // Set mobile viewport await page.setViewportSize({ width: 375, height: 667 }); await page.goto('/app/dashboard'); // Verify mobile menu const mobileMenuButton = page.getByRole('button', { name: /menu|hamburger/i }); await expect(mobileMenuButton).toBeVisible(); // Open mobile menu await mobileMenuButton.click(); // Verify menu items await expect(page.getByRole('link', { name: /dashboard/i })).toBeVisible(); }); }); // ============================================================================ // WAITING AND TIMING // ============================================================================ test.describe('Waiting Example', () => { test.use({ storageState: 'tests/.auth/user.json' }); test('should wait for elements correctly', async ({ page }) => { await page.goto('/app/dashboard'); // Wait for specific element await page.waitForSelector('[data-testid="dashboard-loaded"]'); // Wait for API response const response = await page.waitForResponse((resp) => resp.url().includes('/api/dashboard') && resp.status() === 200 ); // Wait for navigation await page.getByRole('link', { name: /settings/i }).click(); await page.waitForURL(/\/settings/); // Wait for network idle (use sparingly) await page.waitForLoadState('networkidle'); }); }); // ============================================================================ // ASSERTIONS // ============================================================================ test.describe('Assertion Examples', () => { test.use({ storageState: 'tests/.auth/user.json' }); test('should demonstrate various assertions', async ({ page }) => { await page.goto('/app/dashboard'); // Element visibility await expect(page.getByText('Dashboard')).toBeVisible(); await expect(page.getByText('Hidden Text')).not.toBeVisible(); // Text content await expect(page.getByRole('heading')).toContainText('Welcome'); // URL await expect(page).toHaveURL(/\/dashboard/); // Element count await expect(page.getByRole('button')).toHaveCount(5); // Attribute await expect(page.getByRole('link', { name: 'Settings' })).toHaveAttribute('href', '/settings'); // CSS class await expect(page.getByRole('button', { name: 'Active' })).toHaveClass(/active/); // Value await expect(page.getByLabel('Search')).toHaveValue(''); }); }); // ============================================================================ // TEST DATA GENERATION // ============================================================================ test.describe('Test Data Example', () => { test.use({ storageState: 'tests/.auth/user.json' }); test('should use generated test data', async ({ page }) => { await page.goto('/app/products'); // Generate unique test data const productName = `Test Product ${generateTestId()}`; // Use in test await page.getByLabel(/product name/i).fill(productName); await page.getByRole('button', { name: /save/i }).click(); // Verify await expect(page.getByText(productName)).toBeVisible(); }); }); // ============================================================================ // KEYBOARD AND MOUSE INTERACTIONS // ============================================================================ test.describe('Interaction Examples', () => { test.use({ storageState: 'tests/.auth/user.json' }); test('should handle keyboard interactions', async ({ page }) => { await page.goto('/app/search'); const searchInput = page.getByLabel(/search/i); // Type text await searchInput.type('product name'); // Press Enter await searchInput.press('Enter'); // Use keyboard shortcuts await page.keyboard.press('Control+K'); // Open search await page.keyboard.press('Escape'); // Close modal }); test('should handle mouse interactions', async ({ page }) => { await page.goto('/app/dashboard'); const element = page.getByTestId('draggable-item'); // Hover await element.hover(); // Double click await element.dblclick(); // Right click await element.click({ button: 'right' }); }); }); // ============================================================================ // BEST PRACTICES SUMMARY // ============================================================================ /** * BEST PRACTICES: * * 1. Use semantic selectors (getByRole, getByLabel, getByText) * 2. Avoid hard-coded waits (waitForTimeout) - use auto-waiting * 3. Reuse authentication state to save time * 4. Use helpers for common operations * 5. Generate unique test data to avoid conflicts * 6. Mock APIs for faster, more reliable tests * 7. Keep tests independent and isolated * 8. Use descriptive test names * 9. Clean up test data after tests * 10. Use data-testid for complex elements */