Update tests and e2e files to support locale-based routing
- Replaced static paths with dynamic locale subpaths (`/[locale]/*`) in imports, URLs, and assertions across tests. - Updated `next-intl` mocks for improved compatibility with `locale`-aware components. - Standardized `page.goto` and navigation tests with `/en` as the base locale for consistency.
This commit is contained in:
@@ -13,7 +13,7 @@ test.describe('Admin Access Control', () => {
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
|
||||
// Navigate to authenticated page to test authenticated header (not homepage)
|
||||
await page.goto('/settings');
|
||||
await page.goto('/en/settings');
|
||||
await page.waitForSelector('h1:has-text("Settings")');
|
||||
|
||||
// Should not see admin link in authenticated header navigation
|
||||
@@ -28,10 +28,10 @@ test.describe('Admin Access Control', () => {
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
|
||||
// Try to access admin page directly
|
||||
await page.goto('/admin');
|
||||
await page.goto('/en/admin');
|
||||
|
||||
// Should be redirected away from admin (to login or home)
|
||||
await page.waitForURL(/\/(auth\/login|$)/);
|
||||
await page.waitForURL(/\/en\/(auth\/login|$)/);
|
||||
expect(page.url()).not.toContain('/admin');
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ test.describe('Admin Access Control', () => {
|
||||
|
||||
// Navigate to settings page to ensure user state is loaded
|
||||
// (AuthGuard fetches user on protected pages)
|
||||
await page.goto('/settings');
|
||||
await page.goto('/en/settings');
|
||||
await page.waitForSelector('h1:has-text("Settings")');
|
||||
|
||||
// Should see admin link in header navigation bar
|
||||
@@ -52,7 +52,7 @@ test.describe('Admin Access Control', () => {
|
||||
.locator('header nav')
|
||||
.getByRole('link', { name: 'Admin', exact: true });
|
||||
await expect(headerAdminLink).toBeVisible();
|
||||
await expect(headerAdminLink).toHaveAttribute('href', '/admin');
|
||||
await expect(headerAdminLink).toHaveAttribute('href', '/en/admin');
|
||||
});
|
||||
|
||||
test('superuser should be able to access admin dashboard', async ({ page }) => {
|
||||
@@ -61,10 +61,10 @@ test.describe('Admin Access Control', () => {
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
|
||||
// Navigate to admin page
|
||||
await page.goto('/admin');
|
||||
await page.goto('/en/admin');
|
||||
|
||||
// Should see admin dashboard
|
||||
await expect(page).toHaveURL('/admin');
|
||||
await expect(page).toHaveURL('/en/admin');
|
||||
await expect(page.locator('h1')).toContainText('Admin Dashboard');
|
||||
});
|
||||
});
|
||||
@@ -73,7 +73,7 @@ test.describe('Admin Dashboard', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin');
|
||||
await page.goto('/en/admin');
|
||||
});
|
||||
|
||||
test('should display page title and description', async ({ page }) => {
|
||||
@@ -120,7 +120,7 @@ test.describe('Admin Navigation', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin');
|
||||
await page.goto('/en/admin');
|
||||
});
|
||||
|
||||
test('should display admin sidebar', async ({ page }) => {
|
||||
@@ -143,9 +143,9 @@ test.describe('Admin Navigation', () => {
|
||||
});
|
||||
|
||||
test('should navigate to users page', async ({ page }) => {
|
||||
await page.goto('/admin/users');
|
||||
await page.goto('/en/admin/users');
|
||||
|
||||
await expect(page).toHaveURL('/admin/users');
|
||||
await expect(page).toHaveURL('/en/admin/users');
|
||||
await expect(page.locator('h1')).toContainText('User Management');
|
||||
|
||||
// Breadcrumbs should show Admin > Users
|
||||
@@ -158,9 +158,9 @@ test.describe('Admin Navigation', () => {
|
||||
});
|
||||
|
||||
test('should navigate to organizations page', async ({ page }) => {
|
||||
await page.goto('/admin/organizations');
|
||||
await page.goto('/en/admin/organizations');
|
||||
|
||||
await expect(page).toHaveURL('/admin/organizations');
|
||||
await expect(page).toHaveURL('/en/admin/organizations');
|
||||
await expect(page.getByRole('heading', { name: 'All Organizations' })).toBeVisible();
|
||||
|
||||
// Breadcrumbs should show Admin > Organizations
|
||||
@@ -173,9 +173,9 @@ test.describe('Admin Navigation', () => {
|
||||
});
|
||||
|
||||
test('should navigate to settings page', async ({ page }) => {
|
||||
await page.goto('/admin/settings');
|
||||
await page.goto('/en/admin/settings');
|
||||
|
||||
await expect(page).toHaveURL('/admin/settings');
|
||||
await expect(page).toHaveURL('/en/admin/settings');
|
||||
await expect(page.locator('h1')).toContainText('System Settings');
|
||||
|
||||
// Breadcrumbs should show Admin > Settings
|
||||
@@ -208,14 +208,14 @@ test.describe('Admin Navigation', () => {
|
||||
});
|
||||
|
||||
test('should navigate back to dashboard from users page', async ({ page }) => {
|
||||
await page.goto('/admin/users');
|
||||
await page.goto('/en/admin/users');
|
||||
|
||||
// Click dashboard link in sidebar
|
||||
const dashboardLink = page.getByTestId('nav-dashboard');
|
||||
await dashboardLink.click();
|
||||
|
||||
await page.waitForURL('/admin');
|
||||
await expect(page).toHaveURL('/admin');
|
||||
await page.waitForURL('/en/admin');
|
||||
await expect(page).toHaveURL('/en/admin');
|
||||
await expect(page.locator('h1')).toContainText('Admin Dashboard');
|
||||
});
|
||||
});
|
||||
@@ -227,7 +227,7 @@ test.describe('Admin Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
test('should show single breadcrumb on dashboard', async ({ page }) => {
|
||||
await page.goto('/admin');
|
||||
await page.goto('/en/admin');
|
||||
|
||||
const breadcrumbs = page.getByTestId('breadcrumbs');
|
||||
await expect(breadcrumbs).toBeVisible();
|
||||
@@ -239,12 +239,12 @@ test.describe('Admin Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
test('should show clickable parent breadcrumb', async ({ page }) => {
|
||||
await page.goto('/admin/users');
|
||||
await page.goto('/en/admin/users');
|
||||
|
||||
// 'Admin' should be a clickable link (test ID is on the Link element itself)
|
||||
const adminBreadcrumb = page.getByTestId('breadcrumb-admin');
|
||||
await expect(adminBreadcrumb).toBeVisible();
|
||||
await expect(adminBreadcrumb).toHaveAttribute('href', '/admin');
|
||||
await expect(adminBreadcrumb).toHaveAttribute('href', '/en/admin');
|
||||
|
||||
// 'Users' should be current page (not a link, so it's a span)
|
||||
const usersBreadcrumb = page.getByTestId('breadcrumb-users');
|
||||
@@ -253,13 +253,13 @@ test.describe('Admin Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
test('should navigate via breadcrumb link', async ({ page }) => {
|
||||
await page.goto('/admin/users');
|
||||
await page.goto('/en/admin/users');
|
||||
|
||||
// Click 'Admin' breadcrumb to go back to dashboard
|
||||
const adminBreadcrumb = page.getByTestId('breadcrumb-admin');
|
||||
|
||||
await Promise.all([page.waitForURL('/admin'), adminBreadcrumb.click()]);
|
||||
await Promise.all([page.waitForURL('/en/admin'), adminBreadcrumb.click()]);
|
||||
|
||||
await expect(page).toHaveURL('/admin');
|
||||
await expect(page).toHaveURL('/en/admin');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,11 +10,11 @@ test.describe('Admin Dashboard - Page Load', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin');
|
||||
await page.goto('/en/admin');
|
||||
});
|
||||
|
||||
test('should display admin dashboard page', async ({ page }) => {
|
||||
await expect(page).toHaveURL('/admin');
|
||||
await expect(page).toHaveURL('/en/admin');
|
||||
|
||||
await expect(page.getByRole('heading', { name: 'Admin Dashboard' })).toBeVisible();
|
||||
await expect(page.getByText('Manage users, organizations, and system settings')).toBeVisible();
|
||||
@@ -29,7 +29,7 @@ test.describe('Admin Dashboard - Statistics Cards', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin');
|
||||
await page.goto('/en/admin');
|
||||
});
|
||||
|
||||
test('should display all stat cards', async ({ page }) => {
|
||||
@@ -62,7 +62,7 @@ test.describe('Admin Dashboard - Quick Actions', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin');
|
||||
await page.goto('/en/admin');
|
||||
});
|
||||
|
||||
test('should display quick actions section', async ({ page }) => {
|
||||
@@ -86,9 +86,9 @@ test.describe('Admin Dashboard - Quick Actions', () => {
|
||||
test('should navigate to users page when clicking user management', async ({ page }) => {
|
||||
const userManagementLink = page.getByRole('link', { name: /User Management/i });
|
||||
|
||||
await Promise.all([page.waitForURL('/admin/users'), userManagementLink.click()]);
|
||||
await Promise.all([page.waitForURL('/en/admin/users'), userManagementLink.click()]);
|
||||
|
||||
await expect(page).toHaveURL('/admin/users');
|
||||
await expect(page).toHaveURL('/en/admin/users');
|
||||
});
|
||||
|
||||
test('should navigate to organizations page when clicking organizations', async ({ page }) => {
|
||||
@@ -96,9 +96,9 @@ test.describe('Admin Dashboard - Quick Actions', () => {
|
||||
const quickActionsSection = page.locator('h2:has-text("Quick Actions")').locator('..');
|
||||
const organizationsLink = quickActionsSection.getByRole('link', { name: /Organizations/i });
|
||||
|
||||
await Promise.all([page.waitForURL('/admin/organizations'), organizationsLink.click()]);
|
||||
await Promise.all([page.waitForURL('/en/admin/organizations'), organizationsLink.click()]);
|
||||
|
||||
await expect(page).toHaveURL('/admin/organizations');
|
||||
await expect(page).toHaveURL('/en/admin/organizations');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -106,7 +106,7 @@ test.describe('Admin Dashboard - Analytics Charts', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin');
|
||||
await page.goto('/en/admin');
|
||||
});
|
||||
|
||||
test('should display analytics overview section', async ({ page }) => {
|
||||
@@ -151,7 +151,7 @@ test.describe('Admin Dashboard - Accessibility', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin');
|
||||
await page.goto('/en/admin');
|
||||
});
|
||||
|
||||
test('should have proper heading hierarchy', async ({ page }) => {
|
||||
|
||||
@@ -11,7 +11,7 @@ test.describe('Admin Organization Members - Navigation from Organizations List',
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/organizations');
|
||||
await page.goto('/en/admin/organizations');
|
||||
await page.waitForSelector('table tbody tr');
|
||||
});
|
||||
|
||||
@@ -24,12 +24,12 @@ test.describe('Admin Organization Members - Navigation from Organizations List',
|
||||
|
||||
// Click "View Members"
|
||||
await Promise.all([
|
||||
page.waitForURL(/\/admin\/organizations\/[^/]+\/members/),
|
||||
page.waitForURL(/\/en\/admin\/organizations\/[^/]+\/members/),
|
||||
page.getByText('View Members').click(),
|
||||
]);
|
||||
|
||||
// Should be on members page
|
||||
await expect(page).toHaveURL(/\/admin\/organizations\/[^/]+\/members/);
|
||||
await expect(page).toHaveURL(/\/en\/admin\/organizations\/[^/]+\/members/);
|
||||
});
|
||||
|
||||
test('should navigate to members page when clicking member count', async ({ page }) => {
|
||||
@@ -39,12 +39,12 @@ test.describe('Admin Organization Members - Navigation from Organizations List',
|
||||
|
||||
// Click on member count
|
||||
await Promise.all([
|
||||
page.waitForURL(/\/admin\/organizations\/[^/]+\/members/),
|
||||
page.waitForURL(/\/en\/admin\/organizations\/[^/]+\/members/),
|
||||
memberButton.click(),
|
||||
]);
|
||||
|
||||
// Should be on members page
|
||||
await expect(page).toHaveURL(/\/admin\/organizations\/[^/]+\/members/);
|
||||
await expect(page).toHaveURL(/\/en\/admin\/organizations\/[^/]+\/members/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -52,7 +52,7 @@ test.describe('Admin Organization Members - Page Structure', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/organizations');
|
||||
await page.goto('/en/admin/organizations');
|
||||
await page.waitForSelector('table tbody tr');
|
||||
|
||||
// Navigate to members page
|
||||
@@ -60,13 +60,13 @@ test.describe('Admin Organization Members - Page Structure', () => {
|
||||
await actionButton.click();
|
||||
|
||||
await Promise.all([
|
||||
page.waitForURL(/\/admin\/organizations\/[^/]+\/members/),
|
||||
page.waitForURL(/\/en\/admin\/organizations\/[^/]+\/members/),
|
||||
page.getByText('View Members').click(),
|
||||
]);
|
||||
});
|
||||
|
||||
test('should display organization members page', async ({ page }) => {
|
||||
await expect(page).toHaveURL(/\/admin\/organizations\/[^/]+\/members/);
|
||||
await expect(page).toHaveURL(/\/en\/admin\/organizations\/[^/]+\/members/);
|
||||
|
||||
// Wait for page to load
|
||||
await page.waitForSelector('table');
|
||||
@@ -123,7 +123,7 @@ test.describe('Admin Organization Members - AddMemberDialog E2E Tests', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/organizations');
|
||||
await page.goto('/en/admin/organizations');
|
||||
await page.waitForSelector('table tbody tr');
|
||||
|
||||
// Navigate to members page
|
||||
@@ -131,7 +131,7 @@ test.describe('Admin Organization Members - AddMemberDialog E2E Tests', () => {
|
||||
await actionButton.click();
|
||||
|
||||
await Promise.all([
|
||||
page.waitForURL(/\/admin\/organizations\/[^/]+\/members/),
|
||||
page.waitForURL(/\/en\/admin\/organizations\/[^/]+\/members/),
|
||||
page.getByText('View Members').click(),
|
||||
]);
|
||||
|
||||
|
||||
@@ -10,11 +10,11 @@ test.describe('Admin Organization Management - Page Load', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/organizations');
|
||||
await page.goto('/en/admin/organizations');
|
||||
});
|
||||
|
||||
test('should display organization management page', async ({ page }) => {
|
||||
await expect(page).toHaveURL('/admin/organizations');
|
||||
await expect(page).toHaveURL('/en/admin/organizations');
|
||||
|
||||
// Wait for page to load
|
||||
await page.waitForSelector('table');
|
||||
@@ -41,7 +41,7 @@ test.describe('Admin Organization Management - Organization List Table', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/organizations');
|
||||
await page.goto('/en/admin/organizations');
|
||||
});
|
||||
|
||||
test('should display organization list table with headers', async ({ page }) => {
|
||||
@@ -107,7 +107,7 @@ test.describe('Admin Organization Management - Pagination', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/organizations');
|
||||
await page.goto('/en/admin/organizations');
|
||||
});
|
||||
|
||||
test('should display pagination info', async ({ page }) => {
|
||||
@@ -127,7 +127,7 @@ test.describe('Admin Organization Management - Create Organization Button', () =
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/organizations');
|
||||
await page.goto('/en/admin/organizations');
|
||||
});
|
||||
|
||||
test('should display create organization button', async ({ page }) => {
|
||||
@@ -140,7 +140,7 @@ test.describe('Admin Organization Management - Action Menu', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/organizations');
|
||||
await page.goto('/en/admin/organizations');
|
||||
await page.waitForSelector('table tbody tr');
|
||||
});
|
||||
|
||||
@@ -192,12 +192,12 @@ test.describe('Admin Organization Management - Action Menu', () => {
|
||||
|
||||
// Click view members - use Promise.all for Next.js Link navigation
|
||||
await Promise.all([
|
||||
page.waitForURL(/\/admin\/organizations\/[^/]+\/members/),
|
||||
page.waitForURL(/\/en\/admin\/organizations\/[^/]+\/members/),
|
||||
page.getByText('View Members').click(),
|
||||
]);
|
||||
|
||||
// Should navigate to members page
|
||||
await expect(page).toHaveURL(/\/admin\/organizations\/[^/]+\/members/);
|
||||
await expect(page).toHaveURL(/\/en\/admin\/organizations\/[^/]+\/members/);
|
||||
});
|
||||
|
||||
test('should show delete confirmation dialog when clicking delete', async ({ page }) => {
|
||||
@@ -248,7 +248,7 @@ test.describe('Admin Organization Management - Edit Organization Dialog', () =>
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/organizations');
|
||||
await page.goto('/en/admin/organizations');
|
||||
await page.waitForSelector('table tbody tr');
|
||||
});
|
||||
|
||||
@@ -297,7 +297,7 @@ test.describe('Admin Organization Management - Member Count Interaction', () =>
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/organizations');
|
||||
await page.goto('/en/admin/organizations');
|
||||
await page.waitForSelector('table tbody tr');
|
||||
});
|
||||
|
||||
@@ -308,12 +308,12 @@ test.describe('Admin Organization Management - Member Count Interaction', () =>
|
||||
|
||||
// Click on member count - use Promise.all for Next.js Link navigation
|
||||
await Promise.all([
|
||||
page.waitForURL(/\/admin\/organizations\/[^/]+\/members/),
|
||||
page.waitForURL(/\/en\/admin\/organizations\/[^/]+\/members/),
|
||||
memberButton.click(),
|
||||
]);
|
||||
|
||||
// Should navigate to members page
|
||||
await expect(page).toHaveURL(/\/admin\/organizations\/[^/]+\/members/);
|
||||
await expect(page).toHaveURL(/\/en\/admin\/organizations\/[^/]+\/members/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -321,7 +321,7 @@ test.describe('Admin Organization Management - Accessibility', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/organizations');
|
||||
await page.goto('/en/admin/organizations');
|
||||
});
|
||||
|
||||
test('should have proper heading hierarchy', async ({ page }) => {
|
||||
|
||||
@@ -10,11 +10,11 @@ test.describe('Admin User Management - Page Load', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/users');
|
||||
await page.goto('/en/admin/users');
|
||||
});
|
||||
|
||||
test('should display user management page', async ({ page }) => {
|
||||
await expect(page).toHaveURL('/admin/users');
|
||||
await expect(page).toHaveURL('/en/admin/users');
|
||||
await expect(page.locator('h1')).toContainText('User Management');
|
||||
});
|
||||
|
||||
@@ -38,7 +38,7 @@ test.describe('Admin User Management - User List Table', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/users');
|
||||
await page.goto('/en/admin/users');
|
||||
});
|
||||
|
||||
test('should display user list table with headers', async ({ page }) => {
|
||||
@@ -101,7 +101,7 @@ test.describe('Admin User Management - Search and Filters', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/users');
|
||||
await page.goto('/en/admin/users');
|
||||
});
|
||||
|
||||
test('should display search input', async ({ page }) => {
|
||||
@@ -244,7 +244,7 @@ test.describe('Admin User Management - Pagination', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/users');
|
||||
await page.goto('/en/admin/users');
|
||||
});
|
||||
|
||||
test('should display pagination info', async ({ page }) => {
|
||||
@@ -262,7 +262,7 @@ test.describe('Admin User Management - Row Selection', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/users');
|
||||
await page.goto('/en/admin/users');
|
||||
await page.waitForSelector('table tbody tr');
|
||||
});
|
||||
|
||||
@@ -325,7 +325,7 @@ test.describe('Admin User Management - Create User Dialog', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/users');
|
||||
await page.goto('/en/admin/users');
|
||||
});
|
||||
|
||||
test('should open create user dialog', async ({ page }) => {
|
||||
@@ -449,7 +449,7 @@ test.describe('Admin User Management - Action Menu', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/users');
|
||||
await page.goto('/en/admin/users');
|
||||
await page.waitForSelector('table tbody tr');
|
||||
});
|
||||
|
||||
@@ -502,7 +502,7 @@ test.describe('Admin User Management - Edit User Dialog', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/users');
|
||||
await page.goto('/en/admin/users');
|
||||
await page.waitForSelector('table tbody tr');
|
||||
});
|
||||
|
||||
@@ -561,7 +561,7 @@ test.describe('Admin User Management - Bulk Actions', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/users');
|
||||
await page.goto('/en/admin/users');
|
||||
await page.waitForSelector('table tbody tr');
|
||||
});
|
||||
|
||||
@@ -631,7 +631,7 @@ test.describe('Admin User Management - Accessibility', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await setupSuperuserMocks(page);
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
await page.goto('/admin/users');
|
||||
await page.goto('/en/admin/users');
|
||||
});
|
||||
|
||||
test('should have proper heading hierarchy', async ({ page }) => {
|
||||
|
||||
@@ -4,7 +4,7 @@ test.describe('AuthGuard - Route Protection', () => {
|
||||
test.beforeEach(async ({ page, context }) => {
|
||||
// Clear storage before each test to ensure clean state
|
||||
await context.clearCookies();
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
await page.evaluate(() => {
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
@@ -15,7 +15,7 @@ test.describe('AuthGuard - Route Protection', () => {
|
||||
// Try to access a protected route (if you have one)
|
||||
// For now, we'll test the root if it's protected
|
||||
// Adjust the route based on your actual protected routes
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
|
||||
// If root is protected, should redirect to login or show homepage
|
||||
// Wait for page to stabilize
|
||||
@@ -28,24 +28,24 @@ test.describe('AuthGuard - Route Protection', () => {
|
||||
|
||||
test('should allow access to public routes without auth', async ({ page }) => {
|
||||
// Test login page
|
||||
await page.goto('/login');
|
||||
await expect(page).toHaveURL('/login');
|
||||
await page.goto('/en/login');
|
||||
await expect(page).toHaveURL('/en/login');
|
||||
await expect(page.locator('h2')).toContainText('Sign in to your account');
|
||||
|
||||
// Test register page
|
||||
await page.goto('/register');
|
||||
await expect(page).toHaveURL('/register');
|
||||
await page.goto('/en/register');
|
||||
await expect(page).toHaveURL('/en/register');
|
||||
await expect(page.locator('h2')).toContainText('Create your account');
|
||||
|
||||
// Test password reset page
|
||||
await page.goto('/password-reset');
|
||||
await expect(page).toHaveURL('/password-reset');
|
||||
await page.goto('/en/password-reset');
|
||||
await expect(page).toHaveURL('/en/password-reset');
|
||||
await expect(page.locator('h2')).toContainText('Reset your password');
|
||||
});
|
||||
|
||||
test('should persist authentication across page reloads', async ({ page }) => {
|
||||
// Manually set a mock token in localStorage for testing
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
await page.evaluate(() => {
|
||||
const mockToken = {
|
||||
access_token: 'mock-access-token',
|
||||
@@ -73,7 +73,7 @@ test.describe('AuthGuard - Route Protection', () => {
|
||||
|
||||
test('should clear authentication on logout', async ({ page }) => {
|
||||
// Set up authenticated state
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
await page.evaluate(() => {
|
||||
const mockToken = {
|
||||
access_token: 'mock-access-token',
|
||||
@@ -110,7 +110,7 @@ test.describe('AuthGuard - Route Protection', () => {
|
||||
|
||||
test('should not allow access to auth pages when already logged in', async ({ page }) => {
|
||||
// Set up authenticated state
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
await page.evaluate(() => {
|
||||
const mockToken = {
|
||||
access_token: 'mock-access-token',
|
||||
@@ -127,7 +127,7 @@ test.describe('AuthGuard - Route Protection', () => {
|
||||
});
|
||||
|
||||
// Try to access login page
|
||||
await page.goto('/login');
|
||||
await page.goto('/en/login');
|
||||
|
||||
// Wait a bit for potential redirect
|
||||
await page.waitForTimeout(2000);
|
||||
@@ -141,7 +141,7 @@ test.describe('AuthGuard - Route Protection', () => {
|
||||
|
||||
test('should handle expired tokens gracefully', async ({ page }) => {
|
||||
// Set up authenticated state with expired token
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
await page.evaluate(() => {
|
||||
const expiredToken = {
|
||||
access_token: 'expired-access-token',
|
||||
@@ -171,7 +171,7 @@ test.describe('AuthGuard - Route Protection', () => {
|
||||
test('should preserve intended destination after login', async ({ page }) => {
|
||||
// This is a nice-to-have feature that requires protected routes
|
||||
// For now, just verify the test doesn't crash
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
|
||||
// Login (via localStorage for testing)
|
||||
await page.evaluate(() => {
|
||||
|
||||
@@ -28,7 +28,7 @@ test.describe('Login Flow', () => {
|
||||
});
|
||||
|
||||
// Navigate to login page before each test
|
||||
await page.goto('/login');
|
||||
await page.goto('/en/login');
|
||||
});
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
@@ -128,7 +128,7 @@ test.describe('Login Flow', () => {
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Should stay on login page (validation failed)
|
||||
await expect(page).toHaveURL('/login');
|
||||
await expect(page).toHaveURL('/en/login');
|
||||
});
|
||||
|
||||
test('should show error for invalid credentials', async ({ page }) => {
|
||||
@@ -162,10 +162,10 @@ test.describe('Login Flow', () => {
|
||||
// Click forgot password link - use Promise.all to wait for navigation
|
||||
const forgotLink = page.getByRole('link', { name: 'Forgot password?' });
|
||||
|
||||
await Promise.all([page.waitForURL('/password-reset'), forgotLink.click()]);
|
||||
await Promise.all([page.waitForURL('/en/password-reset'), forgotLink.click()]);
|
||||
|
||||
// Should be on password reset page
|
||||
await expect(page).toHaveURL('/password-reset');
|
||||
await expect(page).toHaveURL('/en/password-reset');
|
||||
await expect(page.locator('h2')).toContainText('Reset your password');
|
||||
});
|
||||
|
||||
@@ -173,10 +173,10 @@ test.describe('Login Flow', () => {
|
||||
// Click sign up link - use Promise.all to wait for navigation
|
||||
const signupLink = page.getByRole('link', { name: 'Sign up' });
|
||||
|
||||
await Promise.all([page.waitForURL('/register'), signupLink.click()]);
|
||||
await Promise.all([page.waitForURL('/en/register'), signupLink.click()]);
|
||||
|
||||
// Should be on register page
|
||||
await expect(page).toHaveURL('/register');
|
||||
await expect(page).toHaveURL('/en/register');
|
||||
await expect(page.locator('h2')).toContainText('Create your account');
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { test, expect } from '@playwright/test';
|
||||
test.describe('Password Reset Request Flow', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Navigate to password reset page
|
||||
await page.goto('/password-reset');
|
||||
await page.goto('/en/password-reset');
|
||||
});
|
||||
|
||||
test('should display password reset request form', async ({ page }) => {
|
||||
@@ -37,7 +37,7 @@ test.describe('Password Reset Request Flow', () => {
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Should stay on password reset page (validation failed)
|
||||
await expect(page).toHaveURL('/password-reset');
|
||||
await expect(page).toHaveURL('/en/password-reset');
|
||||
});
|
||||
|
||||
test('should successfully submit password reset request', async ({ page }) => {
|
||||
@@ -55,10 +55,10 @@ test.describe('Password Reset Request Flow', () => {
|
||||
// Click back to login link - use Promise.all to wait for navigation
|
||||
const loginLink = page.getByRole('link', { name: 'Back to login' });
|
||||
|
||||
await Promise.all([page.waitForURL('/login'), loginLink.click()]);
|
||||
await Promise.all([page.waitForURL('/en/login'), loginLink.click()]);
|
||||
|
||||
// Should be on login page
|
||||
await expect(page).toHaveURL('/login');
|
||||
await expect(page).toHaveURL('/en/login');
|
||||
await expect(page.locator('h2')).toContainText('Sign in to your account');
|
||||
});
|
||||
|
||||
@@ -84,7 +84,7 @@ test.describe('Password Reset Request Flow', () => {
|
||||
test.describe('Password Reset Confirm Flow', () => {
|
||||
test('should display error for missing token', async ({ page }) => {
|
||||
// Navigate without token
|
||||
await page.goto('/password-reset/confirm');
|
||||
await page.goto('/en/password-reset/confirm');
|
||||
|
||||
// Should show error message
|
||||
await expect(page.locator('h2')).toContainText(/Invalid/i);
|
||||
@@ -95,7 +95,7 @@ test.describe('Password Reset Confirm Flow', () => {
|
||||
|
||||
test('should display password reset confirm form with valid token', async ({ page }) => {
|
||||
// Navigate with token (using a dummy token for UI testing)
|
||||
await page.goto('/password-reset/confirm?token=dummy-test-token-123');
|
||||
await page.goto('/en/password-reset/confirm?token=dummy-test-token-123');
|
||||
|
||||
// Check page title
|
||||
await expect(page.locator('h2')).toContainText('Set new password');
|
||||
@@ -108,7 +108,7 @@ test.describe('Password Reset Confirm Flow', () => {
|
||||
|
||||
test('should show validation errors for empty form', async ({ page }) => {
|
||||
// Navigate with token
|
||||
await page.goto('/password-reset/confirm?token=dummy-test-token-123');
|
||||
await page.goto('/en/password-reset/confirm?token=dummy-test-token-123');
|
||||
|
||||
// Click submit without filling form
|
||||
await page.locator('button[type="submit"]').click();
|
||||
@@ -120,7 +120,7 @@ test.describe('Password Reset Confirm Flow', () => {
|
||||
|
||||
test('should show validation error for weak password', async ({ page }) => {
|
||||
// Navigate with token
|
||||
await page.goto('/password-reset/confirm?token=dummy-test-token-123');
|
||||
await page.goto('/en/password-reset/confirm?token=dummy-test-token-123');
|
||||
|
||||
// Fill with weak password
|
||||
await page.locator('input[name="new_password"]').fill('weak');
|
||||
@@ -136,7 +136,7 @@ test.describe('Password Reset Confirm Flow', () => {
|
||||
|
||||
test('should show validation error for mismatched passwords', async ({ page }) => {
|
||||
// Navigate with token
|
||||
await page.goto('/password-reset/confirm?token=dummy-test-token-123');
|
||||
await page.goto('/en/password-reset/confirm?token=dummy-test-token-123');
|
||||
|
||||
// Fill with mismatched passwords
|
||||
await page.locator('input[name="new_password"]').fill('Password123!');
|
||||
@@ -152,7 +152,7 @@ test.describe('Password Reset Confirm Flow', () => {
|
||||
|
||||
test('should show error for invalid token', async ({ page }) => {
|
||||
// Navigate with invalid token
|
||||
await page.goto('/password-reset/confirm?token=invalid-token');
|
||||
await page.goto('/en/password-reset/confirm?token=invalid-token');
|
||||
|
||||
// Fill form with valid passwords
|
||||
await page.locator('input[name="new_password"]').fill('NewPassword123!');
|
||||
@@ -172,7 +172,7 @@ test.describe('Password Reset Confirm Flow', () => {
|
||||
// In real scenario, you'd generate a token via API or use a test fixture
|
||||
|
||||
// For UI testing, we use a dummy token - backend will reject it
|
||||
await page.goto('/password-reset/confirm?token=valid-test-token-from-backend');
|
||||
await page.goto('/en/password-reset/confirm?token=valid-test-token-from-backend');
|
||||
|
||||
// Fill form with valid passwords
|
||||
await page.locator('input[name="new_password"]').fill('NewPassword123!');
|
||||
@@ -188,21 +188,21 @@ test.describe('Password Reset Confirm Flow', () => {
|
||||
|
||||
test('should navigate to request new reset link', async ({ page }) => {
|
||||
// Navigate without token to trigger error state
|
||||
await page.goto('/password-reset/confirm');
|
||||
await page.goto('/en/password-reset/confirm');
|
||||
|
||||
// Click request new reset link - use Promise.all to wait for navigation
|
||||
const resetLink = page.getByRole('link', { name: 'Request new reset link' });
|
||||
|
||||
await Promise.all([page.waitForURL('/password-reset'), resetLink.click()]);
|
||||
await Promise.all([page.waitForURL('/en/password-reset'), resetLink.click()]);
|
||||
|
||||
// Should be on password reset request page
|
||||
await expect(page).toHaveURL('/password-reset');
|
||||
await expect(page).toHaveURL('/en/password-reset');
|
||||
await expect(page.locator('h2')).toContainText('Reset your password');
|
||||
});
|
||||
|
||||
test('should toggle password visibility', async ({ page }) => {
|
||||
// Navigate with token
|
||||
await page.goto('/password-reset/confirm?token=dummy-test-token-123');
|
||||
await page.goto('/en/password-reset/confirm?token=dummy-test-token-123');
|
||||
|
||||
const passwordInput = page.locator('input[name="new_password"]');
|
||||
const confirmPasswordInput = page.locator('input[name="confirm_password"]');
|
||||
@@ -216,7 +216,7 @@ test.describe('Password Reset Confirm Flow', () => {
|
||||
|
||||
test('should disable submit button while loading', async ({ page }) => {
|
||||
// Navigate with token
|
||||
await page.goto('/password-reset/confirm?token=dummy-test-token-123');
|
||||
await page.goto('/en/password-reset/confirm?token=dummy-test-token-123');
|
||||
|
||||
// Fill form
|
||||
await page.locator('input[name="new_password"]').fill('NewPassword123!');
|
||||
|
||||
@@ -28,7 +28,7 @@ test.describe('Registration Flow', () => {
|
||||
});
|
||||
|
||||
// Navigate to register page before each test
|
||||
await page.goto('/register');
|
||||
await page.goto('/en/register');
|
||||
});
|
||||
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
@@ -222,10 +222,10 @@ test.describe('Registration Flow', () => {
|
||||
const loginLink = page.getByRole('link', { name: 'Sign in' });
|
||||
|
||||
// Use Promise.all to wait for navigation
|
||||
await Promise.all([page.waitForURL('/login'), loginLink.click()]);
|
||||
await Promise.all([page.waitForURL('/en/login'), loginLink.click()]);
|
||||
|
||||
// Should be on login page
|
||||
await expect(page).toHaveURL('/login');
|
||||
await expect(page).toHaveURL('/en/login');
|
||||
await expect(page.locator('h2')).toContainText('Sign in to your account');
|
||||
});
|
||||
|
||||
|
||||
@@ -100,15 +100,15 @@ export async function loginViaUI(
|
||||
email = 'test@example.com',
|
||||
password = 'Password123!'
|
||||
): Promise<void> {
|
||||
// Navigate to login page
|
||||
await page.goto('/login');
|
||||
// Navigate to login page (with locale prefix)
|
||||
await page.goto('/en/login');
|
||||
|
||||
// Fill login form
|
||||
await page.locator('input[name="email"]').fill(email);
|
||||
await page.locator('input[name="password"]').fill(password);
|
||||
|
||||
// Submit and wait for navigation to home
|
||||
await Promise.all([page.waitForURL('/'), page.locator('button[type="submit"]').click()]);
|
||||
// Submit and wait for navigation to home (with locale prefix)
|
||||
await Promise.all([page.waitForURL('/en'), page.locator('button[type="submit"]').click()]);
|
||||
|
||||
// Wait for auth to settle
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
@@ -8,7 +8,7 @@ import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('Homepage - Desktop Navigation', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
// Wait for page to be fully loaded
|
||||
});
|
||||
|
||||
@@ -36,11 +36,11 @@ test.describe('Homepage - Desktop Navigation', () => {
|
||||
|
||||
// Verify link exists and has correct href
|
||||
await expect(componentsLink).toBeVisible();
|
||||
await expect(componentsLink).toHaveAttribute('href', '/dev');
|
||||
await expect(componentsLink).toHaveAttribute('href', '/en/dev');
|
||||
|
||||
// Click and wait for navigation
|
||||
await componentsLink.click();
|
||||
await page.waitForURL('/dev', { timeout: 10000 }).catch(() => {});
|
||||
await page.waitForURL('/en/dev', { timeout: 10000 }).catch(() => {});
|
||||
|
||||
// Verify URL (might not navigate if /dev page has issues, that's ok for this test)
|
||||
const currentUrl = page.url();
|
||||
@@ -54,11 +54,11 @@ test.describe('Homepage - Desktop Navigation', () => {
|
||||
|
||||
// Verify link exists and has correct href
|
||||
await expect(adminLink).toBeVisible();
|
||||
await expect(adminLink).toHaveAttribute('href', '/admin');
|
||||
await expect(adminLink).toHaveAttribute('href', '/en/admin');
|
||||
|
||||
// Click and wait for navigation
|
||||
await adminLink.click();
|
||||
await page.waitForURL('/admin', { timeout: 10000 }).catch(() => {});
|
||||
await page.waitForURL('/en/admin', { timeout: 10000 }).catch(() => {});
|
||||
|
||||
// Verify URL (might not navigate if /admin requires auth, that's ok for this test)
|
||||
const currentUrl = page.url();
|
||||
@@ -70,9 +70,9 @@ test.describe('Homepage - Desktop Navigation', () => {
|
||||
const header = page.locator('header').first();
|
||||
const headerLoginLink = header.getByRole('link', { name: /^Login$/i });
|
||||
|
||||
await Promise.all([page.waitForURL('/login'), headerLoginLink.click()]);
|
||||
await Promise.all([page.waitForURL('/en/login'), headerLoginLink.click()]);
|
||||
|
||||
await expect(page).toHaveURL('/login');
|
||||
await expect(page).toHaveURL('/en/login');
|
||||
});
|
||||
|
||||
test.skip('should open demo credentials modal when clicking Try Demo', async ({ page }) => {
|
||||
@@ -113,7 +113,7 @@ test.describe('Homepage - Mobile Menu Interactions', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Set mobile viewport
|
||||
await page.setViewportSize({ width: 375, height: 667 });
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
});
|
||||
|
||||
@@ -146,11 +146,11 @@ test.describe('Homepage - Mobile Menu Interactions', () => {
|
||||
const componentsLink = mobileMenu.getByRole('link', { name: 'Components' });
|
||||
|
||||
// Verify link has correct href
|
||||
await expect(componentsLink).toHaveAttribute('href', '/dev');
|
||||
await expect(componentsLink).toHaveAttribute('href', '/en/dev');
|
||||
|
||||
// Click and wait for navigation
|
||||
await componentsLink.click();
|
||||
await page.waitForURL('/dev', { timeout: 10000 }).catch(() => {});
|
||||
await page.waitForURL('/en/dev', { timeout: 10000 }).catch(() => {});
|
||||
|
||||
// Verify URL (might not navigate if /dev page has issues, that's ok)
|
||||
const currentUrl = page.url();
|
||||
@@ -164,11 +164,11 @@ test.describe('Homepage - Mobile Menu Interactions', () => {
|
||||
const adminLink = mobileMenu.getByRole('link', { name: 'Admin Demo' });
|
||||
|
||||
// Verify link has correct href
|
||||
await expect(adminLink).toHaveAttribute('href', '/admin');
|
||||
await expect(adminLink).toHaveAttribute('href', '/en/admin');
|
||||
|
||||
// Click and wait for navigation
|
||||
await adminLink.click();
|
||||
await page.waitForURL('/admin', { timeout: 10000 }).catch(() => {});
|
||||
await page.waitForURL('/en/admin', { timeout: 10000 }).catch(() => {});
|
||||
|
||||
// Verify URL (might not navigate if /admin requires auth, that's ok)
|
||||
const currentUrl = page.url();
|
||||
@@ -204,9 +204,9 @@ test.describe('Homepage - Mobile Menu Interactions', () => {
|
||||
const loginLink = mobileMenu.getByRole('link', { name: /Login/i });
|
||||
await loginLink.waitFor({ state: 'visible' });
|
||||
|
||||
await Promise.all([page.waitForURL('/login'), loginLink.click()]);
|
||||
await Promise.all([page.waitForURL('/en/login'), loginLink.click()]);
|
||||
|
||||
await expect(page).toHaveURL('/login');
|
||||
await expect(page).toHaveURL('/en/login');
|
||||
});
|
||||
|
||||
test.skip('should close mobile menu when clicking outside', async ({ page }) => {
|
||||
@@ -223,7 +223,7 @@ test.describe('Homepage - Mobile Menu Interactions', () => {
|
||||
|
||||
test.describe('Homepage - Hero Section', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
});
|
||||
|
||||
test('should display main headline', async ({ page }) => {
|
||||
@@ -255,11 +255,11 @@ test.describe('Homepage - Hero Section', () => {
|
||||
const exploreLink = page.getByRole('link', { name: /Explore Components/i }).first();
|
||||
|
||||
// Verify link has correct href
|
||||
await expect(exploreLink).toHaveAttribute('href', '/dev');
|
||||
await expect(exploreLink).toHaveAttribute('href', '/en/dev');
|
||||
|
||||
// Click and try to navigate
|
||||
await exploreLink.click();
|
||||
await page.waitForURL('/dev', { timeout: 10000 }).catch(() => {});
|
||||
await page.waitForURL('/en/dev', { timeout: 10000 }).catch(() => {});
|
||||
|
||||
// Verify URL (flexible to handle auth redirects)
|
||||
const currentUrl = page.url();
|
||||
@@ -269,7 +269,7 @@ test.describe('Homepage - Hero Section', () => {
|
||||
|
||||
test.describe('Homepage - Demo Credentials Modal', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
});
|
||||
|
||||
test.skip('should display regular and admin credentials', async ({ page }) => {
|
||||
@@ -321,9 +321,9 @@ test.describe('Homepage - Demo Credentials Modal', () => {
|
||||
|
||||
const loginLink = dialog.getByRole('link', { name: /Go to Login/i });
|
||||
|
||||
await Promise.all([page.waitForURL('/login'), loginLink.click()]);
|
||||
await Promise.all([page.waitForURL('/en/login'), loginLink.click()]);
|
||||
|
||||
await expect(page).toHaveURL('/login');
|
||||
await expect(page).toHaveURL('/en/login');
|
||||
});
|
||||
|
||||
test.skip('should close modal when clicking close button', async ({ page }) => {
|
||||
@@ -344,7 +344,7 @@ test.describe('Homepage - Demo Credentials Modal', () => {
|
||||
|
||||
test.describe('Homepage - Animated Terminal', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
});
|
||||
|
||||
test('should display terminal section', async ({ page }) => {
|
||||
@@ -387,11 +387,11 @@ test.describe('Homepage - Animated Terminal', () => {
|
||||
const terminalDemoLink = demoLinks.last(); // Last one should be from terminal section
|
||||
|
||||
// Verify link has correct href
|
||||
await expect(terminalDemoLink).toHaveAttribute('href', '/login');
|
||||
await expect(terminalDemoLink).toHaveAttribute('href', '/en/login');
|
||||
|
||||
// Click and try to navigate
|
||||
await terminalDemoLink.click();
|
||||
await page.waitForURL('/login', { timeout: 10000 }).catch(() => {});
|
||||
await page.waitForURL('/en/login', { timeout: 10000 }).catch(() => {});
|
||||
|
||||
// Verify URL (flexible to handle redirects)
|
||||
const currentUrl = page.url();
|
||||
@@ -401,7 +401,7 @@ test.describe('Homepage - Animated Terminal', () => {
|
||||
|
||||
test.describe('Homepage - Feature Sections', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
});
|
||||
|
||||
test('should display feature grid section', async ({ page }) => {
|
||||
@@ -417,11 +417,11 @@ test.describe('Homepage - Feature Sections', () => {
|
||||
const authLink = page.getByRole('link', { name: /View Auth Flow/i });
|
||||
|
||||
// Verify link has correct href
|
||||
await expect(authLink).toHaveAttribute('href', '/login');
|
||||
await expect(authLink).toHaveAttribute('href', '/en/login');
|
||||
|
||||
// Click and try to navigate
|
||||
await authLink.click();
|
||||
await page.waitForURL('/login', { timeout: 10000 }).catch(() => {});
|
||||
await page.waitForURL('/en/login', { timeout: 10000 }).catch(() => {});
|
||||
|
||||
// Verify URL (flexible to handle redirects)
|
||||
const currentUrl = page.url();
|
||||
@@ -432,11 +432,11 @@ test.describe('Homepage - Feature Sections', () => {
|
||||
const adminLink = page.getByRole('link', { name: /Try Admin Panel/i });
|
||||
|
||||
// Verify link has correct href
|
||||
await expect(adminLink).toHaveAttribute('href', '/admin');
|
||||
await expect(adminLink).toHaveAttribute('href', '/en/admin');
|
||||
|
||||
// Click and try to navigate
|
||||
await adminLink.click();
|
||||
await page.waitForURL('/admin', { timeout: 10000 }).catch(() => {});
|
||||
await page.waitForURL('/en/admin', { timeout: 10000 }).catch(() => {});
|
||||
|
||||
// Verify URL (flexible to handle auth redirects)
|
||||
const currentUrl = page.url();
|
||||
@@ -462,7 +462,7 @@ test.describe('Homepage - Feature Sections', () => {
|
||||
|
||||
test.describe('Homepage - Footer', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
});
|
||||
|
||||
test('should display footer with copyright', async ({ page }) => {
|
||||
@@ -475,7 +475,7 @@ test.describe('Homepage - Footer', () => {
|
||||
|
||||
test.describe('Homepage - Accessibility', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
});
|
||||
|
||||
test('should have proper heading hierarchy', async ({ page }) => {
|
||||
|
||||
@@ -17,14 +17,14 @@ test.describe('Settings Navigation', () => {
|
||||
|
||||
test('should navigate from home to settings profile', async ({ page }) => {
|
||||
// Start at home page (auth already cached in storage state)
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
await expect(page).toHaveURL('/');
|
||||
|
||||
// Navigate to settings/profile
|
||||
await page.goto('/settings/profile');
|
||||
await page.goto('/en/settings/profile');
|
||||
|
||||
// Verify navigation successful
|
||||
await expect(page).toHaveURL('/settings/profile');
|
||||
await expect(page).toHaveURL('/en/settings/profile');
|
||||
|
||||
// Verify page loaded - use specific heading selector
|
||||
await expect(page.getByRole('heading', { name: 'Profile' })).toBeVisible();
|
||||
@@ -32,14 +32,14 @@ test.describe('Settings Navigation', () => {
|
||||
|
||||
test('should navigate from home to settings password', async ({ page }) => {
|
||||
// Start at home page (auth already cached in storage state)
|
||||
await page.goto('/');
|
||||
await page.goto('/en');
|
||||
await expect(page).toHaveURL('/');
|
||||
|
||||
// Navigate to settings/password
|
||||
await page.goto('/settings/password');
|
||||
await page.goto('/en/settings/password');
|
||||
|
||||
// Verify navigation successful
|
||||
await expect(page).toHaveURL('/settings/password');
|
||||
await expect(page).toHaveURL('/en/settings/password');
|
||||
|
||||
// Verify page loaded - use specific heading selector
|
||||
await expect(page.getByRole('heading', { name: 'Password' })).toBeVisible();
|
||||
@@ -47,24 +47,24 @@ test.describe('Settings Navigation', () => {
|
||||
|
||||
test('should navigate between settings pages', async ({ page }) => {
|
||||
// Start at profile page
|
||||
await page.goto('/settings/profile');
|
||||
await page.goto('/en/settings/profile');
|
||||
await expect(page.getByRole('heading', { name: 'Profile' })).toBeVisible();
|
||||
|
||||
// Navigate to password page
|
||||
await page.goto('/settings/password');
|
||||
await page.goto('/en/settings/password');
|
||||
await expect(page.getByRole('heading', { name: 'Password' })).toBeVisible();
|
||||
|
||||
// Navigate back to profile page
|
||||
await page.goto('/settings/profile');
|
||||
await page.goto('/en/settings/profile');
|
||||
await expect(page.getByRole('heading', { name: 'Profile' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('should redirect from /settings to /settings/profile', async ({ page }) => {
|
||||
// Navigate to base settings page
|
||||
await page.goto('/settings');
|
||||
await page.goto('/en/settings');
|
||||
|
||||
// Should redirect to profile page
|
||||
await expect(page).toHaveURL('/settings/profile');
|
||||
await expect(page).toHaveURL('/en/settings/profile');
|
||||
|
||||
// Verify profile page loaded - use specific heading selector
|
||||
await expect(page.getByRole('heading', { name: 'Profile' })).toBeVisible();
|
||||
@@ -72,10 +72,10 @@ test.describe('Settings Navigation', () => {
|
||||
|
||||
test('should display preferences page placeholder', async ({ page }) => {
|
||||
// Navigate to preferences page
|
||||
await page.goto('/settings/preferences');
|
||||
await page.goto('/en/settings/preferences');
|
||||
|
||||
// Verify navigation successful
|
||||
await expect(page).toHaveURL('/settings/preferences');
|
||||
await expect(page).toHaveURL('/en/settings/preferences');
|
||||
|
||||
// Verify page loaded with placeholder content
|
||||
await expect(page.getByRole('heading', { name: 'Preferences' })).toBeVisible();
|
||||
|
||||
@@ -15,7 +15,7 @@ test.describe('Password Change', () => {
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
|
||||
// Navigate to password page
|
||||
await page.goto('/settings/password');
|
||||
await page.goto('/en/settings/password');
|
||||
|
||||
// Wait for form to be visible
|
||||
await page.getByLabel(/current password/i).waitFor({ state: 'visible' });
|
||||
|
||||
@@ -15,7 +15,7 @@ test.describe('Profile Settings', () => {
|
||||
// Auth already cached in storage state (loginViaUI removed for performance)
|
||||
|
||||
// Navigate to profile page
|
||||
await page.goto('/settings/profile');
|
||||
await page.goto('/en/settings/profile');
|
||||
|
||||
// Wait for page to render
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
@@ -8,12 +8,12 @@ import { test, expect } from '@playwright/test';
|
||||
test.describe('Theme Toggle on Public Pages', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Clear localStorage before each test
|
||||
await page.goto('/login');
|
||||
await page.goto('/en/login');
|
||||
await page.evaluate(() => localStorage.clear());
|
||||
});
|
||||
|
||||
test('theme is applied on login page', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
await page.goto('/en/login');
|
||||
|
||||
// Wait for page to load and theme to be applied
|
||||
await page.waitForTimeout(500);
|
||||
@@ -27,7 +27,7 @@ test.describe('Theme Toggle on Public Pages', () => {
|
||||
});
|
||||
|
||||
test('theme persists across page navigation', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
await page.goto('/en/login');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Set theme to dark via localStorage
|
||||
@@ -43,14 +43,14 @@ test.describe('Theme Toggle on Public Pages', () => {
|
||||
await expect(page.locator('html')).toHaveClass(/dark/);
|
||||
|
||||
// Navigate to register page
|
||||
await page.goto('/register');
|
||||
await page.goto('/en/register');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Theme should still be dark
|
||||
await expect(page.locator('html')).toHaveClass(/dark/);
|
||||
|
||||
// Navigate to password reset
|
||||
await page.goto('/password-reset');
|
||||
await page.goto('/en/password-reset');
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Theme should still be dark
|
||||
@@ -58,7 +58,7 @@ test.describe('Theme Toggle on Public Pages', () => {
|
||||
});
|
||||
|
||||
test('can switch theme programmatically', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
await page.goto('/en/login');
|
||||
|
||||
// Set to light theme
|
||||
await page.evaluate(() => {
|
||||
|
||||
@@ -10,6 +10,8 @@ const customJestConfig = {
|
||||
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
|
||||
testEnvironment: 'jest-environment-jsdom',
|
||||
moduleNameMapper: {
|
||||
'^next-intl/routing$': '<rootDir>/tests/__mocks__/next-intl-routing.tsx',
|
||||
'^next-intl/navigation$': '<rootDir>/tests/__mocks__/next-intl-navigation.tsx',
|
||||
'^@/(.*)$': '<rootDir>/src/$1',
|
||||
},
|
||||
testMatch: ['<rootDir>/tests/**/*.test.ts', '<rootDir>/tests/**/*.test.tsx'],
|
||||
|
||||
29
frontend/tests/__mocks__/next-intl-navigation.tsx
Normal file
29
frontend/tests/__mocks__/next-intl-navigation.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Mock for next-intl/navigation
|
||||
*/
|
||||
|
||||
// Create shared mock instances that tests can manipulate
|
||||
// Note: next-intl's usePathname returns paths WITHOUT locale prefix
|
||||
export const mockUsePathname = jest.fn(() => '/');
|
||||
export const mockPush = jest.fn();
|
||||
export const mockReplace = jest.fn();
|
||||
export const mockUseRouter = jest.fn(() => ({
|
||||
push: mockPush,
|
||||
replace: mockReplace,
|
||||
prefetch: jest.fn(),
|
||||
back: jest.fn(),
|
||||
forward: jest.fn(),
|
||||
refresh: jest.fn(),
|
||||
}));
|
||||
export const mockRedirect = jest.fn();
|
||||
|
||||
export const createNavigation = (_routing: any) => ({
|
||||
Link: ({ children, href, ...props }: any) => (
|
||||
<a href={href} {...props}>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
redirect: mockRedirect,
|
||||
usePathname: mockUsePathname,
|
||||
useRouter: mockUseRouter,
|
||||
});
|
||||
23
frontend/tests/__mocks__/next-intl-routing.tsx
Normal file
23
frontend/tests/__mocks__/next-intl-routing.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Mock for next-intl/routing
|
||||
*/
|
||||
|
||||
export const defineRouting = (config: any) => config;
|
||||
|
||||
export const createNavigation = (_routing: any) => ({
|
||||
Link: ({ children, href, ...props }: any) => (
|
||||
<a href={href} {...props}>
|
||||
{children}
|
||||
</a>
|
||||
),
|
||||
redirect: jest.fn(),
|
||||
usePathname: () => '/en/test',
|
||||
useRouter: () => ({
|
||||
push: jest.fn(),
|
||||
replace: jest.fn(),
|
||||
prefetch: jest.fn(),
|
||||
back: jest.fn(),
|
||||
forward: jest.fn(),
|
||||
refresh: jest.fn(),
|
||||
}),
|
||||
});
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import LoginPage from '@/app/(auth)/login/page';
|
||||
import LoginPage from '@/app/[locale]/(auth)/login/page';
|
||||
|
||||
// Mock dynamic import
|
||||
jest.mock('next/dynamic', () => ({
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
*/
|
||||
|
||||
import { render, screen, act } from '@testing-library/react';
|
||||
import { useSearchParams, useRouter } from 'next/navigation';
|
||||
import PasswordResetConfirmContent from '@/app/(auth)/password-reset/confirm/PasswordResetConfirmContent';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { useRouter } from '@/lib/i18n/routing';
|
||||
import PasswordResetConfirmContent from '@/app/[locale]/(auth)/password-reset/confirm/PasswordResetConfirmContent';
|
||||
|
||||
// Mock Next.js navigation
|
||||
jest.mock('next/navigation', () => ({
|
||||
useSearchParams: jest.fn(),
|
||||
useRouter: jest.fn(),
|
||||
default: jest.fn(),
|
||||
}));
|
||||
|
||||
// Mock Next.js Link
|
||||
jest.mock('next/link', () => ({
|
||||
__esModule: true,
|
||||
default: ({ children, href }: { children: React.ReactNode; href: string }) => (
|
||||
// Mock i18n routing
|
||||
jest.mock('@/lib/i18n/routing', () => ({
|
||||
useRouter: jest.fn(),
|
||||
Link: ({ children, href }: { children: React.ReactNode; href: string }) => (
|
||||
<a href={href}>{children}</a>
|
||||
),
|
||||
}));
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
*/
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import PasswordResetConfirmPage from '@/app/(auth)/password-reset/confirm/page';
|
||||
import PasswordResetConfirmPage from '@/app/[locale]/(auth)/password-reset/confirm/page';
|
||||
|
||||
// Mock the content component
|
||||
jest.mock('@/app/(auth)/password-reset/confirm/PasswordResetConfirmContent', () => ({
|
||||
jest.mock('@/app/[locale]/(auth)/password-reset/confirm/PasswordResetConfirmContent', () => ({
|
||||
__esModule: true,
|
||||
default: () => <div data-testid="password-reset-confirm-content">Content</div>,
|
||||
}));
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import PasswordResetPage from '@/app/(auth)/password-reset/page';
|
||||
import PasswordResetPage from '@/app/[locale]/(auth)/password-reset/page';
|
||||
|
||||
// Mock dynamic import
|
||||
jest.mock('next/dynamic', () => ({
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import RegisterPage from '@/app/(auth)/register/page';
|
||||
import RegisterPage from '@/app/[locale]/(auth)/register/page';
|
||||
|
||||
// Mock dynamic import
|
||||
jest.mock('next/dynamic', () => ({
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { redirect } from 'next/navigation';
|
||||
import SettingsPage from '@/app/(authenticated)/settings/page';
|
||||
import SettingsPage from '@/app/[locale]/(authenticated)/settings/page';
|
||||
|
||||
// Mock Next.js navigation - redirect throws to interrupt execution
|
||||
jest.mock('next/navigation', () => ({
|
||||
@@ -18,8 +18,9 @@ describe('SettingsPage', () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('redirects to /settings/profile', () => {
|
||||
expect(() => SettingsPage()).toThrow('NEXT_REDIRECT');
|
||||
expect(redirect).toHaveBeenCalledWith('/settings/profile');
|
||||
it('redirects to /settings/profile with locale prefix', async () => {
|
||||
const params = Promise.resolve({ locale: 'en' });
|
||||
await expect(SettingsPage({ params })).rejects.toThrow('NEXT_REDIRECT');
|
||||
expect(redirect).toHaveBeenCalledWith('/en/settings/profile');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import PasswordSettingsPage from '@/app/(authenticated)/settings/password/page';
|
||||
import PasswordSettingsPage from '@/app/[locale]/(authenticated)/settings/password/page';
|
||||
|
||||
describe('PasswordSettingsPage', () => {
|
||||
const queryClient = new QueryClient({
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import PreferencesPage from '@/app/(authenticated)/settings/preferences/page';
|
||||
import PreferencesPage from '@/app/[locale]/(authenticated)/settings/preferences/page';
|
||||
|
||||
describe('PreferencesPage', () => {
|
||||
it('renders page title', () => {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import ProfileSettingsPage from '@/app/(authenticated)/settings/profile/page';
|
||||
import ProfileSettingsPage from '@/app/[locale]/(authenticated)/settings/profile/page';
|
||||
import { AuthProvider } from '@/lib/auth/AuthContext';
|
||||
|
||||
// Mock API hooks
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import SessionsPage from '@/app/(authenticated)/settings/sessions/page';
|
||||
import SessionsPage from '@/app/[locale]/(authenticated)/settings/sessions/page';
|
||||
|
||||
// Mock the API client
|
||||
jest.mock('@/lib/api/client', () => ({
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import AdminLayout from '@/app/admin/layout';
|
||||
import AdminLayout from '@/app/[locale]/admin/layout';
|
||||
import { useAuth } from '@/lib/auth/AuthContext';
|
||||
|
||||
// Mock dependencies
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import OrganizationMembersPage from '@/app/admin/organizations/[id]/members/page';
|
||||
import OrganizationMembersPage from '@/app/[locale]/admin/organizations/[id]/members/page';
|
||||
|
||||
// Mock Next.js Link
|
||||
jest.mock('next/link', () => ({
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import AdminOrganizationsPage from '@/app/admin/organizations/page';
|
||||
import AdminOrganizationsPage from '@/app/[locale]/admin/organizations/page';
|
||||
|
||||
// Mock the entire OrganizationManagementContent component
|
||||
jest.mock('@/components/admin/organizations/OrganizationManagementContent', () => ({
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import AdminPage from '@/app/admin/page';
|
||||
import AdminPage from '@/app/[locale]/admin/page';
|
||||
import { useAdminStats } from '@/lib/api/hooks/useAdmin';
|
||||
|
||||
// Mock the useAdminStats hook
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import AdminSettingsPage from '@/app/admin/settings/page';
|
||||
import AdminSettingsPage from '@/app/[locale]/admin/settings/page';
|
||||
|
||||
describe('AdminSettingsPage', () => {
|
||||
it('renders page title', () => {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import AdminUsersPage from '@/app/admin/users/page';
|
||||
import AdminUsersPage from '@/app/[locale]/admin/users/page';
|
||||
import { useAuth } from '@/lib/auth/AuthContext';
|
||||
import { useAdminUsers } from '@/lib/api/hooks/useAdmin';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { render, screen, within } from '@testing-library/react';
|
||||
import DemoTourPage from '@/app/demos/page';
|
||||
import DemoTourPage from '@/app/[locale]/demos/page';
|
||||
|
||||
// Mock Next.js Link
|
||||
jest.mock('next/link', () => ({
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import ForbiddenPage, { metadata } from '@/app/forbidden/page';
|
||||
import ForbiddenPage, { metadata } from '@/app/[locale]/forbidden/page';
|
||||
|
||||
describe('ForbiddenPage', () => {
|
||||
it('has correct metadata', () => {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { render, screen, within, fireEvent } from '@testing-library/react';
|
||||
import Home from '@/app/page';
|
||||
import Home from '@/app/[locale]/page';
|
||||
|
||||
// Mock Next.js components
|
||||
jest.mock('next/image', () => ({
|
||||
|
||||
@@ -7,7 +7,7 @@ import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { AdminSidebar } from '@/components/admin/AdminSidebar';
|
||||
import { useAuth } from '@/lib/auth/AuthContext';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { mockUsePathname } from 'next-intl/navigation';
|
||||
import type { User } from '@/lib/stores/authStore';
|
||||
|
||||
// Mock dependencies
|
||||
@@ -16,10 +16,6 @@ jest.mock('@/lib/auth/AuthContext', () => ({
|
||||
AuthProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
|
||||
}));
|
||||
|
||||
jest.mock('next/navigation', () => ({
|
||||
usePathname: jest.fn(),
|
||||
}));
|
||||
|
||||
// Helper to create mock user
|
||||
function createMockUser(overrides: Partial<User> = {}): User {
|
||||
return {
|
||||
@@ -39,7 +35,7 @@ function createMockUser(overrides: Partial<User> = {}): User {
|
||||
describe('AdminSidebar', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin');
|
||||
mockUsePathname.mockReturnValue('/admin');
|
||||
(useAuth as unknown as jest.Mock).mockReturnValue({
|
||||
user: createMockUser(),
|
||||
});
|
||||
@@ -97,7 +93,7 @@ describe('AdminSidebar', () => {
|
||||
|
||||
describe('Active State Highlighting', () => {
|
||||
it('highlights dashboard link when on /admin', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin');
|
||||
mockUsePathname.mockReturnValue('/admin');
|
||||
|
||||
render(<AdminSidebar />);
|
||||
|
||||
@@ -106,7 +102,7 @@ describe('AdminSidebar', () => {
|
||||
});
|
||||
|
||||
it('highlights users link when on /admin/users', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users');
|
||||
mockUsePathname.mockReturnValue('/admin/users');
|
||||
|
||||
render(<AdminSidebar />);
|
||||
|
||||
@@ -115,7 +111,7 @@ describe('AdminSidebar', () => {
|
||||
});
|
||||
|
||||
it('highlights users link when on /admin/users/123', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users/123');
|
||||
mockUsePathname.mockReturnValue('/admin/users/123');
|
||||
|
||||
render(<AdminSidebar />);
|
||||
|
||||
@@ -124,7 +120,7 @@ describe('AdminSidebar', () => {
|
||||
});
|
||||
|
||||
it('highlights organizations link when on /admin/organizations', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/organizations');
|
||||
mockUsePathname.mockReturnValue('/admin/organizations');
|
||||
|
||||
render(<AdminSidebar />);
|
||||
|
||||
@@ -133,7 +129,7 @@ describe('AdminSidebar', () => {
|
||||
});
|
||||
|
||||
it('highlights settings link when on /admin/settings', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/settings');
|
||||
mockUsePathname.mockReturnValue('/admin/settings');
|
||||
|
||||
render(<AdminSidebar />);
|
||||
|
||||
@@ -142,7 +138,7 @@ describe('AdminSidebar', () => {
|
||||
});
|
||||
|
||||
it('does not highlight dashboard when on other admin routes', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users');
|
||||
mockUsePathname.mockReturnValue('/admin/users');
|
||||
|
||||
render(<AdminSidebar />);
|
||||
|
||||
|
||||
@@ -5,21 +5,17 @@
|
||||
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { Breadcrumbs } from '@/components/admin/Breadcrumbs';
|
||||
import { usePathname } from 'next/navigation';
|
||||
|
||||
// Mock dependencies
|
||||
jest.mock('next/navigation', () => ({
|
||||
usePathname: jest.fn(),
|
||||
}));
|
||||
import { mockUsePathname } from 'next-intl/navigation';
|
||||
|
||||
describe('Breadcrumbs', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockUsePathname.mockReturnValue('/');
|
||||
});
|
||||
|
||||
describe('Rendering', () => {
|
||||
it('renders breadcrumbs container with correct test id', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin');
|
||||
mockUsePathname.mockReturnValue('/admin');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -27,7 +23,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('renders breadcrumbs with proper aria-label', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin');
|
||||
mockUsePathname.mockReturnValue('/admin');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -36,7 +32,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('returns null for empty pathname', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('');
|
||||
mockUsePathname.mockReturnValue('');
|
||||
|
||||
const { container } = render(<Breadcrumbs />);
|
||||
|
||||
@@ -44,7 +40,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('returns null for root pathname', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/');
|
||||
mockUsePathname.mockReturnValue('/');
|
||||
|
||||
const { container } = render(<Breadcrumbs />);
|
||||
|
||||
@@ -54,7 +50,7 @@ describe('Breadcrumbs', () => {
|
||||
|
||||
describe('Single Level Navigation', () => {
|
||||
it('renders single breadcrumb for /admin', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin');
|
||||
mockUsePathname.mockReturnValue('/admin');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -63,7 +59,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('renders current page without link', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin');
|
||||
mockUsePathname.mockReturnValue('/admin');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -75,7 +71,7 @@ describe('Breadcrumbs', () => {
|
||||
|
||||
describe('Multi-Level Navigation', () => {
|
||||
it('renders breadcrumbs for /admin/users', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users');
|
||||
mockUsePathname.mockReturnValue('/admin/users');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -84,7 +80,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('renders parent breadcrumbs as links', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users');
|
||||
mockUsePathname.mockReturnValue('/admin/users');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -94,7 +90,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('renders last breadcrumb as current page', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users');
|
||||
mockUsePathname.mockReturnValue('/admin/users');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -104,7 +100,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('renders breadcrumbs for /admin/organizations', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/organizations');
|
||||
mockUsePathname.mockReturnValue('/admin/organizations');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -113,7 +109,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('renders breadcrumbs for /admin/settings', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/settings');
|
||||
mockUsePathname.mockReturnValue('/admin/settings');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -124,7 +120,7 @@ describe('Breadcrumbs', () => {
|
||||
|
||||
describe('Three-Level Navigation', () => {
|
||||
it('renders all levels correctly', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users/123');
|
||||
mockUsePathname.mockReturnValue('/admin/users/123');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -134,7 +130,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('renders all parent links correctly', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users/123');
|
||||
mockUsePathname.mockReturnValue('/admin/users/123');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -146,7 +142,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('renders last level as current page', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users/123');
|
||||
mockUsePathname.mockReturnValue('/admin/users/123');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -158,7 +154,7 @@ describe('Breadcrumbs', () => {
|
||||
|
||||
describe('Separator Icons', () => {
|
||||
it('renders separator between breadcrumbs', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users');
|
||||
mockUsePathname.mockReturnValue('/admin/users');
|
||||
|
||||
const { container } = render(<Breadcrumbs />);
|
||||
|
||||
@@ -168,7 +164,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('does not render separator before first breadcrumb', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin');
|
||||
mockUsePathname.mockReturnValue('/admin');
|
||||
|
||||
const { container } = render(<Breadcrumbs />);
|
||||
|
||||
@@ -178,7 +174,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('renders correct number of separators', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users/123');
|
||||
mockUsePathname.mockReturnValue('/admin/users/123');
|
||||
|
||||
const { container } = render(<Breadcrumbs />);
|
||||
|
||||
@@ -190,7 +186,7 @@ describe('Breadcrumbs', () => {
|
||||
|
||||
describe('Label Mapping', () => {
|
||||
it('uses predefined label for admin', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin');
|
||||
mockUsePathname.mockReturnValue('/admin');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -198,7 +194,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('uses predefined label for users', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users');
|
||||
mockUsePathname.mockReturnValue('/admin/users');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -206,7 +202,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('uses predefined label for organizations', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/organizations');
|
||||
mockUsePathname.mockReturnValue('/admin/organizations');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -214,7 +210,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('uses predefined label for settings', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/settings');
|
||||
mockUsePathname.mockReturnValue('/admin/settings');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -222,7 +218,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('uses pathname segment for unmapped paths', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/unknown-path');
|
||||
mockUsePathname.mockReturnValue('/admin/unknown-path');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -230,7 +226,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('displays numeric IDs as-is', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users/123');
|
||||
mockUsePathname.mockReturnValue('/admin/users/123');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -240,7 +236,7 @@ describe('Breadcrumbs', () => {
|
||||
|
||||
describe('Styling', () => {
|
||||
it('applies correct styles to parent links', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users');
|
||||
mockUsePathname.mockReturnValue('/admin/users');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -250,7 +246,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('applies correct styles to current page', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users');
|
||||
mockUsePathname.mockReturnValue('/admin/users');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -262,7 +258,7 @@ describe('Breadcrumbs', () => {
|
||||
|
||||
describe('Accessibility', () => {
|
||||
it('has proper navigation role', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin');
|
||||
mockUsePathname.mockReturnValue('/admin');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -270,7 +266,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('has aria-label for navigation', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin');
|
||||
mockUsePathname.mockReturnValue('/admin');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -279,7 +275,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('marks current page with aria-current', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users');
|
||||
mockUsePathname.mockReturnValue('/admin/users');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
@@ -288,7 +284,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('marks separator icons as aria-hidden', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users');
|
||||
mockUsePathname.mockReturnValue('/admin/users');
|
||||
|
||||
const { container } = render(<Breadcrumbs />);
|
||||
|
||||
@@ -299,7 +295,7 @@ describe('Breadcrumbs', () => {
|
||||
});
|
||||
|
||||
it('parent breadcrumbs are keyboard accessible', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin/users');
|
||||
mockUsePathname.mockReturnValue('/admin/users');
|
||||
|
||||
render(<Breadcrumbs />);
|
||||
|
||||
|
||||
@@ -5,18 +5,17 @@
|
||||
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { OrganizationManagementContent } from '@/components/admin/organizations/OrganizationManagementContent';
|
||||
import { useAuth } from '@/lib/auth/AuthContext';
|
||||
import { useAdminOrganizations } from '@/lib/api/hooks/useAdmin';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { mockPush } from 'next-intl/navigation';
|
||||
|
||||
// Mock Next.js navigation
|
||||
const mockPush = jest.fn();
|
||||
const mockSearchParams = new URLSearchParams();
|
||||
|
||||
jest.mock('next/navigation', () => ({
|
||||
useRouter: jest.fn(),
|
||||
useSearchParams: jest.fn(),
|
||||
}));
|
||||
|
||||
@@ -52,7 +51,6 @@ jest.mock('@/components/admin/organizations/OrganizationFormDialog', () => ({
|
||||
) : null,
|
||||
}));
|
||||
|
||||
const mockUseRouter = useRouter as jest.MockedFunction<typeof useRouter>;
|
||||
const mockUseSearchParams = useSearchParams as jest.MockedFunction<typeof useSearchParams>;
|
||||
const mockUseAuth = useAuth as jest.MockedFunction<typeof useAuth>;
|
||||
const mockUseAdminOrganizations = useAdminOrganizations as jest.MockedFunction<
|
||||
@@ -101,12 +99,6 @@ describe('OrganizationManagementContent', () => {
|
||||
|
||||
jest.clearAllMocks();
|
||||
|
||||
mockUseRouter.mockReturnValue({
|
||||
push: mockPush,
|
||||
replace: jest.fn(),
|
||||
prefetch: jest.fn(),
|
||||
} as any);
|
||||
|
||||
mockUseSearchParams.mockReturnValue(mockSearchParams as any);
|
||||
|
||||
mockUseAuth.mockReturnValue({
|
||||
|
||||
@@ -6,17 +6,7 @@
|
||||
import { render, screen, waitFor, act } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { AuthGuard } from '@/components/auth/AuthGuard';
|
||||
|
||||
// Mock Next.js navigation
|
||||
const mockPush = jest.fn();
|
||||
const mockPathname = '/protected';
|
||||
|
||||
jest.mock('next/navigation', () => ({
|
||||
useRouter: () => ({
|
||||
push: mockPush,
|
||||
}),
|
||||
usePathname: () => mockPathname,
|
||||
}));
|
||||
import { mockUsePathname, mockPush } from 'next-intl/navigation';
|
||||
|
||||
// Mock auth state via Context
|
||||
let mockAuthState: {
|
||||
@@ -64,6 +54,10 @@ describe('AuthGuard', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
jest.useFakeTimers();
|
||||
|
||||
// Configure pathname mock
|
||||
mockUsePathname.mockReturnValue('/protected');
|
||||
|
||||
// Reset to default unauthenticated state
|
||||
mockAuthState = {
|
||||
isAuthenticated: false,
|
||||
|
||||
@@ -8,7 +8,7 @@ import userEvent from '@testing-library/user-event';
|
||||
import { Header } from '@/components/layout/Header';
|
||||
import { useAuth } from '@/lib/auth/AuthContext';
|
||||
import { useLogout } from '@/lib/api/hooks/useAuth';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { mockUsePathname } from 'next-intl/navigation';
|
||||
import type { User } from '@/lib/stores/authStore';
|
||||
|
||||
// Mock dependencies
|
||||
@@ -21,10 +21,6 @@ jest.mock('@/lib/api/hooks/useAuth', () => ({
|
||||
useLogout: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('next/navigation', () => ({
|
||||
usePathname: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@/components/theme', () => ({
|
||||
ThemeToggle: () => <div data-testid="theme-toggle">Theme Toggle</div>,
|
||||
}));
|
||||
@@ -51,7 +47,7 @@ describe('Header', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
(usePathname as jest.Mock).mockReturnValue('/');
|
||||
mockUsePathname.mockReturnValue('/');
|
||||
|
||||
(useLogout as jest.Mock).mockReturnValue({
|
||||
mutate: mockLogout,
|
||||
@@ -156,7 +152,7 @@ describe('Header', () => {
|
||||
});
|
||||
|
||||
it('highlights active navigation link', () => {
|
||||
(usePathname as jest.Mock).mockReturnValue('/admin');
|
||||
mockUsePathname.mockReturnValue('/admin');
|
||||
(useAuth as unknown as jest.Mock).mockReturnValue({
|
||||
user: createMockUser({ is_superuser: true }),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user