import { describe, it, expect, vi, afterEach } from 'vitest'; import { render, screen, waitFor, within } from '@testing-library/svelte'; import userEvent from '@testing-library/user-event'; import Page from './+page.svelte'; vi.mock('$app/navigation', () => ({ goto: vi.fn(), invalidateAll: vi.fn() })); vi.mock('$app/forms', () => ({ enhance: () => () => ({ destroy: () => {} }) })); const PLAN_ID = 'plan-00000000-0000-0000-0000-000000000001'; // Use a past week so "today" is never in this range — selectedDay defaults to weekStart (Monday) const DATE = '2025-01-06'; // Monday, January 6 2025 const mockData = { weekPlan: { id: PLAN_ID, weekStart: DATE, status: 'draft', slots: [] as any[] }, varietyScore: null, weekStart: DATE, recipes: [{ id: 'r1', name: 'Beef Bourguignon', effort: 'hard', cookTimeMin: 150 }], benutzer: { rolle: 'planer' } }; const mockDataWithSlot = { ...mockData, weekPlan: { id: PLAN_ID, weekStart: DATE, status: 'draft', slots: [{ id: 'slot-1', slotDate: DATE, recipe: { id: 'r1', name: 'Beef Bourguignon', effort: 'hard', cookTimeMin: 150 } }] } }; const mockSuggestions = [ { recipe: { id: 's1', name: 'Lachsfilet', effort: 'easy', cookTimeMin: 20 }, scoreDelta: 1.5, hasConflict: false } ]; describe('+page.svelte — $effect suggestion fetch', () => { afterEach(() => { vi.restoreAllMocks(); }); it('calls fetch when picker opens with correct planId and date', async () => { vi.stubGlobal( 'fetch', vi.fn().mockResolvedValueOnce({ json: () => Promise.resolve({ suggestions: mockSuggestions }) }) ); render(Page, { props: { data: mockData } }); await userEvent.click(screen.getAllByRole('button', { name: /Gericht/i })[0]); await waitFor(() => expect(fetch).toHaveBeenCalledTimes(1)); expect((fetch as any).mock.calls[0][0]).toContain(`planId=${PLAN_ID}`); expect((fetch as any).mock.calls[0][0]).toContain(`date=${DATE}`); }); it('shows suggestions in RecipePicker after fetch resolves', async () => { vi.stubGlobal( 'fetch', vi.fn().mockResolvedValueOnce({ json: () => Promise.resolve({ suggestions: mockSuggestions }) }) ); render(Page, { props: { data: mockData } }); await userEvent.click(screen.getAllByRole('button', { name: /Gericht/i })[0]); expect(await screen.findByText('Lachsfilet')).toBeTruthy(); }); it('passes AbortSignal to fetch so inflight requests can be cancelled', async () => { vi.stubGlobal( 'fetch', vi.fn().mockResolvedValueOnce({ json: () => Promise.resolve({ suggestions: [] }) }) ); render(Page, { props: { data: mockData } }); await userEvent.click(screen.getAllByRole('button', { name: /Gericht/i })[0]); await waitFor(() => expect(fetch).toHaveBeenCalledTimes(1)); const fetchOptions = (fetch as any).mock.calls[0][1]; expect(fetchOptions?.signal).toBeInstanceOf(AbortSignal); }); }); describe('+page.svelte — swap sheet suggestion fetch', () => { afterEach(() => { vi.restoreAllMocks(); }); it('opening mobile swap sheet triggers fetch with planId and date', async () => { vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ json: () => Promise.resolve({ suggestions: [] }) })); render(Page, { props: { data: mockDataWithSlot } }); // Open action sheet, then swap sheet await userEvent.click(screen.getByTestId('day-meal-card')); await userEvent.click(await screen.findByRole('button', { name: /Gericht tauschen/i })); await waitFor(() => expect(fetch).toHaveBeenCalledTimes(1)); expect((fetch as any).mock.calls[0][0]).toContain(`planId=${PLAN_ID}`); expect((fetch as any).mock.calls[0][0]).toContain(`date=${DATE}`); }); }); describe('+page.svelte — remove meal', () => { afterEach(() => { vi.restoreAllMocks(); }); it('clicking Entfernen in MealActionSheet shows undo bar with recipe name', async () => { vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ json: () => Promise.resolve({ suggestions: [] }) })); render(Page, { props: { data: mockDataWithSlot } }); await userEvent.click(screen.getByTestId('day-meal-card')); await userEvent.click(await screen.findByRole('button', { name: /Entfernen/i })); const undoBar = screen.getByTestId('undo-bar'); expect(undoBar).toBeTruthy(); expect(within(undoBar).getByText(/Beef Bourguignon/)).toBeTruthy(); }); it('clicking Rückgängig after remove hides the undo bar', async () => { vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ json: () => Promise.resolve({ suggestions: [] }) })); render(Page, { props: { data: mockDataWithSlot } }); await userEvent.click(screen.getByTestId('day-meal-card')); await userEvent.click(await screen.findByRole('button', { name: /Entfernen/i })); await userEvent.click(screen.getByRole('button', { name: /Rückgängig/i })); expect(screen.queryByTestId('undo-bar')).toBeNull(); }); });