// @vitest-environment jsdom import { describe, it, expect, vi } from 'vitest'; import { ShoppingCartStore } from '../../src/lib/client/shopping-cart.svelte'; type FetchMock = ReturnType; function snapshotBody(opts: { recipeIds?: number[]; uncheckedCount?: number; }) { return { recipes: (opts.recipeIds ?? []).map((id) => ({ recipe_id: id, title: `R${id}`, image_path: null, servings: 4, servings_default: 4 })), rows: [], uncheckedCount: opts.uncheckedCount ?? 0 }; } function makeFetch(responses: unknown[]): FetchMock { const queue = [...responses]; return vi.fn(async () => ({ ok: true, status: 200, json: async () => queue.shift() } as Response)); } describe('ShoppingCartStore', () => { it('refresh populates recipeIds and uncheckedCount', async () => { const fetchImpl = makeFetch([snapshotBody({ recipeIds: [1, 2], uncheckedCount: 3 })]); const store = new ShoppingCartStore(fetchImpl); await store.refresh(); expect(store.uncheckedCount).toBe(3); expect(store.isInCart(1)).toBe(true); expect(store.isInCart(2)).toBe(true); expect(store.isInCart(3)).toBe(false); expect(store.loaded).toBe(true); }); it('addRecipe posts then refreshes', async () => { const fetchImpl = makeFetch([ {}, snapshotBody({ recipeIds: [42], uncheckedCount: 5 }) ]); const store = new ShoppingCartStore(fetchImpl); await store.addRecipe(42); expect(fetchImpl.mock.calls[0][0]).toBe('/api/shopping-list/recipe'); expect(fetchImpl.mock.calls[0][1]).toMatchObject({ method: 'POST' }); expect(store.isInCart(42)).toBe(true); expect(store.uncheckedCount).toBe(5); }); it('removeRecipe deletes then refreshes', async () => { const fetchImpl = makeFetch([ {}, snapshotBody({ recipeIds: [], uncheckedCount: 0 }) ]); const store = new ShoppingCartStore(fetchImpl); await store.removeRecipe(42); expect(fetchImpl.mock.calls[0][0]).toBe('/api/shopping-list/recipe/42'); expect(fetchImpl.mock.calls[0][1]).toMatchObject({ method: 'DELETE' }); expect(store.uncheckedCount).toBe(0); }); it('refresh keeps last known state on network error', async () => { const fetchImpl = vi.fn().mockRejectedValue(new Error('offline')); const store = new ShoppingCartStore(fetchImpl); store.uncheckedCount = 7; await store.refresh(); expect(store.uncheckedCount).toBe(7); }); });