From d5441d3e550b5225757398bb35ff8226a464626e Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 9 Jun 2026 10:36:56 +0200 Subject: [PATCH] fix(tests): resolve 10 failing browser-mode tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- .../lib/document/DashboardNeedsMetadata.svelte.spec.ts | 2 +- frontend/src/lib/shared/actions/radioGroupNav.ts | 1 + frontend/src/routes/geschichten/[id]/page.svelte.test.ts | 4 ++-- .../routes/geschichten/new/TypeSelector.svelte.spec.ts | 9 +++++---- frontend/src/test-setup.ts | 6 ++++++ 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/frontend/src/lib/document/DashboardNeedsMetadata.svelte.spec.ts b/frontend/src/lib/document/DashboardNeedsMetadata.svelte.spec.ts index b08e32ff..f842f296 100644 --- a/frontend/src/lib/document/DashboardNeedsMetadata.svelte.spec.ts +++ b/frontend/src/lib/document/DashboardNeedsMetadata.svelte.spec.ts @@ -52,6 +52,6 @@ describe('DashboardNeedsMetadata', () => { it('uses totalCount in the footer even when topDocs has fewer items', async () => { const docs = [makeDoc('d1', 'Only one')]; render(DashboardNeedsMetadata, { topDocs: docs, totalCount: 50 }); - await expect.element(page.getByRole('link', { name: /50/ })).toBeInTheDocument(); + await expect.element(page.getByRole('link', { name: /Alle 50/ })).toBeInTheDocument(); }); }); diff --git a/frontend/src/lib/shared/actions/radioGroupNav.ts b/frontend/src/lib/shared/actions/radioGroupNav.ts index 25a6d3d9..6c3d5838 100644 --- a/frontend/src/lib/shared/actions/radioGroupNav.ts +++ b/frontend/src/lib/shared/actions/radioGroupNav.ts @@ -19,6 +19,7 @@ export function radioGroupNav( const next = (current + delta + radios.length) % radios.length; radios[next].focus(); + radios.forEach((r, i) => r.setAttribute('aria-checked', i === next ? 'true' : 'false')); onChangeFn?.(radios[next].getAttribute('value') ?? ''); } diff --git a/frontend/src/routes/geschichten/[id]/page.svelte.test.ts b/frontend/src/routes/geschichten/[id]/page.svelte.test.ts index 6dc213a4..577a03fe 100644 --- a/frontend/src/routes/geschichten/[id]/page.svelte.test.ts +++ b/frontend/src/routes/geschichten/[id]/page.svelte.test.ts @@ -236,7 +236,7 @@ describe('geschichten/[id] page', () => { // Trigger delete — opens confirm dialog const deleteBtn = page.getByRole('button', { name: /löschen/i }); - userEvent.click(deleteBtn); + await userEvent.click(deleteBtn); // Settle the confirmation dialog confirmService.settle(true); @@ -262,7 +262,7 @@ describe('geschichten/[id] page', () => { // Trigger delete — opens confirm dialog const deleteBtn = page.getByRole('button', { name: /löschen/i }); - userEvent.click(deleteBtn); + await userEvent.click(deleteBtn); // Settle the confirmation dialog confirmService.settle(true); diff --git a/frontend/src/routes/geschichten/new/TypeSelector.svelte.spec.ts b/frontend/src/routes/geschichten/new/TypeSelector.svelte.spec.ts index 2c36b34d..9aff1ca9 100644 --- a/frontend/src/routes/geschichten/new/TypeSelector.svelte.spec.ts +++ b/frontend/src/routes/geschichten/new/TypeSelector.svelte.spec.ts @@ -85,8 +85,9 @@ describe('TypeSelector', () => { const onweiter = vi.fn(); render(TypeSelector, { props: { onweiter } }); - const weiter = page.getByRole('button', { name: /Weiter/i }); - await userEvent.click(weiter); + // aria-disabled="true" prevents Playwright actionability — dispatch via DOM to test handler behaviour + const weiter = document.querySelector('button[aria-disabled="true"]'); + weiter?.click(); expect(onweiter).not.toHaveBeenCalled(); }); @@ -101,7 +102,7 @@ describe('TypeSelector', () => { render(TypeSelector, { props: { onweiter: vi.fn() } }); const storyCard = page.getByRole('radio', { name: /Geschichte/i }); - await storyCard.focus(); + await userEvent.click(storyCard); // click focuses the card; .focus() is not on vitest-browser Locator await userEvent.keyboard('{ArrowRight}'); const journeyCard = page.getByRole('radio', { name: /Lesereise/i }); @@ -113,7 +114,7 @@ describe('TypeSelector', () => { render(TypeSelector, { props: { onweiter: vi.fn() } }); const storyCard = page.getByRole('radio', { name: /Geschichte/i }); - await storyCard.focus(); + await userEvent.click(storyCard); // click focuses the card; .focus() is not on vitest-browser Locator await userEvent.keyboard('{ArrowLeft}'); const journeyCard = page.getByRole('radio', { name: /Lesereise/i }); diff --git a/frontend/src/test-setup.ts b/frontend/src/test-setup.ts index efdba26e..a20320b5 100644 --- a/frontend/src/test-setup.ts +++ b/frontend/src/test-setup.ts @@ -1,3 +1,9 @@ +// Load global CSS so Tailwind utility classes (text-xs, min-h-[44px], etc.) +// are compiled and applied in browser-mode tests. Without this import the +// @tailwindcss/vite plugin never generates the utility stylesheet, leaving +// computed styles at browser defaults (16 px font, content-only height). +import './routes/layout.css'; + // Disable SvelteKit hover-prefetch (both data + code) in browser-mode tests. // ADR-012 / #553. //