import { describe, it, expect, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; import { m } from '$lib/paraglide/messages.js'; const { default: JourneyInterlude } = await import('./JourneyInterlude.svelte'); afterEach(cleanup); declare global { interface Window { __xss_interlude?: number; } } describe('JourneyInterlude', () => { it('renders the note text as plaintext', async () => { render(JourneyInterlude, { props: { note: 'Eine kurze Pause auf der Reise.' } }); await expect.element(page.getByText('Eine kurze Pause auf der Reise.')).toBeVisible(); }); it('has aria-label from i18n (journey_interlude_aria_label)', async () => { render(JourneyInterlude, { props: { note: 'Notiz' } }); const el = document.querySelector(`[aria-label="${m.journey_interlude_aria_label()}"]`); expect(el).not.toBeNull(); }); it('has role="note" so the aria-label is announced by screen readers', async () => { render(JourneyInterlude, { props: { note: 'Notiz' } }); const el = document.querySelector('[role="note"]'); expect(el).not.toBeNull(); expect(el?.getAttribute('aria-label')).toBe(m.journey_interlude_aria_label()); }); it('note text uses readable body size (text-base, #800)', async () => { render(JourneyInterlude, { props: { note: 'Notiz' } }); const text = document.querySelector('[role="note"] p'); expect(text!.className).toContain('text-base'); expect(text!.className).not.toContain('text-xs'); }); it('XSS: note is rendered as plaintext — injected payload does not execute', async () => { // Interlude uses Svelte text interpolation ({note}), NOT {@html}. render(JourneyInterlude, { props: { note: '' } }); expect(window.__xss_interlude).toBeUndefined(); expect(document.body.textContent).toContain('