Refactor Playwright tests to use cached authentication state for improved performance
- Removed redundant `loginViaUI` calls across E2E tests, leveraging cached storage state for faster test execution. - Enhanced Playwright configuration to include a `setup` project for pre-caching admin and regular user authentication states. - Added new `auth.setup.ts` to handle initial authentication and save storage states to `.auth` directory. - Increased local worker count to 16 (CI unchanged) to optimize parallel execution. - Updated `.gitignore` to exclude authentication state files.
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -27,6 +27,10 @@ coverage
|
|||||||
# nyc test coverage
|
# nyc test coverage
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
|
||||||
|
# Playwright authentication state (contains test auth tokens)
|
||||||
|
frontend/e2e/.auth/
|
||||||
|
**/playwright/.auth/
|
||||||
|
|
||||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
.grunt
|
.grunt
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ test.describe('Admin Access Control', () => {
|
|||||||
test('regular user should not see admin link in header', async ({ page }) => {
|
test('regular user should not see admin link in header', async ({ page }) => {
|
||||||
// Set up mocks for regular user (not superuser)
|
// Set up mocks for regular user (not superuser)
|
||||||
await setupAuthenticatedMocks(page);
|
await setupAuthenticatedMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
|
|
||||||
// Navigate to authenticated page to test authenticated header (not homepage)
|
// Navigate to authenticated page to test authenticated header (not homepage)
|
||||||
await page.goto('/settings');
|
await page.goto('/settings');
|
||||||
@@ -31,7 +31,7 @@ test.describe('Admin Access Control', () => {
|
|||||||
}) => {
|
}) => {
|
||||||
// Set up mocks for regular user
|
// Set up mocks for regular user
|
||||||
await setupAuthenticatedMocks(page);
|
await setupAuthenticatedMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
|
|
||||||
// Try to access admin page directly
|
// Try to access admin page directly
|
||||||
await page.goto('/admin');
|
await page.goto('/admin');
|
||||||
@@ -42,9 +42,10 @@ test.describe('Admin Access Control', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('superuser should see admin link in header', async ({ page }) => {
|
test('superuser should see admin link in header', async ({ page }) => {
|
||||||
// Set up mocks for superuser
|
// Auth state already loaded from setup (admin.json storage state)
|
||||||
|
// Set up API route mocks for superuser
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Note: loginViaUI removed - auth already cached in storage state!
|
||||||
|
|
||||||
// Navigate to settings page to ensure user state is loaded
|
// Navigate to settings page to ensure user state is loaded
|
||||||
// (AuthGuard fetches user on protected pages)
|
// (AuthGuard fetches user on protected pages)
|
||||||
@@ -65,7 +66,7 @@ test.describe('Admin Access Control', () => {
|
|||||||
}) => {
|
}) => {
|
||||||
// Set up mocks for superuser
|
// Set up mocks for superuser
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
|
|
||||||
// Navigate to admin page
|
// Navigate to admin page
|
||||||
await page.goto('/admin');
|
await page.goto('/admin');
|
||||||
@@ -79,7 +80,7 @@ test.describe('Admin Access Control', () => {
|
|||||||
test.describe('Admin Dashboard', () => {
|
test.describe('Admin Dashboard', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin');
|
await page.goto('/admin');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -134,7 +135,7 @@ test.describe('Admin Dashboard', () => {
|
|||||||
test.describe('Admin Navigation', () => {
|
test.describe('Admin Navigation', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin');
|
await page.goto('/admin');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -240,7 +241,7 @@ test.describe('Admin Navigation', () => {
|
|||||||
test.describe('Admin Breadcrumbs', () => {
|
test.describe('Admin Breadcrumbs', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should show single breadcrumb on dashboard', async ({ page }) => {
|
test('should show single breadcrumb on dashboard', async ({ page }) => {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { setupSuperuserMocks, loginViaUI } from './helpers/auth';
|
|||||||
test.describe('Admin Dashboard - Page Load', () => {
|
test.describe('Admin Dashboard - Page Load', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin');
|
await page.goto('/admin');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ test.describe('Admin Dashboard - Page Load', () => {
|
|||||||
test.describe('Admin Dashboard - Statistics Cards', () => {
|
test.describe('Admin Dashboard - Statistics Cards', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin');
|
await page.goto('/admin');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ test.describe('Admin Dashboard - Statistics Cards', () => {
|
|||||||
test.describe('Admin Dashboard - Quick Actions', () => {
|
test.describe('Admin Dashboard - Quick Actions', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin');
|
await page.goto('/admin');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ test.describe('Admin Dashboard - Quick Actions', () => {
|
|||||||
test.describe('Admin Dashboard - Analytics Charts', () => {
|
test.describe('Admin Dashboard - Analytics Charts', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin');
|
await page.goto('/admin');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ test.describe('Admin Dashboard - Analytics Charts', () => {
|
|||||||
test.describe('Admin Dashboard - Accessibility', () => {
|
test.describe('Admin Dashboard - Accessibility', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin');
|
await page.goto('/admin');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { setupSuperuserMocks, loginViaUI } from './helpers/auth';
|
|||||||
test.describe('Admin Organization Members - Navigation from Organizations List', () => {
|
test.describe('Admin Organization Members - Navigation from Organizations List', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/organizations');
|
await page.goto('/admin/organizations');
|
||||||
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
||||||
});
|
});
|
||||||
@@ -49,7 +49,7 @@ test.describe('Admin Organization Members - Navigation from Organizations List',
|
|||||||
test.describe('Admin Organization Members - Page Structure', () => {
|
test.describe('Admin Organization Members - Page Structure', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/organizations');
|
await page.goto('/admin/organizations');
|
||||||
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ test.describe('Admin Organization Members - Page Structure', () => {
|
|||||||
test.describe('Admin Organization Members - AddMemberDialog E2E Tests', () => {
|
test.describe('Admin Organization Members - AddMemberDialog E2E Tests', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/organizations');
|
await page.goto('/admin/organizations');
|
||||||
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { setupSuperuserMocks, loginViaUI } from './helpers/auth';
|
|||||||
test.describe('Admin Organization Management - Page Load', () => {
|
test.describe('Admin Organization Management - Page Load', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/organizations');
|
await page.goto('/admin/organizations');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ test.describe('Admin Organization Management - Page Load', () => {
|
|||||||
test.describe('Admin Organization Management - Organization List Table', () => {
|
test.describe('Admin Organization Management - Organization List Table', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/organizations');
|
await page.goto('/admin/organizations');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ test.describe('Admin Organization Management - Organization List Table', () => {
|
|||||||
test.describe('Admin Organization Management - Pagination', () => {
|
test.describe('Admin Organization Management - Pagination', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/organizations');
|
await page.goto('/admin/organizations');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ test.describe('Admin Organization Management - Pagination', () => {
|
|||||||
test.describe('Admin Organization Management - Create Organization Button', () => {
|
test.describe('Admin Organization Management - Create Organization Button', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/organizations');
|
await page.goto('/admin/organizations');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ test.describe('Admin Organization Management - Create Organization Button', () =
|
|||||||
test.describe('Admin Organization Management - Action Menu', () => {
|
test.describe('Admin Organization Management - Action Menu', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/organizations');
|
await page.goto('/admin/organizations');
|
||||||
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
||||||
});
|
});
|
||||||
@@ -245,7 +245,7 @@ test.describe('Admin Organization Management - Action Menu', () => {
|
|||||||
test.describe('Admin Organization Management - Edit Organization Dialog', () => {
|
test.describe('Admin Organization Management - Edit Organization Dialog', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/organizations');
|
await page.goto('/admin/organizations');
|
||||||
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
||||||
});
|
});
|
||||||
@@ -294,7 +294,7 @@ test.describe('Admin Organization Management - Edit Organization Dialog', () =>
|
|||||||
test.describe('Admin Organization Management - Member Count Interaction', () => {
|
test.describe('Admin Organization Management - Member Count Interaction', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/organizations');
|
await page.goto('/admin/organizations');
|
||||||
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
||||||
});
|
});
|
||||||
@@ -318,7 +318,7 @@ test.describe('Admin Organization Management - Member Count Interaction', () =>
|
|||||||
test.describe('Admin Organization Management - Accessibility', () => {
|
test.describe('Admin Organization Management - Accessibility', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/organizations');
|
await page.goto('/admin/organizations');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { setupSuperuserMocks, loginViaUI } from './helpers/auth';
|
|||||||
test.describe('Admin User Management - Page Load', () => {
|
test.describe('Admin User Management - Page Load', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/users');
|
await page.goto('/admin/users');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ test.describe('Admin User Management - Page Load', () => {
|
|||||||
test.describe('Admin User Management - User List Table', () => {
|
test.describe('Admin User Management - User List Table', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/users');
|
await page.goto('/admin/users');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ test.describe('Admin User Management - User List Table', () => {
|
|||||||
test.describe('Admin User Management - Search and Filters', () => {
|
test.describe('Admin User Management - Search and Filters', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/users');
|
await page.goto('/admin/users');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -222,7 +222,7 @@ test.describe('Admin User Management - Search and Filters', () => {
|
|||||||
test.describe('Admin User Management - Pagination', () => {
|
test.describe('Admin User Management - Pagination', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/users');
|
await page.goto('/admin/users');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -240,7 +240,7 @@ test.describe('Admin User Management - Pagination', () => {
|
|||||||
test.describe('Admin User Management - Row Selection', () => {
|
test.describe('Admin User Management - Row Selection', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/users');
|
await page.goto('/admin/users');
|
||||||
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
||||||
});
|
});
|
||||||
@@ -303,7 +303,7 @@ test.describe('Admin User Management - Row Selection', () => {
|
|||||||
test.describe('Admin User Management - Create User Dialog', () => {
|
test.describe('Admin User Management - Create User Dialog', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/users');
|
await page.goto('/admin/users');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -427,7 +427,7 @@ test.describe('Admin User Management - Create User Dialog', () => {
|
|||||||
test.describe('Admin User Management - Action Menu', () => {
|
test.describe('Admin User Management - Action Menu', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/users');
|
await page.goto('/admin/users');
|
||||||
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
||||||
});
|
});
|
||||||
@@ -480,7 +480,7 @@ test.describe('Admin User Management - Action Menu', () => {
|
|||||||
test.describe('Admin User Management - Edit User Dialog', () => {
|
test.describe('Admin User Management - Edit User Dialog', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/users');
|
await page.goto('/admin/users');
|
||||||
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
||||||
});
|
});
|
||||||
@@ -541,7 +541,7 @@ test.describe('Admin User Management - Edit User Dialog', () => {
|
|||||||
test.describe('Admin User Management - Bulk Actions', () => {
|
test.describe('Admin User Management - Bulk Actions', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/users');
|
await page.goto('/admin/users');
|
||||||
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
await page.waitForSelector('table tbody tr', { timeout: 10000 });
|
||||||
});
|
});
|
||||||
@@ -611,7 +611,7 @@ test.describe('Admin User Management - Bulk Actions', () => {
|
|||||||
test.describe('Admin User Management - Accessibility', () => {
|
test.describe('Admin User Management - Accessibility', () => {
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await setupSuperuserMocks(page);
|
await setupSuperuserMocks(page);
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
await page.goto('/admin/users');
|
await page.goto('/admin/users');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
70
frontend/e2e/auth.setup.ts
Normal file
70
frontend/e2e/auth.setup.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* Authentication Setup for Playwright Tests
|
||||||
|
*
|
||||||
|
* This file sets up authenticated browser states that can be reused across tests.
|
||||||
|
* Instead of logging in via UI for every test (5-7s overhead), we login once per
|
||||||
|
* worker and save the storage state (cookies, localStorage) to disk.
|
||||||
|
*
|
||||||
|
* Performance Impact:
|
||||||
|
* - Before: 133 tests × 5-7s login = ~700s overhead
|
||||||
|
* - After: 2 logins (once per role) × 5s = ~10s overhead
|
||||||
|
* - Savings: ~690s (~11 minutes) per test run
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { test as setup, expect } from '@playwright/test';
|
||||||
|
import path from 'path';
|
||||||
|
import { setupAuthenticatedMocks, setupSuperuserMocks, loginViaUI } from './helpers/auth';
|
||||||
|
|
||||||
|
// Use absolute paths to ensure correct file location
|
||||||
|
const ADMIN_STORAGE_STATE = path.join(__dirname, '.auth', 'admin.json');
|
||||||
|
const USER_STORAGE_STATE = path.join(__dirname, '.auth', 'user.json');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup: Authenticate as admin/superuser
|
||||||
|
* This runs ONCE before all admin tests
|
||||||
|
*/
|
||||||
|
setup('authenticate as admin', async ({ page }) => {
|
||||||
|
// Set up API mocks for superuser
|
||||||
|
await setupSuperuserMocks(page);
|
||||||
|
|
||||||
|
// Login via UI (one time only)
|
||||||
|
await loginViaUI(page);
|
||||||
|
|
||||||
|
// Verify we're actually logged in
|
||||||
|
await page.goto('/settings');
|
||||||
|
await page.waitForSelector('h1:has-text("Settings")', { timeout: 10000 });
|
||||||
|
|
||||||
|
// Verify admin access
|
||||||
|
const adminLink = page.locator('header nav').getByRole('link', { name: 'Admin', exact: true });
|
||||||
|
await expect(adminLink).toBeVisible();
|
||||||
|
|
||||||
|
// Save authenticated state to file
|
||||||
|
await page.context().storageState({ path: ADMIN_STORAGE_STATE });
|
||||||
|
|
||||||
|
console.log('✅ Admin authentication state saved to:', ADMIN_STORAGE_STATE);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup: Authenticate as regular user
|
||||||
|
* This runs ONCE before all user tests
|
||||||
|
*/
|
||||||
|
setup('authenticate as regular user', async ({ page }) => {
|
||||||
|
// Set up API mocks for regular user
|
||||||
|
await setupAuthenticatedMocks(page);
|
||||||
|
|
||||||
|
// Login via UI (one time only)
|
||||||
|
await loginViaUI(page);
|
||||||
|
|
||||||
|
// Verify we're actually logged in
|
||||||
|
await page.goto('/settings');
|
||||||
|
await page.waitForSelector('h1:has-text("Settings")', { timeout: 10000 });
|
||||||
|
|
||||||
|
// Verify NOT admin (regular user)
|
||||||
|
const adminLink = page.locator('header nav').getByRole('link', { name: 'Admin', exact: true });
|
||||||
|
await expect(adminLink).not.toBeVisible();
|
||||||
|
|
||||||
|
// Save authenticated state to file
|
||||||
|
await page.context().storageState({ path: USER_STORAGE_STATE });
|
||||||
|
|
||||||
|
console.log('✅ Regular user authentication state saved to:', USER_STORAGE_STATE);
|
||||||
|
});
|
||||||
@@ -12,11 +12,12 @@ test.describe('Settings Navigation', () => {
|
|||||||
await setupAuthenticatedMocks(page);
|
await setupAuthenticatedMocks(page);
|
||||||
|
|
||||||
// Login via UI to establish authenticated session
|
// Login via UI to establish authenticated session
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should navigate from home to settings profile', async ({ page }) => {
|
test('should navigate from home to settings profile', async ({ page }) => {
|
||||||
// From home page
|
// Start at home page (auth already cached in storage state)
|
||||||
|
await page.goto('/');
|
||||||
await expect(page).toHaveURL('/');
|
await expect(page).toHaveURL('/');
|
||||||
|
|
||||||
// Navigate to settings/profile
|
// Navigate to settings/profile
|
||||||
@@ -30,7 +31,8 @@ test.describe('Settings Navigation', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should navigate from home to settings password', async ({ page }) => {
|
test('should navigate from home to settings password', async ({ page }) => {
|
||||||
// From home page
|
// Start at home page (auth already cached in storage state)
|
||||||
|
await page.goto('/');
|
||||||
await expect(page).toHaveURL('/');
|
await expect(page).toHaveURL('/');
|
||||||
|
|
||||||
// Navigate to settings/password
|
// Navigate to settings/password
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ test.describe('Password Change', () => {
|
|||||||
await setupAuthenticatedMocks(page);
|
await setupAuthenticatedMocks(page);
|
||||||
|
|
||||||
// Login via UI to establish authenticated session
|
// Login via UI to establish authenticated session
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
|
|
||||||
// Navigate to password page
|
// Navigate to password page
|
||||||
await page.goto('/settings/password', { waitUntil: 'networkidle' });
|
await page.goto('/settings/password', { waitUntil: 'networkidle' });
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ test.describe('Profile Settings', () => {
|
|||||||
await setupAuthenticatedMocks(page);
|
await setupAuthenticatedMocks(page);
|
||||||
|
|
||||||
// Login via UI to establish authenticated session
|
// Login via UI to establish authenticated session
|
||||||
await loginViaUI(page);
|
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||||
|
|
||||||
// Navigate to profile page
|
// Navigate to profile page
|
||||||
await page.goto('/settings/profile');
|
await page.goto('/settings/profile');
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { defineConfig, devices } from '@playwright/test';
|
import { defineConfig, devices } from '@playwright/test';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read environment variables from file.
|
* Read environment variables from file.
|
||||||
@@ -18,7 +19,7 @@ export default defineConfig({
|
|||||||
/* Retry on CI and locally to handle flaky tests */
|
/* Retry on CI and locally to handle flaky tests */
|
||||||
retries: process.env.CI ? 2 : 1,
|
retries: process.env.CI ? 2 : 1,
|
||||||
/* Use 8 workers locally (optimized for parallel execution), 1 on CI to reduce resource usage */
|
/* Use 8 workers locally (optimized for parallel execution), 1 on CI to reduce resource usage */
|
||||||
workers: process.env.CI ? 1 : 8,
|
workers: process.env.CI ? 1 : 16,
|
||||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
reporter: process.env.CI ? 'github' : 'list',
|
reporter: process.env.CI ? 'github' : 'list',
|
||||||
/* Suppress console output unless VERBOSE=true */
|
/* Suppress console output unless VERBOSE=true */
|
||||||
@@ -40,43 +41,79 @@ export default defineConfig({
|
|||||||
// video: 'retain-on-failure',
|
// video: 'retain-on-failure',
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Configure projects for major browsers */
|
/* Configure projects with authentication state caching for performance */
|
||||||
projects: [
|
projects: [
|
||||||
|
/**
|
||||||
|
* Setup Project - Runs FIRST
|
||||||
|
* Creates authenticated browser states (admin + regular user)
|
||||||
|
* Saves to e2e/.auth/*.json for reuse across tests
|
||||||
|
* Performance: Login 2 times instead of 133 times (~11min savings!)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
name: 'chromium',
|
name: 'setup',
|
||||||
|
testMatch: /auth\.setup\.ts/,
|
||||||
use: { ...devices['Desktop Chrome'] },
|
use: { ...devices['Desktop Chrome'] },
|
||||||
},
|
},
|
||||||
//
|
|
||||||
// {
|
|
||||||
// name: 'firefox',
|
|
||||||
// use: { ...devices['Desktop Firefox'] },
|
|
||||||
// },
|
|
||||||
|
|
||||||
// Disabled: WebKit has missing system dependencies on this OS
|
/**
|
||||||
// {
|
* Admin Tests - Superuser Authenticated
|
||||||
// name: 'webkit',
|
* Requires admin/superuser privileges (access to /admin routes)
|
||||||
// use: { ...devices['Desktop Safari'] },
|
* Uses cached auth state from setup project
|
||||||
// },
|
*/
|
||||||
|
{
|
||||||
|
name: 'admin tests',
|
||||||
|
testMatch: /admin-.*\.spec\.ts/,
|
||||||
|
use: {
|
||||||
|
...devices['Desktop Chrome'],
|
||||||
|
storageState: path.join(__dirname, 'e2e', '.auth', 'admin.json'), // Reuse admin auth state
|
||||||
|
},
|
||||||
|
dependencies: ['setup'], // Wait for setup to create admin.json
|
||||||
|
},
|
||||||
|
|
||||||
/* Test against mobile viewports. */
|
/**
|
||||||
// {
|
* Settings Tests - Regular User Authenticated
|
||||||
// name: 'Mobile Chrome',
|
* Requires regular user auth (access to /settings routes)
|
||||||
// use: { ...devices['Pixel 5'] },
|
* Uses cached auth state from setup project
|
||||||
// },
|
*/
|
||||||
// {
|
{
|
||||||
// name: 'Mobile Safari',
|
name: 'settings tests',
|
||||||
// use: { ...devices['iPhone 12'] },
|
testMatch: /settings-.*\.spec\.ts/,
|
||||||
// },
|
use: {
|
||||||
|
...devices['Desktop Chrome'],
|
||||||
|
storageState: path.join(__dirname, 'e2e', '.auth', 'user.json'), // Reuse user auth state
|
||||||
|
},
|
||||||
|
dependencies: ['setup'], // Wait for setup to create user.json
|
||||||
|
},
|
||||||
|
|
||||||
/* Test against branded browsers. */
|
/**
|
||||||
// {
|
* Auth Guard Tests - Tests Auth System Itself
|
||||||
// name: 'Microsoft Edge',
|
* Tests authentication flows, guards, redirects
|
||||||
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
* Needs to test both authenticated and unauthenticated states
|
||||||
// },
|
* Dependencies on setup to ensure auth system works
|
||||||
// {
|
*/
|
||||||
// name: 'Google Chrome',
|
{
|
||||||
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
name: 'auth guard tests',
|
||||||
// },
|
testMatch: /auth-guard\.spec\.ts/,
|
||||||
|
use: { ...devices['Desktop Chrome'] },
|
||||||
|
dependencies: ['setup'], // Ensure auth system is working first
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public Tests - No Authentication Required
|
||||||
|
* Tests public pages: homepage, login, register, password reset
|
||||||
|
* No dependency on setup (faster startup for these tests)
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
name: 'public tests',
|
||||||
|
testMatch: [
|
||||||
|
/homepage\.spec\.ts/,
|
||||||
|
/auth-login\.spec\.ts/,
|
||||||
|
/auth-register\.spec\.ts/,
|
||||||
|
/auth-password-reset\.spec\.ts/,
|
||||||
|
/theme-toggle\.spec\.ts/,
|
||||||
|
],
|
||||||
|
use: { ...devices['Desktop Chrome'] },
|
||||||
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
/* Run your local dev server before starting the tests */
|
/* Run your local dev server before starting the tests */
|
||||||
|
|||||||
Reference in New Issue
Block a user