import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; vi.mock('$app/navigation', () => ({ goto: vi.fn() })); vi.mock('$app/state', () => ({ navigating: { to: null } })); import Page from './+page.svelte'; afterEach(() => { cleanup(); vi.useRealTimers(); }); const SEARCH_LABEL = 'Titel, Personen, Tags durchsuchen…'; function makeData(overrides: Record = {}) { return { items: [], total: 0, q: '', from: '', to: '', senderId: '', receiverId: '', tags: [], sort: 'DATE', dir: 'desc', tagQ: '', tagOp: 'AND', canWrite: false, error: null, ...overrides }; } // ─── Initial state from server data ─────────────────────────────────────────── describe('documents page — initial state', () => { it('pre-fills the search input from data.q', async () => { render(Page, { data: makeData({ q: 'Geburtstag' }) }); await expect .element(page.getByRole('textbox', { name: SEARCH_LABEL })) .toHaveValue('Geburtstag'); }); it('leaves the search input empty when data.q is not set', async () => { render(Page, { data: makeData() }); await expect.element(page.getByRole('textbox', { name: SEARCH_LABEL })).toHaveValue(''); }); }); // ─── URL building via triggerSearch ─────────────────────────────────────────── describe('documents page — URL building', () => { beforeEach(() => vi.useFakeTimers()); it('calls goto with /documents?q=… after the 500 ms debounce', async () => { const { goto } = await import('$app/navigation'); vi.mocked(goto).mockClear(); render(Page, { data: makeData() }); const input = page.getByRole('textbox', { name: SEARCH_LABEL }); await input.fill('Urlaub'); expect(goto).not.toHaveBeenCalled(); vi.advanceTimersByTime(500); expect(goto).toHaveBeenCalledOnce(); const [url] = vi.mocked(goto).mock.calls[0]; expect(url).toContain('q=Urlaub'); expect(url).toMatch(/^\/documents\?/); }); it('omits q from the URL when the search field is empty', async () => { const { goto } = await import('$app/navigation'); vi.mocked(goto).mockClear(); render(Page, { data: makeData() }); const input = page.getByRole('textbox', { name: SEARCH_LABEL }); await input.fill(''); vi.advanceTimersByTime(500); const [url] = vi.mocked(goto).mock.calls[0] ?? ['']; expect(url).not.toContain('q='); }); it('second keystroke within 500 ms cancels the first timer — goto called only once', async () => { const { goto } = await import('$app/navigation'); vi.mocked(goto).mockClear(); render(Page, { data: makeData() }); const input = page.getByRole('textbox', { name: SEARCH_LABEL }); await input.fill('U'); vi.advanceTimersByTime(200); await input.fill('Urlaub'); vi.advanceTimersByTime(500); expect(goto).toHaveBeenCalledOnce(); }); it('passes keepFocus and noScroll options to goto', async () => { const { goto } = await import('$app/navigation'); vi.mocked(goto).mockClear(); render(Page, { data: makeData() }); const input = page.getByRole('textbox', { name: SEARCH_LABEL }); await input.fill('Brief'); vi.advanceTimersByTime(500); expect(goto).toHaveBeenCalledWith( expect.any(String), expect.objectContaining({ keepFocus: true, noScroll: true }) ); }); });