Files
gridpilot.gg/apps/website/tests/flows/admin.test.ts
Marc Mintel fb1221701d
Some checks failed
Contract Testing / contract-tests (push) Failing after 6m7s
Contract Testing / contract-snapshot (push) Failing after 4m46s
add tests
2026-01-22 11:52:42 +01:00

1268 lines
46 KiB
TypeScript

/**
* Admin Feature Flow Tests
*
* These tests verify routing, guards, navigation, cross-screen state, and user flows
* for the admin module. They run with real frontend and mocked contracts.
*
* Contracts are defined in apps/website/lib/types/generated
*
* @file apps/website/tests/flows/admin.test.ts
*/
import { test, expect } from '@playwright/test';
import { WebsiteAuthManager } from '../../../tests/shared/website/WebsiteAuthManager';
import { WebsiteRouteManager } from '../../../tests/shared/website/WebsiteRouteManager';
import { ConsoleErrorCapture } from '../../../tests/shared/website/ConsoleErrorCapture';
import { HttpDiagnostics } from '../../../tests/shared/website/HttpDiagnostics';
import { RouteContractSpec } from '../../../tests/shared/website/RouteContractSpec';
import { RouteScenarioMatrix } from '../../../tests/shared/website/RouteScenarioMatrix';
test.describe('Admin Feature Flow', () => {
test.describe('Admin Dashboard Navigation', () => {
test('should redirect to login when accessing admin routes without authentication', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
// Navigate to admin route without authentication
await page.goto(routeManager.getRoute('/admin'));
// Verify redirect to login page
await expect(page).toHaveURL(/.*\/auth\/login/);
// Check return URL parameter
const url = new URL(page.url());
expect(url.searchParams.get('returnUrl')).toBe('/admin');
});
test('should redirect to login when accessing admin users route without authentication', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
// Navigate to admin users route without authentication
await page.goto(routeManager.getRoute('/admin/users'));
// Verify redirect to login page
await expect(page).toHaveURL(/.*\/auth\/login/);
// Check return URL parameter
const url = new URL(page.url());
expect(url.searchParams.get('returnUrl')).toBe('/admin/users');
});
test('should redirect to login when accessing admin routes with invalid role', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
// Login as regular user (non-admin)
await authManager.loginAsUser();
// Navigate to admin route
await page.goto(routeManager.getRoute('/admin'));
// Verify redirect to appropriate error page or dashboard
// Regular users should be redirected away from admin routes
await expect(page).not.toHaveURL(/.*\/admin/);
});
test('should allow access to admin dashboard with valid admin role', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
// Login as admin user
await authManager.loginAsAdmin();
// Navigate to admin dashboard
await page.goto(routeManager.getRoute('/admin'));
// Verify dashboard loads successfully
await expect(page).toHaveURL(/.*\/admin/);
// Check for expected dashboard elements
await expect(page.locator('h1')).toContainText(/admin/i);
await expect(page.locator('[data-testid="admin-dashboard"]')).toBeVisible();
});
test('should navigate from admin dashboard to users management', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
// Login as admin
await authManager.loginAsAdmin();
// Navigate to admin dashboard
await page.goto(routeManager.getRoute('/admin'));
// Click users link/button
await page.locator('[data-testid="users-link"]').click();
// Verify navigation to /admin/users
await expect(page).toHaveURL(/.*\/admin\/users/);
});
test('should navigate back from users to dashboard', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
// Login as admin
await authManager.loginAsAdmin();
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Click back/dashboard link
await page.locator('[data-testid="back-to-dashboard"]').click();
// Verify navigation to /admin
await expect(page).toHaveURL(/.*\/admin/);
});
});
describe('Admin Dashboard Data Flow', () => {
test('should load and display dashboard statistics', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock AdminDashboardPageQuery response
const mockDashboardData = {
totalUsers: 150,
activeUsers: 120,
pendingUsers: 25,
suspendedUsers: 5,
totalRevenue: 12500,
recentActivity: [
{ id: '1', action: 'User created', timestamp: '2024-01-15T10:00:00Z' },
{ id: '2', action: 'User updated', timestamp: '2024-01-15T09:30:00Z' },
],
};
await routeContractSpec.mockApiCall('AdminDashboardPageQuery', mockDashboardData);
// Navigate to admin dashboard
await page.goto(routeManager.getRoute('/admin'));
// Verify stats are displayed
await expect(page.locator('[data-testid="total-users"]')).toContainText('150');
await expect(page.locator('[data-testid="active-users"]')).toContainText('120');
await expect(page.locator('[data-testid="pending-users"]')).toContainText('25');
await expect(page.locator('[data-testid="suspended-users"]')).toContainText('5');
// Check for proper data formatting (e.g., currency formatting)
await expect(page.locator('[data-testid="total-revenue"]')).toContainText('$12,500');
});
test('should handle dashboard data loading errors', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
const consoleErrorCapture = new ConsoleErrorCapture(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock AdminDashboardPageQuery to return error
await routeContractSpec.mockApiCall('AdminDashboardPageQuery', {
error: 'Internal Server Error',
status: 500,
});
// Navigate to admin dashboard
await page.goto(routeManager.getRoute('/admin'));
// Verify error banner is displayed
await expect(page.locator('[data-testid="error-banner"]')).toBeVisible();
// Check error message content
await expect(page.locator('[data-testid="error-message"]')).toContainText(/error/i);
// Verify console error was captured
const errors = consoleErrorCapture.getErrors();
expect(errors.length).toBeGreaterThan(0);
});
test('should refresh dashboard data on refresh button click', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock initial dashboard data
const mockDashboardData = {
totalUsers: 150,
activeUsers: 120,
pendingUsers: 25,
suspendedUsers: 5,
totalRevenue: 12500,
recentActivity: [],
};
await routeContractSpec.mockApiCall('AdminDashboardPageQuery', mockDashboardData);
// Navigate to admin dashboard
await page.goto(routeManager.getRoute('/admin'));
// Mock refreshed data
const refreshedData = {
...mockDashboardData,
totalUsers: 155,
activeUsers: 125,
};
await routeContractSpec.mockApiCall('AdminDashboardPageQuery', refreshedData);
// Click refresh button
await page.locator('[data-testid="refresh-button"]').click();
// Verify loading state is shown
await expect(page.locator('[data-testid="loading-spinner"]')).toBeVisible();
// Verify data is updated
await expect(page.locator('[data-testid="total-users"]')).toContainText('155');
await expect(page.locator('[data-testid="active-users"]')).toContainText('125');
});
test('should handle dashboard access denied (403/401)', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock API to return 403 error
await routeContractSpec.mockApiCall('AdminDashboardPageQuery', {
error: 'Access Denied',
status: 403,
message: 'You must have Owner or Admin role to access this resource',
});
// Navigate to admin dashboard
await page.goto(routeManager.getRoute('/admin'));
// Verify "Access Denied" error banner
await expect(page.locator('[data-testid="access-denied-banner"]')).toBeVisible();
// Check message about Owner or Admin role
await expect(page.locator('[data-testid="access-denied-message"]')).toContainText(/Owner or Admin/i);
});
});
describe('Admin Users Management Flow', () => {
test('should load and display users list', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock AdminUsersPageQuery response
const mockUsersData = {
users: [
{
id: 'user-1',
email: 'john@example.com',
roles: ['admin'],
status: 'active',
createdAt: '2024-01-15T10:00:00Z',
},
{
id: 'user-2',
email: 'jane@example.com',
roles: ['user'],
status: 'active',
createdAt: '2024-01-14T15:30:00Z',
},
{
id: 'user-3',
email: 'bob@example.com',
roles: ['user'],
status: 'suspended',
createdAt: '2024-01-10T09:00:00Z',
},
],
total: 3,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Verify users are displayed in table/list
await expect(page.locator('[data-testid="user-row-user-1"]')).toBeVisible();
await expect(page.locator('[data-testid="user-row-user-2"]')).toBeVisible();
await expect(page.locator('[data-testid="user-row-user-3"]')).toBeVisible();
// Check for expected user fields
await expect(page.locator('[data-testid="user-email-user-1"]')).toContainText('john@example.com');
await expect(page.locator('[data-testid="user-roles-user-1"]')).toContainText('admin');
await expect(page.locator('[data-testid="user-status-user-1"]')).toContainText('active');
});
test('should handle users data loading errors', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
const consoleErrorCapture = new ConsoleErrorCapture(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock AdminUsersPageQuery to return error
await routeContractSpec.mockApiCall('AdminUsersPageQuery', {
error: 'Internal Server Error',
status: 500,
});
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Verify error banner is displayed
await expect(page.locator('[data-testid="error-banner"]')).toBeVisible();
// Verify console error was captured
const errors = consoleErrorCapture.getErrors();
expect(errors.length).toBeGreaterThan(0);
});
test('should filter users by search term', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock initial users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
{ id: 'user-2', email: 'jane@example.com', roles: ['user'], status: 'active' },
],
total: 2,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Mock filtered results
const filteredData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
],
total: 1,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', filteredData);
// Enter search term in search input
await page.locator('[data-testid="search-input"]').fill('john');
// Verify URL is updated with search parameter
await expect(page).toHaveURL(/.*search=john/);
// Verify filtered results
await expect(page.locator('[data-testid="user-row-user-1"]')).toBeVisible();
await expect(page.locator('[data-testid="user-row-user-2"]')).not.toBeVisible();
});
test('should filter users by role', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock initial users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
{ id: 'user-2', email: 'jane@example.com', roles: ['user'], status: 'active' },
],
total: 2,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Mock filtered results
const filteredData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
],
total: 1,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', filteredData);
// Select role filter
await page.locator('[data-testid="role-filter"]').selectOption('admin');
// Verify URL is updated with role parameter
await expect(page).toHaveURL(/.*role=admin/);
// Verify filtered results
await expect(page.locator('[data-testid="user-row-user-1"]')).toBeVisible();
await expect(page.locator('[data-testid="user-row-user-2"]')).not.toBeVisible();
});
test('should filter users by status', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock initial users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
{ id: 'user-2', email: 'jane@example.com', roles: ['user'], status: 'suspended' },
],
total: 2,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Mock filtered results
const filteredData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
],
total: 1,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', filteredData);
// Select status filter
await page.locator('[data-testid="status-filter"]').selectOption('active');
// Verify URL is updated with status parameter
await expect(page).toHaveURL(/.*status=active/);
// Verify filtered results
await expect(page.locator('[data-testid="user-row-user-1"]')).toBeVisible();
await expect(page.locator('[data-testid="user-row-user-2"]')).not.toBeVisible();
});
test('should clear all filters', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock initial users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
{ id: 'user-2', email: 'jane@example.com', roles: ['user'], status: 'active' },
],
total: 2,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Apply search, role, and status filters
await page.locator('[data-testid="search-input"]').fill('john');
await page.locator('[data-testid="role-filter"]').selectOption('admin');
await page.locator('[data-testid="status-filter"]').selectOption('active');
// Mock cleared filters data
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Click clear filters button
await page.locator('[data-testid="clear-filters-button"]').click();
// Verify URL parameters are removed
await expect(page).not.toHaveURL(/.*search=/);
await expect(page).not.toHaveURL(/.*role=/);
await expect(page).not.toHaveURL(/.*status=/);
// Verify all users are shown again
await expect(page.locator('[data-testid="user-row-user-1"]')).toBeVisible();
await expect(page.locator('[data-testid="user-row-user-2"]')).toBeVisible();
});
test('should select individual users', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
{ id: 'user-2', email: 'jane@example.com', roles: ['user'], status: 'active' },
],
total: 2,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Click checkbox for a user
await page.locator('[data-testid="user-checkbox-user-1"]').click();
// Verify user is added to selectedUserIds
// Check that the checkbox is checked
await expect(page.locator('[data-testid="user-checkbox-user-1"]')).toBeChecked();
// Verify selection count is updated
await expect(page.locator('[data-testid="selection-count"]')).toContainText('1');
});
test('should select all users', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
{ id: 'user-2', email: 'jane@example.com', roles: ['user'], status: 'active' },
],
total: 2,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Click select all checkbox
await page.locator('[data-testid="select-all-checkbox"]').click();
// Verify all checkboxes are checked
await expect(page.locator('[data-testid="user-checkbox-user-1"]')).toBeChecked();
await expect(page.locator('[data-testid="user-checkbox-user-2"]')).toBeChecked();
// Verify selection count is updated
await expect(page.locator('[data-testid="selection-count"]')).toContainText('2');
});
test('should clear user selection', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
{ id: 'user-2', email: 'jane@example.com', roles: ['user'], status: 'active' },
],
total: 2,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Select multiple users
await page.locator('[data-testid="user-checkbox-user-1"]').click();
await page.locator('[data-testid="user-checkbox-user-2"]').click();
// Click clear selection button
await page.locator('[data-testid="clear-selection-button"]').click();
// Verify no checkboxes are checked
await expect(page.locator('[data-testid="user-checkbox-user-1"]')).not.toBeChecked();
await expect(page.locator('[data-testid="user-checkbox-user-2"]')).not.toBeChecked();
// Verify selection count is cleared
await expect(page.locator('[data-testid="selection-count"]')).toContainText('0');
});
test('should update user status', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
],
total: 1,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Mock updateUserStatus action
await routeContractSpec.mockApiCall('UpdateUserStatus', { success: true });
// Click status update for a user (e.g., suspend)
await page.locator('[data-testid="status-action-user-1"]').click();
await page.locator('[data-testid="suspend-option"]').click();
// Verify action is called with correct parameters
// This would be verified by checking the mock call count
await expect(page.locator('[data-testid="success-toast"]')).toBeVisible();
// Verify router.refresh() is called (indicated by data refresh)
await expect(page.locator('[data-testid="user-row-user-1"]')).toBeVisible();
});
test('should handle user status update errors', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
],
total: 1,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Mock updateUserStatus to return error
await routeContractSpec.mockApiCall('UpdateUserStatus', {
error: 'Failed to update status',
status: 500,
});
// Attempt to update user status
await page.locator('[data-testid="status-action-user-1"]').click();
await page.locator('[data-testid="suspend-option"]').click();
// Verify error message is displayed
await expect(page.locator('[data-testid="error-toast"]')).toBeVisible();
// Verify loading state is cleared
await expect(page.locator('[data-testid="loading-spinner"]')).not.toBeVisible();
});
test('should open delete confirmation dialog', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
],
total: 1,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Click delete button for a user
await page.locator('[data-testid="delete-button-user-1"]').click();
// Verify ConfirmDialog opens
await expect(page.locator('[data-testid="confirm-dialog"]')).toBeVisible();
// Verify dialog content (title, description)
await expect(page.locator('[data-testid="confirm-dialog-title"]')).toContainText(/delete/i);
await expect(page.locator('[data-testid="confirm-dialog-description"]')).toContainText(/john@example.com/i);
});
test('should cancel user deletion', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
],
total: 1,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Open delete confirmation dialog
await page.locator('[data-testid="delete-button-user-1"]').click();
// Click cancel/close
await page.locator('[data-testid="cancel-button"]').click();
// Verify dialog closes
await expect(page.locator('[data-testid="confirm-dialog"]')).not.toBeVisible();
// Verify delete action is NOT called
// This would be verified by checking the mock call count
});
test('should confirm and delete user', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
],
total: 1,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Mock deleteUser action
await routeContractSpec.mockApiCall('DeleteUser', { success: true });
// Open delete confirmation dialog
await page.locator('[data-testid="delete-button-user-1"]').click();
// Click confirm/delete button
await page.locator('[data-testid="confirm-delete-button"]').click();
// Verify deleteUser is called with correct userId
// This would be verified by checking the mock call count
// Verify router.refresh() is called (indicated by data refresh)
await expect(page.locator('[data-testid="user-row-user-1"]')).not.toBeVisible();
// Verify dialog closes
await expect(page.locator('[data-testid="confirm-dialog"]')).not.toBeVisible();
});
test('should handle user deletion errors', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
],
total: 1,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Mock deleteUser to return error
await routeContractSpec.mockApiCall('DeleteUser', {
error: 'Failed to delete user',
status: 500,
});
// Open delete confirmation dialog
await page.locator('[data-testid="delete-button-user-1"]').click();
// Click confirm/delete button
await page.locator('[data-testid="confirm-delete-button"]').click();
// Verify error message is displayed
await expect(page.locator('[data-testid="error-toast"]')).toBeVisible();
// Verify dialog remains open
await expect(page.locator('[data-testid="confirm-dialog"]')).toBeVisible();
});
test('should refresh users list', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
],
total: 1,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Mock refreshed data
const refreshedData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
{ id: 'user-2', email: 'jane@example.com', roles: ['user'], status: 'active' },
],
total: 2,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', refreshedData);
// Click refresh button
await page.locator('[data-testid="refresh-button"]').click();
// Verify router.refresh() is called (indicated by data refresh)
await expect(page.locator('[data-testid="user-row-user-2"]')).toBeVisible();
});
test('should handle users access denied (403/401)', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock API to return 403 error
await routeContractSpec.mockApiCall('AdminUsersPageQuery', {
error: 'Access Denied',
status: 403,
message: 'You must have Owner or Admin role to access this resource',
});
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Verify "Access Denied" error banner
await expect(page.locator('[data-testid="access-denied-banner"]')).toBeVisible();
// Check message about Owner or Admin role
await expect(page.locator('[data-testid="access-denied-message"]')).toContainText(/Owner or Admin/i);
});
});
describe('Admin Route Guard Integration', () => {
test('should enforce role-based access control on admin routes', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Test regular user role
await authManager.loginAsUser();
await page.goto(routeManager.getRoute('/admin'));
await expect(page).not.toHaveURL(/.*\/admin/);
// Test sponsor role
await authManager.loginAsSponsor();
await page.goto(routeManager.getRoute('/admin'));
await expect(page).not.toHaveURL(/.*\/admin/);
// Test admin role
await authManager.loginAsAdmin();
await page.goto(routeManager.getRoute('/admin'));
await expect(page).toHaveURL(/.*\/admin/);
// Test owner role
await authManager.loginAsOwner();
await page.goto(routeManager.getRoute('/admin'));
await expect(page).toHaveURL(/.*\/admin/);
});
test('should handle session expiration during admin operations', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
],
total: 1,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to /admin/users
await page.goto(routeManager.getRoute('/admin/users'));
// Mock session expiration
await routeContractSpec.mockApiCall('UpdateUserStatus', {
error: 'Unauthorized',
status: 401,
message: 'Session expired',
});
// Attempt operation (update status)
await page.locator('[data-testid="status-action-user-1"]').click();
await page.locator('[data-testid="suspend-option"]').click();
// Verify redirect to login
await expect(page).toHaveURL(/.*\/auth\/login/);
});
test('should maintain return URL after admin authentication', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Attempt to access /admin/users without auth
await page.goto(routeManager.getRoute('/admin/users'));
// Verify redirect to login with return URL
await expect(page).toHaveURL(/.*\/auth\/login/);
const url = new URL(page.url());
expect(url.searchParams.get('returnUrl')).toBe('/admin/users');
// Mock users data for after login
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
],
total: 1,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Login as admin
await authManager.loginAsAdmin();
// Verify redirect back to /admin/users
await expect(page).toHaveURL(/.*\/admin\/users/);
});
});
describe('Admin Cross-Screen State Management', () => {
test('should preserve filter state when navigating between admin pages', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
{ id: 'user-2', email: 'jane@example.com', roles: ['user'], status: 'active' },
],
total: 2,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Apply filters
await page.locator('[data-testid="search-input"]').fill('john');
await page.locator('[data-testid="role-filter"]').selectOption('admin');
// Verify URL has filter parameters
await expect(page).toHaveURL(/.*search=john.*role=admin/);
// Navigate to admin dashboard
await page.goto(routeManager.getRoute('/admin'));
// Navigate back to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Verify filters are preserved in URL
await expect(page).toHaveURL(/.*search=john.*role=admin/);
// Verify filter inputs still show the values
await expect(page.locator('[data-testid="search-input"]')).toHaveValue('john');
await expect(page.locator('[data-testid="role-filter"]')).toHaveValue('admin');
});
test('should preserve selection state during operations', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
{ id: 'user-2', email: 'jane@example.com', roles: ['user'], status: 'active' },
],
total: 2,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Select multiple users
await page.locator('[data-testid="user-checkbox-user-1"]').click();
await page.locator('[data-testid="user-checkbox-user-2"]').click();
// Verify selection count
await expect(page.locator('[data-testid="selection-count"]')).toContainText('2');
// Mock update status action
await routeContractSpec.mockApiCall('UpdateUserStatus', { success: true });
// Update status of one selected user
await page.locator('[data-testid="status-action-user-1"]').click();
await page.locator('[data-testid="suspend-option"]').click();
// Verify selection is maintained after operation
await expect(page.locator('[data-testid="user-checkbox-user-1"]')).toBeChecked();
await expect(page.locator('[data-testid="user-checkbox-user-2"]')).toBeChecked();
await expect(page.locator('[data-testid="selection-count"]')).toContainText('2');
});
test('should handle concurrent admin operations', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock users data
const mockUsersData = {
users: [
{ id: 'user-1', email: 'john@example.com', roles: ['admin'], status: 'active' },
{ id: 'user-2', email: 'jane@example.com', roles: ['user'], status: 'active' },
],
total: 2,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', mockUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Select users
await page.locator('[data-testid="user-checkbox-user-1"]').click();
// Mock multiple concurrent operations
await routeContractSpec.mockApiCall('UpdateUserStatus', { success: true });
await routeContractSpec.mockApiCall('DeleteUser', { success: true });
// Start filter operation
await page.locator('[data-testid="search-input"]').fill('john');
// Start update operation
const updatePromise = page.locator('[data-testid="status-action-user-1"]').click()
.then(() => page.locator('[data-testid="suspend-option"]').click());
// Start delete operation
const deletePromise = page.locator('[data-testid="delete-button-user-2"]').click()
.then(() => page.locator('[data-testid="confirm-delete-button"]').click());
// Wait for all operations to complete
await Promise.all([updatePromise, deletePromise]);
// Verify loading states are managed (no stuck spinners)
await expect(page.locator('[data-testid="loading-spinner"]')).not.toBeVisible();
// Verify UI remains usable after concurrent operations
await expect(page.locator('[data-testid="refresh-button"]')).toBeEnabled();
});
});
describe('Admin UI State Management', () => {
test('should show loading states during data operations', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock delayed response for dashboard
const mockDashboardData = {
totalUsers: 150,
activeUsers: 120,
pendingUsers: 25,
suspendedUsers: 5,
totalRevenue: 12500,
recentActivity: [],
};
// Mock with delay to simulate loading state
await routeContractSpec.mockApiCall('AdminDashboardPageQuery', mockDashboardData, { delay: 500 });
// Navigate to admin dashboard
await page.goto(routeManager.getRoute('/admin'));
// Verify loading spinner appears during data load
await expect(page.locator('[data-testid="loading-spinner"]')).toBeVisible();
// Wait for loading to complete
await expect(page.locator('[data-testid="loading-spinner"]')).not.toBeVisible();
// Verify data is displayed after loading
await expect(page.locator('[data-testid="total-users"]')).toContainText('150');
});
test('should handle error states gracefully', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock various error scenarios
await routeContractSpec.mockApiCall('AdminDashboardPageQuery', {
error: 'Internal Server Error',
status: 500,
});
// Navigate to admin dashboard
await page.goto(routeManager.getRoute('/admin'));
// Verify error banner is displayed
await expect(page.locator('[data-testid="error-banner"]')).toBeVisible();
// Verify error message content
await expect(page.locator('[data-testid="error-message"]')).toContainText(/error/i);
// Verify UI remains usable after errors
await expect(page.locator('[data-testid="refresh-button"]')).toBeEnabled();
await expect(page.locator('[data-testid="navigation-menu"]')).toBeVisible();
// Verify error can be dismissed
await page.locator('[data-testid="error-dismiss"]').click();
await expect(page.locator('[data-testid="error-banner"]')).not.toBeVisible();
});
test('should handle empty states', async ({ page }) => {
const routeManager = new WebsiteRouteManager(page);
const authManager = new WebsiteAuthManager(page);
const routeContractSpec = new RouteContractSpec(page);
// Login as admin
await authManager.loginAsAdmin();
// Mock empty users list
const emptyUsersData = {
users: [],
total: 0,
page: 1,
totalPages: 1,
};
await routeContractSpec.mockApiCall('AdminUsersPageQuery', emptyUsersData);
// Navigate to admin users
await page.goto(routeManager.getRoute('/admin/users'));
// Verify empty state message is shown
await expect(page.locator('[data-testid="empty-state"]')).toBeVisible();
await expect(page.locator('[data-testid="empty-state-message"]')).toContainText(/no users/i);
// Verify empty state has helpful actions
await expect(page.locator('[data-testid="empty-state-refresh"]')).toBeVisible();
});
});
});