All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 4m44s
CI / Build container images (pull_request) Successful in 4m2s
CI / Auth e2e pack (pull_request) Successful in 10m50s
CI / Import/export fidelity gate (pull_request) Successful in 55s
CD / Build and push images (push) Successful in 19s
CD / Deploy to Test (push) Successful in 17s
CD / Smoke tests against Test (push) Successful in 4m2s
CI / Lint, typecheck, test (push) Successful in 4m47s
CI / Build container images (push) Has been skipped
CD / Promote to Int (push) Successful in 14s
CI / Auth e2e pack (push) Successful in 10m7s
CI / Import/export fidelity gate (push) Successful in 56s
Release / Build release images and notes (push) Successful in 1m11s
Release / Release-candidate operations QA (push) Successful in 1m0s
Prod deploy / Deploy the released images to Prod (push) Successful in 17s
pondSettingsSchema gains theme = { accent: '#rrggbb' | null } (null =
inherit the viewer's theme), exposed as a top-level key of the flat
updatePondInputSchema and included in the PondsService settings merge
(the known silent-no-op pitfall). The server validates only the hex;
conformance arises at render time: PondThemeScope (mounted around the
page content next to PondFontScope) derives the accent pair for the
EFFECTIVE mode via useEffectiveTheme and sets it as inline custom
properties — inline beats both tokens.css and the user-theme <style>,
which IS the cascade precedence pond > user > default.
Pond settings get a PondThemeSection (inherit | presets | custom color
with per-mode preview swatches, explicit save like the font manager);
AccentSwatches extracted for reuse; i18n de+en. The no-JS public shell
stays deliberately un-themed (ADR 0018 amendment).
Tests: pond DB test (theme merge keeps fonts, invalid hex 400), e2e
pond-theme.spec (scope boundary content vs. chrome, per-mode
re-derivation, axe on the pond settings page; resets the fixture pond).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QRtCnB3uLdQtFmvp9HXcRX
73 lines
2.8 KiB
TypeScript
73 lines
2.8 KiB
TypeScript
import AxeBuilder from '@axe-core/playwright';
|
|
import { expect, test, type Page } from '@playwright/test';
|
|
|
|
import { contextForUser } from './helpers';
|
|
|
|
/**
|
|
* Teich-Akzent (issue #186, ADR 0018 Stufe C): der Akzent eines Teichs
|
|
* färbt NUR die Teich-Inhalte (PondThemeScope), das App-Chrome behält den
|
|
* Akzent der Betrachtenden; je Modus wird abgeleitet. Der Spec setzt das
|
|
* Theme des content-fixtures-Teichs per API und RÄUMT ES AM ENDE WIEDER AB
|
|
* (Fixtures sind pack-übergreifend geteilt).
|
|
*/
|
|
|
|
const BASE = process.env.E2E_BASE_URL ?? 'http://localhost:5173';
|
|
const POND = 'content-fixtures';
|
|
const ACCENT = '#6d4fa0'; // iris-violet: besteht hell verbatim, dunkel wird abgeleitet
|
|
|
|
const accentAt = (page: Page, selector: string) =>
|
|
page.evaluate(
|
|
(sel) =>
|
|
getComputedStyle(document.querySelector(sel)!).getPropertyValue('--color-accent').trim(),
|
|
selector,
|
|
);
|
|
|
|
test('pond accent colors the pond content but not the app chrome', async ({ browser }) => {
|
|
const context = await contextForUser(browser, BASE, 'fixture-user');
|
|
const page = await context.newPage();
|
|
await page.emulateMedia({ colorScheme: 'light' });
|
|
|
|
const pond = await page.request.get(`${BASE}/api/v1/ponds/${POND}`);
|
|
expect(pond.ok()).toBe(true);
|
|
const pondId = ((await pond.json()) as { id: string }).id;
|
|
|
|
try {
|
|
const patch = await page.request.patch(`${BASE}/api/v1/ponds/${pondId}`, {
|
|
data: { theme: { accent: ACCENT } },
|
|
});
|
|
expect(patch.ok()).toBe(true);
|
|
|
|
await page.goto(`/p/${POND}/every-element`);
|
|
await page.locator('.pond-theme-scope').waitFor();
|
|
|
|
// Scope-Grenze: Inhalt trägt den Teich-Akzent, die TopBar den Default.
|
|
expect(await accentAt(page, '.pond-theme-scope')).toBe(ACCENT);
|
|
expect(await accentAt(page, '.topbar')).toBe('#2f6f4f');
|
|
|
|
// Moduswechsel leitet neu ab: dunkel bekommt einen helleren Wert.
|
|
await page.locator('.topbar__theme').click(); // system -> light
|
|
await page.locator('.topbar__theme').click(); // light -> dark
|
|
const darkAccent = await accentAt(page, '.pond-theme-scope');
|
|
expect(darkAccent).not.toBe(ACCENT);
|
|
expect(darkAccent).toMatch(/^#[0-9a-f]{6}$/);
|
|
expect(await accentAt(page, '.topbar')).not.toBe(ACCENT);
|
|
|
|
// Die Teich-Einstellungen (mit neuer Akzent-Sektion) bleiben axe-sauber.
|
|
await page.goto(`/p/${POND}/settings`);
|
|
await page.locator('input[name="pond-theme-accent"]').first().waitFor();
|
|
const results = await new AxeBuilder({ page })
|
|
.withTags(['wcag2a', 'wcag21a', 'wcag2aa', 'wcag21aa'])
|
|
.analyze();
|
|
expect(
|
|
results.violations.map((v) => ({ rule: v.id, help: v.help })),
|
|
'axe auf den Teich-Einstellungen',
|
|
).toEqual([]);
|
|
} finally {
|
|
await page.request.patch(`${BASE}/api/v1/ponds/${pondId}`, {
|
|
data: { theme: { accent: null } },
|
|
});
|
|
}
|
|
|
|
await context.close();
|
|
});
|