dorfteich/apps/web/e2e/admin-settings.spec.ts
Claude Fable 5 1c757fb76e
Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 6m58s
CI / Build container images (pull_request) Successful in 1m22s
CI / Auth e2e pack (pull_request) Failing after 9m54s
CI / Import/export fidelity gate (pull_request) Has been skipped
Document title follows the configured instance name (#323)
useDocumentTitle pinned APP_NAME = 'Dorfteich', so every route title —
tab, bookmarks, the window title a screen reader announces (WCAG 2.4.2)
— named the product instead of the operator's instance. The trailing
name now comes from the public branding query, exactly like the TopBar
brand (#306); until the query resolves (or when it cannot, e.g.
maintenance mode) the shipped default keeps the title stable, so an
untouched instance reads exactly as before. The static index.html title
stays the pre-JS placeholder — server-rendering it is #179's territory,
deliberately out of scope (recorded in the issue).

The admin-settings e2e now also asserts the title carries the new name
right after saving, without a reload.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017aviRTgWCcAHUh1SBoxf6P
2026-08-04 11:20:21 +02:00

56 lines
2.4 KiB
TypeScript

import { expect, test } from '@playwright/test';
import { contextForUser } from './helpers';
const BASE_URL = process.env.E2E_BASE_URL ?? 'http://localhost:5173';
/**
* The general admin settings card saves THROUGH THE FORM (issue #322).
*
* This must drive the UI, not the api: the bug it fences was invisible to
* every api-level test — react-hook-form nested the dotted field names on
* input, the strict PATCH schema rejected the body, and the form looked
* fine while never saving. Verified end to end: success message, the value
* survives a full reload, the api returns it, and the TopBar picks it up
* without a reload (branding query invalidation).
*/
test('instance name changed in the general settings form persists', async ({ browser }) => {
const admin = await contextForUser(browser, BASE_URL, 'fixture-admin');
const before = (
(await (await admin.request.get('/api/v1/admin/settings')).json()) as Record<string, unknown>
)['instance.name'] as string;
const newName = `Renamed ${Date.now()}`;
const nameLabel = /^(Instance name|Name der Instanz)$/;
const page = await admin.newPage();
try {
await page.goto('/admin');
const generalCard = page
.locator('section.settings-section')
.filter({ has: page.getByLabel(nameLabel) });
await page.getByLabel(nameLabel).fill(newName);
await generalCard.getByRole('button', { name: /^(Save|Speichern)$/ }).click();
// Scoped to the card: the page has several forms with status regions.
await expect(generalCard.getByRole('status')).toHaveText(/^(Saved\.|Gespeichert\.)$/);
// The TopBar and the document title show the new name without a reload —
// the save invalidates the branding query both read from (issue #323).
await expect(page.locator('.topbar__brand')).toHaveText(newName);
await expect(page).toHaveTitle(new RegExp(`${newName}$`));
// The proof the form really persisted: the value survives a reload and
// the api returns it.
await page.reload();
await expect(page.getByLabel(nameLabel)).toHaveValue(newName);
const stored = (
(await (await admin.request.get('/api/v1/admin/settings')).json()) as Record<string, unknown>
)['instance.name'];
expect(stored).toBe(newName);
} finally {
await admin.request.patch('/api/v1/admin/settings', {
data: { 'instance.name': before },
});
await admin.close();
}
});