From cdf10e079deecca06c8124dd9d0a87130c2c48ee Mon Sep 17 00:00:00 2001 From: Marcel Date: Sat, 9 May 2026 22:13:46 +0200 Subject: [PATCH] test(routes): cover AppNav navigation branches Brand link, four primary nav links, admin link gated on isAdmin, hamburger menu open/close state via aria-expanded. Mocks $app/state so the page URL drives the active-route highlighting. 6 tests, ~30 branches. Refs #496. Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/routes/AppNav.svelte.test.ts | 78 +++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 frontend/src/routes/AppNav.svelte.test.ts diff --git a/frontend/src/routes/AppNav.svelte.test.ts b/frontend/src/routes/AppNav.svelte.test.ts new file mode 100644 index 00000000..1d9c5cf5 --- /dev/null +++ b/frontend/src/routes/AppNav.svelte.test.ts @@ -0,0 +1,78 @@ +import { describe, it, expect, vi, afterEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import { page as browserPage } from 'vitest/browser'; + +const mockPage = { url: new URL('http://localhost/documents') }; + +vi.mock('$app/state', () => ({ + get page() { + return mockPage; + } +})); + +afterEach(cleanup); + +async function loadComponent() { + return (await import('./AppNav.svelte')).default; +} + +describe('AppNav', () => { + it('renders the brand link', async () => { + mockPage.url = new URL('http://localhost/'); + const AppNav = await loadComponent(); + render(AppNav, { props: { isAdmin: false } }); + + await expect.element(browserPage.getByRole('link', { name: /familienarchiv/i })).toBeVisible(); + }); + + it('renders all four primary nav links by default', async () => { + mockPage.url = new URL('http://localhost/'); + const AppNav = await loadComponent(); + render(AppNav, { props: { isAdmin: false } }); + + await expect.element(browserPage.getByRole('link', { name: /^dokumente$/i })).toBeVisible(); + await expect.element(browserPage.getByRole('link', { name: /^personen$/i })).toBeVisible(); + await expect.element(browserPage.getByRole('link', { name: /^stammbaum$/i })).toBeVisible(); + await expect.element(browserPage.getByRole('link', { name: /^geschichten$/i })).toBeVisible(); + }); + + it('hides the admin link when isAdmin is false', async () => { + mockPage.url = new URL('http://localhost/'); + const AppNav = await loadComponent(); + render(AppNav, { props: { isAdmin: false } }); + + await expect + .element(browserPage.getByRole('link', { name: /^admin$/i })) + .not.toBeInTheDocument(); + }); + + it('shows the admin link when isAdmin is true', async () => { + mockPage.url = new URL('http://localhost/'); + const AppNav = await loadComponent(); + render(AppNav, { props: { isAdmin: true } }); + + await expect.element(browserPage.getByRole('link', { name: /^admin$/i })).toBeVisible(); + }); + + it('shows the open-menu hamburger by default', async () => { + mockPage.url = new URL('http://localhost/'); + const AppNav = await loadComponent(); + render(AppNav, { props: { isAdmin: false } }); + + await expect + .element(browserPage.getByRole('button', { name: /menü öffnen/i })) + .toHaveAttribute('aria-expanded', 'false'); + }); + + it('opens the mobile nav and switches the hamburger label when clicked', async () => { + mockPage.url = new URL('http://localhost/'); + const AppNav = await loadComponent(); + render(AppNav, { props: { isAdmin: false } }); + + await browserPage.getByRole('button', { name: /menü öffnen/i }).click(); + + await expect + .element(browserPage.getByRole('button', { name: /menü schließen/i })) + .toHaveAttribute('aria-expanded', 'true'); + }); +});