Admins can now add users directly from the admin portal via a modal form (name, email, password). Created users are pre-approved and can log in immediately. - POST /api/admin/users endpoint (AddUserHandler, AddUserCommand) - Add user modal in AdminPage with inline error handling - 5 new integration tests covering auth, happy path, duplicate email, and immediate login; fix SQLite file-lock on test cleanup via SqliteConnection.ClearAllPools() - 4 new E2E tests covering modal open/close, happy path, and duplicate email error
102 lines
4.4 KiB
TypeScript
102 lines
4.4 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import { loginAsAdmin } from './helpers';
|
|
|
|
test.describe('Admin portal', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await loginAsAdmin(page);
|
|
});
|
|
|
|
test('admin user sees the Admin button in the header', async ({ page }) => {
|
|
await expect(page.getByRole('button', { name: 'Admin' })).toBeVisible();
|
|
});
|
|
|
|
test('navigates to the admin portal', async ({ page }) => {
|
|
await page.getByRole('button', { name: 'Admin' }).click();
|
|
await page.waitForURL('/admin');
|
|
|
|
await expect(page.getByRole('heading', { name: /Who can book/i })).toBeVisible();
|
|
await expect(page.getByText(/Users.*The Hague HQ/i)).toBeVisible();
|
|
});
|
|
|
|
test('admin portal lists the admin account', async ({ page }) => {
|
|
await page.getByRole('button', { name: 'Admin' }).click();
|
|
await page.waitForURL('/admin');
|
|
|
|
await expect(page.getByText('admin@randall.local')).toBeVisible();
|
|
const adminRow = page.locator('[data-testid="user-row"]').filter({ hasText: 'admin@randall.local' });
|
|
await expect(adminRow.locator('[data-testid="role-badge"]').filter({ hasText: /^admin$/i })).toBeVisible();
|
|
});
|
|
|
|
test('admin account has the admin role badge and no make-admin button', async ({ page }) => {
|
|
await page.getByRole('button', { name: 'Admin' }).click();
|
|
await page.waitForURL('/admin');
|
|
|
|
const adminRow = page.locator('[data-testid="user-row"]').filter({ hasText: 'admin@randall.local' });
|
|
await expect(adminRow.locator('[data-testid="role-badge"]').filter({ hasText: /^admin$/i })).toBeVisible();
|
|
// The make-admin action button is not rendered for users who are already admin
|
|
await expect(adminRow.getByRole('button', { name: 'Admin' })).not.toBeVisible();
|
|
});
|
|
|
|
test('wordmark navigates back to the planner', async ({ page }) => {
|
|
await page.getByRole('button', { name: 'Admin' }).click();
|
|
await page.waitForURL('/admin');
|
|
await page.locator('span', { hasText: 'randall' }).first().click();
|
|
|
|
await page.waitForURL('/');
|
|
await expect(page.getByRole('heading', { name: /Where to sit/i })).toBeVisible();
|
|
});
|
|
|
|
test('+ Add user button opens the add-user modal', async ({ page }) => {
|
|
await page.getByRole('button', { name: 'Admin' }).click();
|
|
await page.waitForURL('/admin');
|
|
|
|
await page.getByRole('button', { name: '+ Add user' }).click();
|
|
|
|
await expect(page.getByRole('heading', { name: 'Add user' })).toBeVisible();
|
|
await expect(page.locator('form input[type="text"]')).toBeVisible();
|
|
await expect(page.locator('form input[type="email"]')).toBeVisible();
|
|
await expect(page.locator('form input[type="password"]')).toBeVisible();
|
|
});
|
|
|
|
test('add-user modal closes when Cancel is clicked', async ({ page }) => {
|
|
await page.getByRole('button', { name: 'Admin' }).click();
|
|
await page.waitForURL('/admin');
|
|
|
|
await page.getByRole('button', { name: '+ Add user' }).click();
|
|
await expect(page.getByRole('heading', { name: 'Add user' })).toBeVisible();
|
|
|
|
await page.locator('form').getByRole('button', { name: 'Cancel' }).click();
|
|
|
|
await expect(page.getByRole('heading', { name: 'Add user' })).not.toBeVisible();
|
|
});
|
|
|
|
test('can add a new user via the modal and see them in the table', async ({ page }) => {
|
|
await page.getByRole('button', { name: 'Admin' }).click();
|
|
await page.waitForURL('/admin');
|
|
|
|
const email = `adduser+${Date.now()}@test.com`;
|
|
|
|
await page.getByRole('button', { name: '+ Add user' }).click();
|
|
await page.locator('form input[type="text"]').fill('New Test User');
|
|
await page.locator('form input[type="email"]').fill(email);
|
|
await page.locator('form input[type="password"]').fill('Test@1234');
|
|
await page.locator('form button[type="submit"]').click();
|
|
|
|
await expect(page.getByText(email)).toBeVisible();
|
|
});
|
|
|
|
test('add-user modal shows an error for a duplicate email', async ({ page }) => {
|
|
await page.getByRole('button', { name: 'Admin' }).click();
|
|
await page.waitForURL('/admin');
|
|
|
|
await page.getByRole('button', { name: '+ Add user' }).click();
|
|
await page.locator('form input[type="text"]').fill('Duplicate');
|
|
await page.locator('form input[type="email"]').fill('admin@randall.local');
|
|
await page.locator('form input[type="password"]').fill('Test@1234');
|
|
await page.locator('form button[type="submit"]').click();
|
|
|
|
await expect(page.getByText(/already exists/i)).toBeVisible();
|
|
await expect(page.getByRole('heading', { name: 'Add user' })).toBeVisible();
|
|
});
|
|
});
|