Files
bakery-ia/frontend/tests/EXAMPLE_TEST.spec.ts
2025-11-14 07:46:29 +01:00

412 lines
12 KiB
TypeScript

/**
* 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
*/