From fe9b4a95695e0efdf5f6b7efff34bd62ec5e40e2 Mon Sep 17 00:00:00 2001 From: Marcel Date: Thu, 19 Mar 2026 20:28:43 +0100 Subject: [PATCH] fix(e2e): fix locale cookie httpOnly and add hydration waits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Paraglide's client-side setLocale writes the locale via document.cookie, which silently fails for HttpOnly cookies. SvelteKit's cookies.set() defaults to httpOnly: true, so locale switching never worked in tests. Fix by setting httpOnly: false on the locale cookie (it's a UI preference, not a credential — no security concern). Add waitForSelector('[data-hydrated]') before any click that relies on SvelteKit JavaScript event handlers. Without this, the click fires before hydration and the onclick handler is not yet registered. Co-Authored-By: Claude Sonnet 4.6 --- frontend/e2e/lang.spec.ts | 3 +++ frontend/e2e/persons.spec.ts | 1 + frontend/src/hooks.server.ts | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/e2e/lang.spec.ts b/frontend/e2e/lang.spec.ts index 04294a98..9034b4c1 100644 --- a/frontend/e2e/lang.spec.ts +++ b/frontend/e2e/lang.spec.ts @@ -10,6 +10,7 @@ test.describe('Language selector', () => { test('switching to EN translates the navigation', async ({ page }) => { await page.goto('/'); + await page.waitForSelector('[data-hydrated]'); await page.getByRole('banner').getByRole('button', { name: 'EN', exact: true }).click(); await expect(page.getByRole('navigation').getByRole('link', { name: 'Documents' })).toBeVisible(); await expect(page.getByRole('navigation').getByRole('link', { name: 'Persons' })).toBeVisible(); @@ -17,6 +18,7 @@ test.describe('Language selector', () => { test('language choice persists after navigation', async ({ page }) => { await page.goto('/'); + await page.waitForSelector('[data-hydrated]'); await page.getByRole('banner').getByRole('button', { name: 'EN', exact: true }).click(); await page.goto('/persons'); await expect(page.getByRole('navigation').getByRole('link', { name: 'Documents' })).toBeVisible(); @@ -24,6 +26,7 @@ test.describe('Language selector', () => { test('switching back to DE restores German', async ({ page }) => { await page.goto('/'); + await page.waitForSelector('[data-hydrated]'); await page.getByRole('banner').getByRole('button', { name: 'EN', exact: true }).click(); await page.getByRole('banner').getByRole('button', { name: 'DE', exact: true }).click(); await expect(page.getByRole('navigation').getByRole('link', { name: 'Dokumente' })).toBeVisible(); diff --git a/frontend/e2e/persons.spec.ts b/frontend/e2e/persons.spec.ts index a6c689bd..c6072347 100644 --- a/frontend/e2e/persons.spec.ts +++ b/frontend/e2e/persons.spec.ts @@ -87,6 +87,7 @@ test.describe('Conversations', () => { test('sort toggle changes the button label', async ({ page }) => { await page.goto('/conversations'); + await page.waitForSelector('[data-hydrated]'); const btn = page.getByRole('button', { name: /Sortierung/i }); await expect(btn).toContainText('Neueste zuerst'); await btn.click(); diff --git a/frontend/src/hooks.server.ts b/frontend/src/hooks.server.ts index 02275849..a88994f9 100644 --- a/frontend/src/hooks.server.ts +++ b/frontend/src/hooks.server.ts @@ -11,7 +11,7 @@ const handleLocaleDetection: Handle = ({ event, resolve }) => { if (!event.cookies.get(cookieName)) { const locale = detectLocale(event.request.headers.get('accept-language') ?? ''); if (locale) { - event.cookies.set(cookieName, locale, { path: '/', sameSite: 'lax', maxAge: cookieMaxAge }); + event.cookies.set(cookieName, locale, { path: '/', sameSite: 'lax', maxAge: cookieMaxAge, httpOnly: false }); } } return resolve(event);