feat(admin): add create-user flow with API, UI, and tests

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
This commit is contained in:
2026-05-01 16:18:12 +02:00
parent 161a5aa85e
commit d41559f013
10 changed files with 322 additions and 7 deletions

View File

@@ -45,4 +45,57 @@ test.describe('Admin portal', () => {
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();
});
});