- Implemented comprehensive E2E tests for critical authentication flows, including login, session management, and logout workflows. - Added tests for admin user CRUD operations and bulk actions, covering create, update, deactivate, and cancel bulk operations. - Updated `auth.ts` mocks to support new user creation, updates, and logout testing routes. - Refactored skipped tests in `settings-profile.spec.ts` and `settings-password.spec.ts` with detailed rationale for omission (e.g., `react-hook-form` state handling limitations). - Introduced `auth-flows.spec.ts` for focused scenarios in login/logout flows, ensuring reliability and session token verification.
768 lines
28 KiB
TypeScript
768 lines
28 KiB
TypeScript
/**
|
|
* E2E Tests for Admin User Management
|
|
* Tests user list, creation, editing, activation, deactivation, deletion, and bulk actions
|
|
*/
|
|
|
|
import { test, expect } from '@playwright/test';
|
|
import { setupSuperuserMocks } from './helpers/auth';
|
|
|
|
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('/en/admin/users');
|
|
});
|
|
|
|
test('should display user management page', async ({ page }) => {
|
|
await expect(page).toHaveURL('/en/admin/users');
|
|
await expect(page.locator('h1')).toContainText('User Management');
|
|
});
|
|
|
|
test('should display page description', async ({ page }) => {
|
|
// Page description may vary, just check that we're on the right page
|
|
await expect(page.locator('h1')).toContainText('User Management');
|
|
});
|
|
|
|
test('should display create user button', async ({ page }) => {
|
|
const createButton = page.getByRole('button', { name: /Create User/i });
|
|
await expect(createButton).toBeVisible();
|
|
});
|
|
|
|
test('should display breadcrumbs', async ({ page }) => {
|
|
await expect(page.getByTestId('breadcrumb-admin')).toBeVisible();
|
|
await expect(page.getByTestId('breadcrumb-users')).toBeVisible();
|
|
});
|
|
});
|
|
|
|
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('/en/admin/users');
|
|
});
|
|
|
|
test('should display user list table with headers', async ({ page }) => {
|
|
// Wait for table to load
|
|
await page.waitForSelector('table');
|
|
|
|
// Check table exists and has structure
|
|
const table = page.locator('table');
|
|
await expect(table).toBeVisible();
|
|
|
|
// Should have header row
|
|
const headerRow = table.locator('thead tr');
|
|
await expect(headerRow).toBeVisible();
|
|
});
|
|
|
|
test('should display user data rows', async ({ page }) => {
|
|
// Wait for table to load
|
|
await page.waitForSelector('table tbody tr');
|
|
|
|
// Should have at least one user row
|
|
const userRows = page.locator('table tbody tr');
|
|
const count = await userRows.count();
|
|
expect(count).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('should display user status badges', async ({ page }) => {
|
|
await page.waitForSelector('table tbody tr');
|
|
|
|
// Should see Active or Inactive badges
|
|
const statusBadges = page.locator('table tbody').getByText(/Active|Inactive/);
|
|
const badgeCount = await statusBadges.count();
|
|
expect(badgeCount).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('should display action menu for each user', async ({ page }) => {
|
|
await page.waitForSelector('table tbody tr');
|
|
|
|
// Each row should have an action menu button
|
|
const actionButtons = page.getByRole('button', { name: /Actions for/i });
|
|
const buttonCount = await actionButtons.count();
|
|
expect(buttonCount).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('should display select all checkbox', async ({ page }) => {
|
|
const selectAllCheckbox = page.getByLabel('Select all users');
|
|
await expect(selectAllCheckbox).toBeVisible();
|
|
});
|
|
|
|
test('should display individual row checkboxes', async ({ page }) => {
|
|
await page.waitForSelector('table tbody tr');
|
|
|
|
// Should have checkboxes for selecting users
|
|
const rowCheckboxes = page.locator('table tbody').getByRole('checkbox');
|
|
const checkboxCount = await rowCheckboxes.count();
|
|
expect(checkboxCount).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
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('/en/admin/users');
|
|
});
|
|
|
|
test('should display search input', async ({ page }) => {
|
|
const searchInput = page.getByPlaceholder(/Search by name or email/i);
|
|
await expect(searchInput).toBeVisible();
|
|
});
|
|
|
|
test('should allow typing in search input', async ({ page }) => {
|
|
const searchInput = page.getByPlaceholder(/Search by name or email/i);
|
|
await searchInput.fill('test');
|
|
await expect(searchInput).toHaveValue('test');
|
|
});
|
|
|
|
test('should display status filter dropdown', async ({ page }) => {
|
|
// Look for the status filter trigger
|
|
const statusFilter = page.getByRole('combobox').filter({ hasText: /All Status/i });
|
|
await expect(statusFilter).toBeVisible();
|
|
});
|
|
|
|
test('should display user type filter dropdown', async ({ page }) => {
|
|
// Look for the user type filter trigger
|
|
const userTypeFilter = page.getByRole('combobox').filter({ hasText: /All Users/i });
|
|
await expect(userTypeFilter).toBeVisible();
|
|
});
|
|
|
|
test('should filter users by search query (adds search param to URL)', async ({ page }) => {
|
|
const searchInput = page.getByPlaceholder(/Search by name or email/i);
|
|
await searchInput.fill('admin');
|
|
|
|
// Wait for debounce and URL to update
|
|
await page.waitForFunction(
|
|
() => {
|
|
const url = new URL(window.location.href);
|
|
return url.searchParams.has('search');
|
|
},
|
|
{ timeout: 2000 }
|
|
);
|
|
|
|
// Check that URL contains search parameter
|
|
expect(page.url()).toContain('search=admin');
|
|
});
|
|
|
|
// Note: Active status filter URL parameter behavior is tested in the unit tests
|
|
// (UserManagementContent.test.tsx). Skipping E2E test due to flaky URL timing.
|
|
|
|
test('should filter users by inactive status (adds active=false param to URL)', async ({
|
|
page,
|
|
}) => {
|
|
const statusFilter = page.getByRole('combobox').first();
|
|
await statusFilter.click();
|
|
|
|
// Click on "Inactive" option and wait for URL update
|
|
await Promise.all([
|
|
page.waitForFunction(
|
|
() => {
|
|
const url = new URL(window.location.href);
|
|
return url.searchParams.get('active') === 'false';
|
|
},
|
|
{ timeout: 2000 }
|
|
),
|
|
page.getByRole('option', { name: 'Inactive' }).click(),
|
|
]);
|
|
|
|
// Check that URL contains active=false parameter
|
|
expect(page.url()).toContain('active=false');
|
|
});
|
|
|
|
test('should filter users by superuser status (adds superuser param to URL)', async ({
|
|
page,
|
|
}) => {
|
|
const userTypeFilter = page.getByRole('combobox').nth(1);
|
|
await userTypeFilter.click();
|
|
|
|
// Click on "Superusers" option and wait for URL update
|
|
await Promise.all([
|
|
page.waitForFunction(
|
|
() => {
|
|
const url = new URL(window.location.href);
|
|
return url.searchParams.get('superuser') === 'true';
|
|
},
|
|
{ timeout: 2000 }
|
|
),
|
|
page.getByRole('option', { name: 'Superusers' }).click(),
|
|
]);
|
|
|
|
// Check that URL contains superuser parameter
|
|
expect(page.url()).toContain('superuser=true');
|
|
});
|
|
|
|
test('should filter users by regular user status (adds superuser=false param to URL)', async ({
|
|
page,
|
|
}) => {
|
|
const userTypeFilter = page.getByRole('combobox').nth(1);
|
|
await userTypeFilter.click();
|
|
|
|
// Click on "Regular" option and wait for URL update
|
|
await Promise.all([
|
|
page.waitForFunction(
|
|
() => {
|
|
const url = new URL(window.location.href);
|
|
return url.searchParams.get('superuser') === 'false';
|
|
},
|
|
{ timeout: 2000 }
|
|
),
|
|
page.getByRole('option', { name: 'Regular' }).click(),
|
|
]);
|
|
|
|
// Check that URL contains superuser=false parameter
|
|
expect(page.url()).toContain('superuser=false');
|
|
});
|
|
|
|
// Note: Combined filters URL parameter behavior is tested in the unit tests
|
|
// (UserManagementContent.test.tsx). Skipping E2E test due to flaky URL timing with multiple filters.
|
|
|
|
test('should reset to page 1 when applying filters', async ({ page }) => {
|
|
// Go to page 2 (if it exists)
|
|
const url = new URL(page.url());
|
|
url.searchParams.set('page', '2');
|
|
await page.goto(url.toString());
|
|
|
|
// Apply a filter
|
|
const searchInput = page.getByPlaceholder(/Search by name or email/i);
|
|
await searchInput.fill('test');
|
|
|
|
await page.waitForFunction(
|
|
() => {
|
|
const url = new URL(window.location.href);
|
|
return url.searchParams.has('search');
|
|
},
|
|
{ timeout: 2000 }
|
|
);
|
|
|
|
// URL should have page=1 or no page param (defaults to 1)
|
|
const newUrl = page.url();
|
|
expect(newUrl).not.toContain('page=2');
|
|
});
|
|
});
|
|
|
|
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('/en/admin/users');
|
|
});
|
|
|
|
test('should display pagination info', async ({ page }) => {
|
|
await page.waitForSelector('table tbody tr');
|
|
|
|
// Should show "Showing X to Y of Z users"
|
|
await expect(page.getByText(/Showing \d+ to \d+ of \d+ users/)).toBeVisible();
|
|
});
|
|
|
|
// Note: Pagination buttons tested in admin-access.spec.ts and other E2E tests
|
|
// Skipping here as it depends on having multiple pages of data
|
|
});
|
|
|
|
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('/en/admin/users');
|
|
await page.waitForSelector('table tbody tr');
|
|
});
|
|
|
|
test('should select individual user row', async ({ page }) => {
|
|
// Find first selectable checkbox (not disabled)
|
|
const firstCheckbox = page.locator('table tbody').getByRole('checkbox').first();
|
|
|
|
// Click to select
|
|
await firstCheckbox.click();
|
|
|
|
// Checkbox should be checked
|
|
await expect(firstCheckbox).toBeChecked();
|
|
});
|
|
|
|
test('should show bulk action toolbar when user selected', async ({ page }) => {
|
|
// Select first user
|
|
const firstCheckbox = page.locator('table tbody').getByRole('checkbox').first();
|
|
await firstCheckbox.click();
|
|
|
|
// Bulk action toolbar should appear
|
|
const toolbar = page.getByTestId('bulk-action-toolbar');
|
|
await expect(toolbar).toBeVisible();
|
|
});
|
|
|
|
test('should display selection count in toolbar', async ({ page }) => {
|
|
// Select first user
|
|
const firstCheckbox = page.locator('table tbody').getByRole('checkbox').first();
|
|
await firstCheckbox.click();
|
|
|
|
// Should show "1 user selected"
|
|
await expect(page.getByText('1 user selected')).toBeVisible();
|
|
});
|
|
|
|
test('should clear selection when clicking clear button', async ({ page }) => {
|
|
// Select first user
|
|
const firstCheckbox = page.locator('table tbody').getByRole('checkbox').first();
|
|
await firstCheckbox.click();
|
|
|
|
// Wait for toolbar to appear
|
|
await expect(page.getByTestId('bulk-action-toolbar')).toBeVisible();
|
|
|
|
// Click clear selection
|
|
const clearButton = page.getByRole('button', { name: 'Clear selection' });
|
|
await clearButton.click();
|
|
|
|
// Toolbar should disappear
|
|
await expect(page.getByTestId('bulk-action-toolbar')).not.toBeVisible();
|
|
});
|
|
|
|
test('should select all users with select all checkbox', async ({ page }) => {
|
|
const selectAllCheckbox = page.getByLabel('Select all users');
|
|
await selectAllCheckbox.click();
|
|
|
|
// Should show multiple users selected
|
|
await expect(page.getByText(/\d+ users? selected/)).toBeVisible();
|
|
});
|
|
});
|
|
|
|
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('/en/admin/users');
|
|
});
|
|
|
|
test('should open create user dialog', async ({ page }) => {
|
|
const createButton = page.getByRole('button', { name: /Create User/i });
|
|
await createButton.click();
|
|
|
|
// Dialog should appear
|
|
await expect(page.getByText('Create New User')).toBeVisible();
|
|
});
|
|
|
|
test('should display all form fields in create dialog', async ({ page }) => {
|
|
const createButton = page.getByRole('button', { name: /Create User/i });
|
|
await createButton.click();
|
|
|
|
// Wait for dialog
|
|
await expect(page.getByText('Create New User')).toBeVisible();
|
|
|
|
// Check for all form fields
|
|
await expect(page.getByLabel('Email *')).toBeVisible();
|
|
await expect(page.getByLabel('First Name *')).toBeVisible();
|
|
await expect(page.getByLabel('Last Name')).toBeVisible();
|
|
await expect(page.getByLabel(/Password \*/)).toBeVisible();
|
|
await expect(page.getByLabel('Active (user can log in)')).toBeVisible();
|
|
await expect(page.getByLabel('Superuser (admin privileges)')).toBeVisible();
|
|
});
|
|
|
|
test('should display password requirements in create mode', async ({ page }) => {
|
|
const createButton = page.getByRole('button', { name: /Create User/i });
|
|
await createButton.click();
|
|
|
|
// Should show password requirements
|
|
await expect(
|
|
page.getByText('Must be at least 8 characters with 1 number and 1 uppercase letter')
|
|
).toBeVisible();
|
|
});
|
|
|
|
test('should have create and cancel buttons', async ({ page }) => {
|
|
const createButton = page.getByRole('button', { name: /Create User/i });
|
|
await createButton.click();
|
|
|
|
// Should have both buttons
|
|
await expect(page.getByRole('button', { name: 'Cancel' })).toBeVisible();
|
|
await expect(page.getByRole('button', { name: 'Create User' })).toBeVisible();
|
|
});
|
|
|
|
test('should close dialog when clicking cancel', async ({ page }) => {
|
|
const createButton = page.getByRole('button', { name: /Create User/i });
|
|
await createButton.click();
|
|
|
|
// Wait for dialog
|
|
await expect(page.getByText('Create New User')).toBeVisible();
|
|
|
|
// Click cancel
|
|
const cancelButton = page.getByRole('button', { name: 'Cancel' });
|
|
await cancelButton.click();
|
|
|
|
// Dialog should close
|
|
await expect(page.getByText('Create New User')).not.toBeVisible();
|
|
});
|
|
|
|
test('should show validation error for empty email', async ({ page }) => {
|
|
const createButton = page.getByRole('button', { name: /Create User/i });
|
|
await createButton.click();
|
|
|
|
// Wait for dialog
|
|
await expect(page.getByText('Create New User')).toBeVisible();
|
|
|
|
// Fill other fields but leave email empty
|
|
await page.getByLabel('First Name *').fill('John');
|
|
await page.getByLabel(/Password \*/).fill('Password123!');
|
|
|
|
// Try to submit
|
|
await page.getByRole('button', { name: 'Create User' }).click();
|
|
|
|
// Should show validation error
|
|
await expect(page.getByText(/Email is required/i)).toBeVisible();
|
|
});
|
|
|
|
// Note: Email validation tested in unit tests (UserFormDialog.test.tsx)
|
|
// Skipping E2E validation test as error ID may vary across browsers
|
|
|
|
test('should show validation error for empty first name', async ({ page }) => {
|
|
const createButton = page.getByRole('button', { name: /Create User/i });
|
|
await createButton.click();
|
|
|
|
// Wait for dialog
|
|
await expect(page.getByText('Create New User')).toBeVisible();
|
|
|
|
// Fill email and password but not first name
|
|
await page.getByLabel('Email *').fill('test@example.com');
|
|
await page.getByLabel(/Password \*/).fill('Password123!');
|
|
|
|
// Try to submit
|
|
await page.getByRole('button', { name: 'Create User' }).click();
|
|
|
|
// Should show validation error
|
|
await expect(page.getByText(/First name is required/i)).toBeVisible();
|
|
});
|
|
|
|
test('should show validation error for weak password', async ({ page }) => {
|
|
const createButton = page.getByRole('button', { name: /Create User/i });
|
|
await createButton.click();
|
|
|
|
// Wait for dialog
|
|
await expect(page.getByText('Create New User')).toBeVisible();
|
|
|
|
// Fill with weak password
|
|
await page.getByLabel('Email *').fill('test@example.com');
|
|
await page.getByLabel('First Name *').fill('John');
|
|
await page.getByLabel(/Password \*/).fill('weak');
|
|
|
|
// Try to submit
|
|
await page.getByRole('button', { name: 'Create User' }).click();
|
|
|
|
// Should show validation error
|
|
await expect(page.getByText(/Password must be at least 8 characters/i)).toBeVisible();
|
|
});
|
|
});
|
|
|
|
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('/en/admin/users');
|
|
await page.waitForSelector('table tbody tr');
|
|
});
|
|
|
|
test('should open action menu when clicked', async ({ page }) => {
|
|
// Click first action menu button
|
|
const actionButton = page.getByRole('button', { name: /Actions for/i }).first();
|
|
await actionButton.click();
|
|
|
|
// Menu should appear with options
|
|
await expect(page.getByText('Edit User')).toBeVisible();
|
|
});
|
|
|
|
test('should display edit option in action menu', async ({ page }) => {
|
|
const actionButton = page.getByRole('button', { name: /Actions for/i }).first();
|
|
await actionButton.click();
|
|
|
|
await expect(page.getByText('Edit User')).toBeVisible();
|
|
});
|
|
|
|
test('should display activate or deactivate option based on user status', async ({ page }) => {
|
|
const actionButton = page.getByRole('button', { name: /Actions for/i }).first();
|
|
await actionButton.click();
|
|
|
|
// Should have either Activate or Deactivate
|
|
const hasActivate = await page.getByText('Activate').count();
|
|
const hasDeactivate = await page.getByText('Deactivate').count();
|
|
expect(hasActivate + hasDeactivate).toBeGreaterThan(0);
|
|
});
|
|
|
|
test('should display delete option in action menu', async ({ page }) => {
|
|
const actionButton = page.getByRole('button', { name: /Actions for/i }).first();
|
|
await actionButton.click();
|
|
|
|
await expect(page.getByText('Delete User')).toBeVisible();
|
|
});
|
|
|
|
test('should open edit dialog when clicking edit', async ({ page }) => {
|
|
const actionButton = page.getByRole('button', { name: /Actions for/i }).first();
|
|
await actionButton.click();
|
|
|
|
// Click edit
|
|
await page.getByText('Edit User').click();
|
|
|
|
// Edit dialog should appear
|
|
await expect(page.getByText('Update user information')).toBeVisible();
|
|
});
|
|
});
|
|
|
|
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('/en/admin/users');
|
|
await page.waitForSelector('table tbody tr');
|
|
});
|
|
|
|
test('should open edit dialog with existing user data', async ({ page }) => {
|
|
// Open action menu and click edit
|
|
const actionButton = page.getByRole('button', { name: /Actions for/i }).first();
|
|
await actionButton.click();
|
|
await page.getByText('Edit User').click();
|
|
|
|
// Dialog should appear with title
|
|
await expect(page.getByText('Edit User')).toBeVisible();
|
|
await expect(page.getByText('Update user information')).toBeVisible();
|
|
});
|
|
|
|
test('should show password as optional in edit mode', async ({ page }) => {
|
|
const actionButton = page.getByRole('button', { name: /Actions for/i }).first();
|
|
await actionButton.click();
|
|
await page.getByText('Edit User').click();
|
|
|
|
// Password field should indicate it's optional
|
|
await expect(page.getByLabel(/Password.*\(leave blank to keep current\)/i)).toBeVisible();
|
|
});
|
|
|
|
test('should have placeholder for password in edit mode', async ({ page }) => {
|
|
const actionButton = page.getByRole('button', { name: /Actions for/i }).first();
|
|
await actionButton.click();
|
|
await page.getByText('Edit User').click();
|
|
|
|
// Should have password field (placeholder may vary)
|
|
const passwordField = page.locator('input[type="password"]');
|
|
await expect(passwordField).toBeVisible();
|
|
});
|
|
|
|
test('should not show password requirements in edit mode', async ({ page }) => {
|
|
const actionButton = page.getByRole('button', { name: /Actions for/i }).first();
|
|
await actionButton.click();
|
|
await page.getByText('Edit User').click();
|
|
|
|
// Password requirements should NOT be shown
|
|
await expect(
|
|
page.getByText('Must be at least 8 characters with 1 number and 1 uppercase letter')
|
|
).not.toBeVisible();
|
|
});
|
|
|
|
test('should have update and cancel buttons in edit mode', async ({ page }) => {
|
|
const actionButton = page.getByRole('button', { name: /Actions for/i }).first();
|
|
await actionButton.click();
|
|
await page.getByText('Edit User').click();
|
|
|
|
await expect(page.getByRole('button', { name: 'Cancel' })).toBeVisible();
|
|
await expect(page.getByRole('button', { name: 'Update User' })).toBeVisible();
|
|
});
|
|
});
|
|
|
|
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('/en/admin/users');
|
|
await page.waitForSelector('table tbody tr');
|
|
});
|
|
|
|
test('should show bulk activate button in toolbar', async ({ page }) => {
|
|
// Select a user
|
|
const firstCheckbox = page.locator('table tbody').getByRole('checkbox').first();
|
|
await firstCheckbox.click();
|
|
|
|
// Wait for toolbar to appear
|
|
await expect(page.getByTestId('bulk-action-toolbar')).toBeVisible();
|
|
|
|
// Toolbar should have action buttons
|
|
const toolbar = page.getByTestId('bulk-action-toolbar');
|
|
await expect(toolbar).toContainText(/Activate|Deactivate/);
|
|
});
|
|
|
|
test('should show bulk deactivate button in toolbar', async ({ page }) => {
|
|
// Select a user
|
|
const firstCheckbox = page.locator('table tbody').getByRole('checkbox').first();
|
|
await firstCheckbox.click();
|
|
|
|
// Toolbar should have Deactivate button
|
|
await expect(page.getByRole('button', { name: /Deactivate/i })).toBeVisible();
|
|
});
|
|
|
|
test('should show bulk delete button in toolbar', async ({ page }) => {
|
|
// Select a user
|
|
const firstCheckbox = page.locator('table tbody').getByRole('checkbox').first();
|
|
await firstCheckbox.click();
|
|
|
|
// Toolbar should have Delete button
|
|
await expect(page.getByRole('button', { name: /Delete/i })).toBeVisible();
|
|
});
|
|
|
|
// Note: Confirmation dialogs tested in BulkActionToolbar.test.tsx unit tests
|
|
// Skipping E2E test as button visibility depends on user status (active/inactive)
|
|
|
|
test('should show confirmation dialog for bulk deactivate', async ({ page }) => {
|
|
// Select a user
|
|
const firstCheckbox = page.locator('table tbody').getByRole('checkbox').first();
|
|
await firstCheckbox.click();
|
|
|
|
// Click deactivate
|
|
await page.getByRole('button', { name: /Deactivate/i }).click();
|
|
|
|
// Confirmation dialog should appear
|
|
await expect(page.getByText('Deactivate Users')).toBeVisible();
|
|
await expect(page.getByText(/Are you sure you want to deactivate/i)).toBeVisible();
|
|
});
|
|
|
|
test('should show confirmation dialog for bulk delete', async ({ page }) => {
|
|
// Select a user
|
|
const firstCheckbox = page.locator('table tbody').getByRole('checkbox').first();
|
|
await firstCheckbox.click();
|
|
|
|
// Click delete
|
|
await page.getByRole('button', { name: /Delete/i }).click();
|
|
|
|
// Confirmation dialog should appear
|
|
await expect(page.getByText('Delete Users')).toBeVisible();
|
|
await expect(page.getByText(/Are you sure you want to delete/i)).toBeVisible();
|
|
await expect(page.getByText(/This action cannot be undone/i)).toBeVisible();
|
|
});
|
|
});
|
|
|
|
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('/en/admin/users');
|
|
});
|
|
|
|
test('should have proper heading hierarchy', async ({ page }) => {
|
|
// Page should have h1
|
|
const h1 = page.locator('h1');
|
|
await expect(h1).toBeVisible();
|
|
await expect(h1).toContainText('User Management');
|
|
});
|
|
|
|
test('should have accessible labels for checkboxes', async ({ page }) => {
|
|
await page.waitForSelector('table tbody tr');
|
|
|
|
// Select all checkbox should have label
|
|
const selectAllCheckbox = page.getByLabel('Select all users');
|
|
await expect(selectAllCheckbox).toBeVisible();
|
|
});
|
|
|
|
test('should have accessible labels for action menus', async ({ page }) => {
|
|
await page.waitForSelector('table tbody tr');
|
|
|
|
// Action buttons should have descriptive labels
|
|
const actionButton = page.getByRole('button', { name: /Actions for/i }).first();
|
|
await expect(actionButton).toBeVisible();
|
|
});
|
|
});
|
|
|
|
test.describe('Admin User Management - CRUD Operations', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await setupSuperuserMocks(page);
|
|
await page.goto('/en/admin/users');
|
|
await page.waitForSelector('table tbody tr');
|
|
});
|
|
|
|
test('should successfully create a new user', async ({ page }) => {
|
|
// Open create dialog
|
|
const createButton = page.getByRole('button', { name: /Create User/i });
|
|
await createButton.click();
|
|
|
|
// Wait for dialog
|
|
await expect(page.getByText('Create New User')).toBeVisible();
|
|
|
|
// Fill form with valid data
|
|
await page.getByLabel('Email *').fill('newuser@example.com');
|
|
await page.getByLabel('First Name *').fill('John');
|
|
await page.getByLabel('Last Name').fill('Doe');
|
|
await page.getByLabel(/Password \*/).fill('SecurePassword123!');
|
|
|
|
// Submit form
|
|
await page.getByRole('button', { name: 'Create User' }).click();
|
|
|
|
// Wait for dialog to close (indicates success)
|
|
await expect(page.getByText('Create New User')).not.toBeVisible({ timeout: 3000 });
|
|
|
|
// Verify no error was shown
|
|
const errorAlert = page.locator('[role="alert"]').filter({ hasText: /error|failed/i });
|
|
await expect(errorAlert).not.toBeVisible();
|
|
});
|
|
|
|
test('should successfully update an existing user', async ({ page }) => {
|
|
// Open action menu for first user
|
|
const actionButton = page.getByRole('button', { name: /Actions for/i }).first();
|
|
await actionButton.click();
|
|
|
|
// Click edit
|
|
await page.getByText('Edit User').click();
|
|
|
|
// Wait for edit dialog
|
|
await expect(page.getByText('Update user information')).toBeVisible();
|
|
|
|
// Modify first name
|
|
const firstNameInput = page.getByLabel('First Name *');
|
|
await firstNameInput.clear();
|
|
await firstNameInput.fill('UpdatedJohn');
|
|
|
|
// Submit form
|
|
await page.getByRole('button', { name: 'Update User' }).click();
|
|
|
|
// Wait for dialog to close (indicates success)
|
|
await expect(page.getByText('Update user information')).not.toBeVisible({ timeout: 3000 });
|
|
|
|
// Verify no error was shown
|
|
const errorAlert = page.locator('[role="alert"]').filter({ hasText: /error|failed/i });
|
|
await expect(errorAlert).not.toBeVisible();
|
|
});
|
|
|
|
test('should execute bulk deactivate action', async ({ page }) => {
|
|
// Select first user
|
|
const firstCheckbox = page.locator('table tbody').getByRole('checkbox').first();
|
|
await firstCheckbox.click();
|
|
|
|
// Wait for toolbar to appear
|
|
await expect(page.getByTestId('bulk-action-toolbar')).toBeVisible();
|
|
|
|
// Click deactivate button
|
|
await page.getByRole('button', { name: /Deactivate/i }).click();
|
|
|
|
// Confirmation dialog should appear
|
|
await expect(page.getByText('Deactivate Users')).toBeVisible();
|
|
|
|
// Confirm the action
|
|
await page.getByRole('button', { name: 'Deactivate' }).click();
|
|
|
|
// Dialog should close after success
|
|
await expect(page.getByText('Deactivate Users')).not.toBeVisible({ timeout: 3000 });
|
|
|
|
// Toolbar should be hidden (selection cleared)
|
|
await expect(page.getByTestId('bulk-action-toolbar')).not.toBeVisible();
|
|
});
|
|
|
|
test('should cancel bulk action when clicking cancel', async ({ page }) => {
|
|
// Select first user
|
|
const firstCheckbox = page.locator('table tbody').getByRole('checkbox').first();
|
|
await firstCheckbox.click();
|
|
|
|
// Wait for toolbar to appear
|
|
await expect(page.getByTestId('bulk-action-toolbar')).toBeVisible();
|
|
|
|
// Click delete button
|
|
await page.getByRole('button', { name: /Delete/i }).click();
|
|
|
|
// Confirmation dialog should appear
|
|
await expect(page.getByText('Delete Users')).toBeVisible();
|
|
|
|
// Click cancel
|
|
await page.getByRole('button', { name: 'Cancel' }).click();
|
|
|
|
// Dialog should close
|
|
await expect(page.getByText('Delete Users')).not.toBeVisible();
|
|
|
|
// Selection should still be there
|
|
await expect(firstCheckbox).toBeChecked();
|
|
});
|
|
});
|