Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 6m47s
CI / Build container images (pull_request) Successful in 3m59s
CI / Auth e2e pack (pull_request) Successful in 9m7s
CI / Import/export fidelity gate (pull_request) Successful in 57s
CD / Deploy to Test (push) Blocked by required conditions
CD / Smoke tests against Test (push) Blocked by required conditions
CD / Promote to Int (push) Blocked by required conditions
CI / Build container images (push) Blocked by required conditions
CI / Lint, typecheck, test (push) Has been cancelled
CI / Auth e2e pack (push) Blocked by required conditions
CI / Import/export fidelity gate (push) Blocked by required conditions
CD / Build and push images (push) Has been cancelled
POST /admin/users (Site-Admin guard) creates an account with the same field rules as self-registration, but active immediately: the admin vouches for the address, so the e-mail is marked verified and the personal pond is provisioned exactly like the verify-email path does (markEmailVerified alone would skip the pond). The user manager gains a create dialog (useModalFocus/useDismissable, Field wiring, flat RHF field names per the #322 lesson). New audit action user.created_by_admin, catalogue bumped to 1.9. Tests: api e2e-db (create + immediate login + personal pond, duplicate username 409, non-admin 403), web e2e through the dialog, and the admin a11y scan now opens the dialog too. Both packs verified locally against a fresh stack. Closes #331
87 lines
3.4 KiB
TypeScript
87 lines
3.4 KiB
TypeScript
import { expect, request, test } from '@playwright/test';
|
|
|
|
import { contextForUser, FIXTURE_PASSWORD } from './helpers';
|
|
|
|
const BASE_URL = process.env.E2E_BASE_URL ?? 'http://localhost:5173';
|
|
|
|
/**
|
|
* Site-Admin user management UI (issue #59): disabling a user through the admin
|
|
* list logs them out and blocks login with a distinct message; enabling
|
|
* restores access. (Delete + pseudonymization is covered thoroughly by the api
|
|
* db test; the browser pack stays non-destructive so fixtures survive.)
|
|
*/
|
|
test('disabling a user in the admin UI blocks their login, enabling restores it', async ({
|
|
browser,
|
|
}) => {
|
|
const admin = await contextForUser(browser, BASE_URL, 'fixture-admin');
|
|
|
|
const login = async (): Promise<number> => {
|
|
const ctx = await request.newContext({ baseURL: BASE_URL });
|
|
const res = await ctx.post('/api/v1/auth/login', {
|
|
data: { usernameOrEmail: 'fixture-viewer', password: FIXTURE_PASSWORD },
|
|
});
|
|
const status = res.status();
|
|
await ctx.dispose();
|
|
return status;
|
|
};
|
|
|
|
expect(await login()).toBe(200); // active to begin with
|
|
|
|
const page = await admin.newPage();
|
|
await page.goto('/admin');
|
|
await page.locator('.user-manager__search').fill('fixture-viewer');
|
|
const row = page.locator('.user-row[data-username="fixture-viewer"]');
|
|
await expect(row).toBeVisible();
|
|
|
|
try {
|
|
await row.locator('.user-row__disable').click();
|
|
await expect(row.locator('.user-row__status')).toHaveText(/disabled|deaktiviert/i);
|
|
expect(await login()).toBe(403); // account_disabled
|
|
} finally {
|
|
// Re-enable so the fixture is left intact for other packs / reruns.
|
|
await row.locator('.user-row__disable').click();
|
|
await expect(row.locator('.user-row__status')).toHaveText(/active|aktiv/i);
|
|
await admin.close();
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Direct account creation (issue #331): the dialog creates an active account
|
|
* — the new user logs in immediately, no verification hop. The account stays
|
|
* in the e2e database; the unique name keeps reruns independent.
|
|
*/
|
|
test('creating a user in the admin UI yields an account that can log in at once', async ({
|
|
browser,
|
|
}) => {
|
|
const admin = await contextForUser(browser, BASE_URL, 'fixture-admin');
|
|
const username = `created-${Date.now()}`;
|
|
const password = 'ein sicheres anfangspasswort';
|
|
|
|
const page = await admin.newPage();
|
|
await page.goto('/admin');
|
|
await page.locator('.user-manager__create').click();
|
|
|
|
const dialog = page.getByRole('dialog');
|
|
await dialog.getByLabel(/username|benutzername/i).fill(username);
|
|
await dialog.getByLabel(/e-mail/i).fill(`${username}@example.org`);
|
|
await dialog.getByLabel(/display name|anzeigename/i).fill('Created via UI');
|
|
await dialog.getByLabel(/initial password|anfangspasswort/i).fill(password);
|
|
await dialog.getByRole('button', { name: /^create$|^anlegen$/i }).click();
|
|
await expect(dialog).toBeHidden();
|
|
|
|
// The list refetches; the fresh account is findable.
|
|
await page.locator('.user-manager__search').fill(username);
|
|
const row = page.locator(`.user-row[data-username="${username}"]`);
|
|
await expect(row).toBeVisible();
|
|
await expect(row.locator('.user-row__status')).toHaveText(/active|aktiv/i);
|
|
await admin.close();
|
|
|
|
// No verification mail hop: login works right away.
|
|
const ctx = await request.newContext({ baseURL: BASE_URL });
|
|
const res = await ctx.post('/api/v1/auth/login', {
|
|
data: { usernameOrEmail: username, password },
|
|
});
|
|
expect(res.status()).toBe(200);
|
|
await ctx.dispose();
|
|
});
|