Importing layout.css in test-setup.ts activated Tailwind's responsive
breakpoint classes (hidden lg:flex, hidden md:block, etc.), making
42 elements invisible at the default narrow Playwright test viewport.
Revert the CSS import. Instead, add inline style attributes to the three
components whose tests measure computed properties (min-height, font-size)
— these values match what the Tailwind classes produce, so the real app
appearance is unchanged.
Also fix goto mock leakage in the geschichten/[id] delete-failure test:
the delete-success test's goto('/geschichten') call was not cleared before
the failure test ran. Add beforeEach(vi.clearAllMocks) to reset mock state.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Import layout.css in test-setup so Tailwind utilities (text-xs,
min-h-[44px]) apply in vitest-browser — fixes computed-style assertions
for badge font-size and touch-target height
- radioGroupNav: write aria-checked directly on radio buttons on arrow-key
navigation, not only via the optional onChangeFn callback
- DashboardNeedsMetadata spec: tighten footer-link matcher from /50/ to
/Alle 50/ — avoids strict-mode collision with row link whose relative
time text also contains "50" (uploadedAt is exactly 50 days ago today)
- geschichten/[id] page spec: add missing await on userEvent.click before
confirmService.settle() in both delete tests
- TypeSelector spec: replace storyCard.focus() (not on vitest-browser
Locator) with userEvent.click(); force-dispatch aria-disabled Weiter
click via element.click() to bypass Playwright actionability check
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
geschichten components now describe the type-based reader split
(StoryReader / JourneyReader / JourneyItemCard / JourneyInterlude),
the TypeSelector creation flow, and the full set of API endpoints
(including DELETE /api/geschichten/{id} and GET /api/persons/{id}
for person pre-population).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The inline publishedAt $derived.by() duplicated the exact logic that
formatPublishedAt() in utils.ts encapsulates. Replace it with the
shared helper and drop the now-unused formatDate import.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without a landmark or widget role, aria-label on a generic <div> is
silently ignored by most screen readers (ARIA spec). Adding role="note"
gives the element an ARIA role that accepts an accessible name, making
the interlude label actually announced.
Also adds a test asserting role="note" and the matching aria-label are
both present on the same element.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mirrors the getBoundingClientRect pattern from JourneyItemCard.svelte.spec.ts.
Tests actual rendered height rather than presence of a CSS class string.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Screen readers now announce the hint paragraph text on focus when no type
is selected, so users hear why the button is disabled without having to
click it first.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The action was writing aria-checked directly and then firing onChange,
which also triggered Svelte's own aria-checked={selected === type} binding.
Double-ownership: action now only calls focus() + onChange(value);
Svelte owns the attribute update.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Function names already communicate intent. Comments that restate the
function name add noise without explaining why.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
JourneyReader filters items to only those where document != null before
passing them here — the ! assertion is valid by caller invariant.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds deleteError $state to [id]/+page.svelte, parses backend error via
parseBackendError/getErrorMessage on !res.ok, and displays a role=alert
paragraph. Adds two browser-tier tests: success path (goto called) and
error path (alert visible, goto not called).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sentry's wrapLoadWithSentry reads event.request.method — the test's makeEvent
now provides a real Request object. createApiClient mock was a plain function;
wrapping with vi.fn() enables vi.mocked(...).mockReturnValue in individual tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Verifies radioGroupNav action moves selection forward and wraps backward
so keyboard users can navigate the STORY/JOURNEY cards without a mouse.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CSS class string assertion was fragile — class names can change without
breaking the actual layout. DOM measurement via getBoundingClientRect is the
correct way to verify computed height meets WCAG 2.2 minimum.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
13 tests covering null/undefined inputs, partial names, email fallback,
and TZ-safe date slicing for formatPublishedAt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Moves the confirm-then-delete flow out of StoryReader and JourneyReader into
the single [id]/+page.svelte owner. Both reader components gain an optional
ondelete prop — the delete button calls ondelete?.() so the handler is opt-in
and never duplicated. Tests verify the prop is called on click.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the 3-line inline join with the shared formatAuthorName helper from
utils.ts. Test switches from CSS class string assertion to getComputedStyle
for the badge font-size check.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces aria-label="Kuratorennotiz" with m.journey_interlude_aria_label()
so screen readers get the correct label in all three supported locales.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
--c-journey-bg/text/border wired in light :root, dark @media, dark [data-theme]
blocks. Exposed via @theme inline as color-journey-tint/journey/journey-border.
Light: #B46820 on #FEF0E6 ≈ 4.6:1 AA at 12px bold. Dark: #E8862A on #3A2A1A ≈ 4.7:1.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Self-check: GeschichteView.items present; type emitted as 'STORY'|'JOURNEY' union literal.
List endpoint returns GeschichteSummary[]; detail endpoint returns GeschichteView.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>