feat(test): implement comprehensive Playwright test suite
- Add complete E2E test coverage for authentication flows - Implement component interaction and navigation testing - Create responsive design validation across viewports - Add theme switching and visual regression testing - Include smoke tests for critical user paths - Configure Playwright with proper test setup Test suite ensures application reliability with automated validation of user flows, accessibility, and visual consistency. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
258
reactrebuild0825/tests/auth.spec.ts
Normal file
258
reactrebuild0825/tests/auth.spec.ts
Normal file
@@ -0,0 +1,258 @@
|
||||
import { test, expect, Page } from '@playwright/test';
|
||||
import path from 'path';
|
||||
|
||||
const DEMO_ACCOUNTS = {
|
||||
admin: { email: 'admin@example.com', password: 'demo123' },
|
||||
organizer: { email: 'organizer@example.com', password: 'demo123' },
|
||||
staff: { email: 'staff@example.com', password: 'demo123' },
|
||||
};
|
||||
|
||||
async function takeScreenshot(page: Page, name: string) {
|
||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
||||
const fileName = `auth_${name}_${timestamp}.png`;
|
||||
await page.screenshot({
|
||||
path: path.join('screenshots', fileName),
|
||||
fullPage: true
|
||||
});
|
||||
return fileName;
|
||||
}
|
||||
|
||||
async function clearAuthStorage(page: Page) {
|
||||
await page.evaluate(() => {
|
||||
localStorage.removeItem('bct_auth_user');
|
||||
localStorage.removeItem('bct_auth_remember');
|
||||
});
|
||||
}
|
||||
|
||||
test.describe('Authentication Flows', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Clear any existing auth state
|
||||
await clearAuthStorage(page);
|
||||
await page.goto('/');
|
||||
});
|
||||
|
||||
test('should redirect to login when accessing protected route', async ({ page }) => {
|
||||
await page.goto('/dashboard');
|
||||
|
||||
// Should redirect to login page
|
||||
await expect(page).toHaveURL('/login');
|
||||
await expect(page.locator('h1')).toContainText('Sign In');
|
||||
|
||||
await takeScreenshot(page, 'protected-route-redirect');
|
||||
});
|
||||
|
||||
test('should login successfully with valid admin credentials', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
await takeScreenshot(page, 'login-page-initial');
|
||||
|
||||
// Fill in admin credentials
|
||||
await page.fill('[data-testid="email-input"]', DEMO_ACCOUNTS.admin.email);
|
||||
await page.fill('[data-testid="password-input"]', DEMO_ACCOUNTS.admin.password);
|
||||
|
||||
await takeScreenshot(page, 'login-form-filled');
|
||||
|
||||
// Submit login form
|
||||
await page.click('[data-testid="login-button"]');
|
||||
|
||||
// Wait for loading state
|
||||
await expect(page.locator('[data-testid="login-button"]')).toBeDisabled();
|
||||
await takeScreenshot(page, 'login-loading-state');
|
||||
|
||||
// Should redirect to dashboard
|
||||
await expect(page).toHaveURL('/dashboard', { timeout: 10000 });
|
||||
|
||||
// Verify user is logged in
|
||||
await expect(page.locator('[data-testid="user-menu"]')).toBeVisible();
|
||||
await expect(page.locator('[data-testid="user-name"]')).toContainText('Sarah Admin');
|
||||
|
||||
await takeScreenshot(page, 'dashboard-logged-in-admin');
|
||||
});
|
||||
|
||||
test('should login successfully with organizer credentials', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
|
||||
await page.fill('[data-testid="email-input"]', DEMO_ACCOUNTS.organizer.email);
|
||||
await page.fill('[data-testid="password-input"]', DEMO_ACCOUNTS.organizer.password);
|
||||
await page.click('[data-testid="login-button"]');
|
||||
|
||||
await expect(page).toHaveURL('/dashboard', { timeout: 10000 });
|
||||
await expect(page.locator('[data-testid="user-name"]')).toContainText('John Organizer');
|
||||
|
||||
await takeScreenshot(page, 'dashboard-logged-in-organizer');
|
||||
});
|
||||
|
||||
test('should login successfully with staff credentials', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
|
||||
await page.fill('[data-testid="email-input"]', DEMO_ACCOUNTS.staff.email);
|
||||
await page.fill('[data-testid="password-input"]', DEMO_ACCOUNTS.staff.password);
|
||||
await page.click('[data-testid="login-button"]');
|
||||
|
||||
await expect(page).toHaveURL('/dashboard', { timeout: 10000 });
|
||||
await expect(page.locator('[data-testid="user-name"]')).toContainText('Emma Staff');
|
||||
|
||||
await takeScreenshot(page, 'dashboard-logged-in-staff');
|
||||
});
|
||||
|
||||
test('should show error for invalid credentials', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
|
||||
await page.fill('[data-testid="email-input"]', 'invalid@example.com');
|
||||
await page.fill('[data-testid="password-input"]', 'wrongpassword');
|
||||
await page.click('[data-testid="login-button"]');
|
||||
|
||||
// Should show error message
|
||||
await expect(page.locator('[data-testid="error-message"]')).toBeVisible();
|
||||
await expect(page.locator('[data-testid="error-message"]')).toContainText('Invalid email or password');
|
||||
|
||||
// Should remain on login page
|
||||
await expect(page).toHaveURL('/login');
|
||||
|
||||
await takeScreenshot(page, 'login-error-state');
|
||||
});
|
||||
|
||||
test('should show error for short password', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
|
||||
await page.fill('[data-testid="email-input"]', DEMO_ACCOUNTS.admin.email);
|
||||
await page.fill('[data-testid="password-input"]', '12'); // Too short
|
||||
await page.click('[data-testid="login-button"]');
|
||||
|
||||
await expect(page.locator('[data-testid="error-message"]')).toBeVisible();
|
||||
await expect(page.locator('[data-testid="error-message"]')).toContainText('Invalid email or password');
|
||||
|
||||
await takeScreenshot(page, 'login-short-password-error');
|
||||
});
|
||||
|
||||
test('should toggle password visibility', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
|
||||
const passwordInput = page.locator('[data-testid="password-input"]');
|
||||
const toggleButton = page.locator('[data-testid="password-toggle"]');
|
||||
|
||||
// Password should be hidden initially
|
||||
await expect(passwordInput).toHaveAttribute('type', 'password');
|
||||
|
||||
await page.fill('[data-testid="password-input"]', 'testpassword');
|
||||
await takeScreenshot(page, 'password-hidden');
|
||||
|
||||
// Click toggle to show password
|
||||
await toggleButton.click();
|
||||
await expect(passwordInput).toHaveAttribute('type', 'text');
|
||||
await takeScreenshot(page, 'password-visible');
|
||||
|
||||
// Click toggle to hide password again
|
||||
await toggleButton.click();
|
||||
await expect(passwordInput).toHaveAttribute('type', 'password');
|
||||
});
|
||||
|
||||
test('should remember login when remember me is checked', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
|
||||
await page.fill('[data-testid="email-input"]', DEMO_ACCOUNTS.admin.email);
|
||||
await page.fill('[data-testid="password-input"]', DEMO_ACCOUNTS.admin.password);
|
||||
|
||||
// Check remember me
|
||||
await page.check('[data-testid="remember-me"]');
|
||||
await takeScreenshot(page, 'remember-me-checked');
|
||||
|
||||
await page.click('[data-testid="login-button"]');
|
||||
await expect(page).toHaveURL('/dashboard', { timeout: 10000 });
|
||||
|
||||
// Verify localStorage has auth data
|
||||
const authData = await page.evaluate(() => localStorage.getItem('bct_auth_user'));
|
||||
const rememberMe = await page.evaluate(() => localStorage.getItem('bct_auth_remember'));
|
||||
|
||||
expect(authData).toBeTruthy();
|
||||
expect(rememberMe).toBe('true');
|
||||
|
||||
// Refresh page - should stay logged in
|
||||
await page.reload();
|
||||
await expect(page).toHaveURL('/dashboard');
|
||||
await expect(page.locator('[data-testid="user-name"]')).toContainText('Sarah Admin');
|
||||
|
||||
await takeScreenshot(page, 'persistent-login-after-refresh');
|
||||
});
|
||||
|
||||
test('should logout successfully', async ({ page }) => {
|
||||
// First login
|
||||
await page.goto('/login');
|
||||
await page.fill('[data-testid="email-input"]', DEMO_ACCOUNTS.admin.email);
|
||||
await page.fill('[data-testid="password-input"]', DEMO_ACCOUNTS.admin.password);
|
||||
await page.click('[data-testid="login-button"]');
|
||||
await expect(page).toHaveURL('/dashboard', { timeout: 10000 });
|
||||
|
||||
// Open user menu and logout
|
||||
await page.click('[data-testid="user-menu"]');
|
||||
await takeScreenshot(page, 'user-menu-open');
|
||||
|
||||
await page.click('[data-testid="logout-button"]');
|
||||
|
||||
// Should redirect to login
|
||||
await expect(page).toHaveURL('/login', { timeout: 10000 });
|
||||
|
||||
// Verify localStorage is cleared
|
||||
const authData = await page.evaluate(() => localStorage.getItem('bct_auth_user'));
|
||||
const rememberMe = await page.evaluate(() => localStorage.getItem('bct_auth_remember'));
|
||||
|
||||
expect(authData).toBeNull();
|
||||
expect(rememberMe).toBeNull();
|
||||
|
||||
await takeScreenshot(page, 'logout-complete');
|
||||
});
|
||||
|
||||
test('should redirect back to intended route after login', async ({ page }) => {
|
||||
// Try to access events page directly
|
||||
await page.goto('/events');
|
||||
|
||||
// Should redirect to login with return URL
|
||||
await expect(page).toHaveURL('/login');
|
||||
|
||||
// Login
|
||||
await page.fill('[data-testid="email-input"]', DEMO_ACCOUNTS.admin.email);
|
||||
await page.fill('[data-testid="password-input"]', DEMO_ACCOUNTS.admin.password);
|
||||
await page.click('[data-testid="login-button"]');
|
||||
|
||||
// Should redirect back to events page
|
||||
await expect(page).toHaveURL('/events', { timeout: 10000 });
|
||||
|
||||
await takeScreenshot(page, 'redirect-to-intended-route');
|
||||
});
|
||||
|
||||
test('should handle form validation', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
|
||||
// Try to submit empty form
|
||||
await page.click('[data-testid="login-button"]');
|
||||
|
||||
// Should show validation errors
|
||||
await expect(page.locator('[data-testid="email-error"]')).toBeVisible();
|
||||
await expect(page.locator('[data-testid="password-error"]')).toBeVisible();
|
||||
|
||||
await takeScreenshot(page, 'form-validation-errors');
|
||||
|
||||
// Fill invalid email
|
||||
await page.fill('[data-testid="email-input"]', 'invalidemail');
|
||||
await page.click('[data-testid="login-button"]');
|
||||
|
||||
await expect(page.locator('[data-testid="email-error"]')).toContainText('Invalid email');
|
||||
|
||||
await takeScreenshot(page, 'invalid-email-error');
|
||||
});
|
||||
|
||||
test('should handle network errors gracefully', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
|
||||
// Simulate network failure by blocking requests
|
||||
await page.route('**/api/**', route => route.abort());
|
||||
|
||||
await page.fill('[data-testid="email-input"]', DEMO_ACCOUNTS.admin.email);
|
||||
await page.fill('[data-testid="password-input"]', DEMO_ACCOUNTS.admin.password);
|
||||
await page.click('[data-testid="login-button"]');
|
||||
|
||||
// Should show network error
|
||||
await expect(page.locator('[data-testid="error-message"]')).toBeVisible();
|
||||
|
||||
await takeScreenshot(page, 'network-error-state');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user