1268 lines
46 KiB
TypeScript
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();
|
|
});
|
|
});
|
|
}); |