import { cleanup, renderHook, waitFor } from "@testing-library/react"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; const apiMocks = vi.hoisted(() => ({ getMarineForecast: vi.fn(), getNearestTide: vi.fn() })); vi.mock("../src/api", () => apiMocks); import { useMarineData } from "../src/hooks/useMarineData"; beforeEach(() => { apiMocks.getMarineForecast.mockResolvedValue({ source: "test", updatedAt: new Date().toISOString() }); apiMocks.getNearestTide.mockResolvedValue({ station: "test", distanceKm: 1, nextHigh: null, nextLow: null, waterLevelCurve: [], source: "test", updatedAt: new Date().toISOString() }); }); afterEach(() => { cleanup(); vi.clearAllMocks(); }); describe("useMarineData", () => { it("does not refetch weather and tide for every GPS jitter inside the same rounded cell", async () => { const { result, rerender } = renderHook( ({ position }) => useMarineData(position), { initialProps: { position: { lat: 53.201, lon: 7.101 } } } ); await waitFor(() => expect(result.current.loading).toBe(false)); expect(apiMocks.getMarineForecast).toHaveBeenCalledTimes(1); expect(apiMocks.getNearestTide).toHaveBeenCalledTimes(1); rerender({ position: { lat: 53.203, lon: 7.103 } }); await Promise.resolve(); expect(apiMocks.getMarineForecast).toHaveBeenCalledTimes(1); expect(apiMocks.getNearestTide).toHaveBeenCalledTimes(1); rerender({ position: { lat: 53.216, lon: 7.116 } }); await waitFor(() => expect(apiMocks.getMarineForecast).toHaveBeenCalledTimes(2)); expect(apiMocks.getNearestTide).toHaveBeenCalledTimes(2); }); it("keeps partial data visible and exposes the failed source separately", async () => { apiMocks.getMarineForecast.mockRejectedValueOnce(new Error("forecast offline")); const { result } = renderHook(() => useMarineData({ lat: 53.2159, lon: 6.5766 }) ); await waitFor(() => expect(result.current.loading).toBe(false)); expect(result.current.forecast).toBeNull(); expect(result.current.tide?.station).toBe("test"); expect(result.current.forecastError).toBe("Wetterdaten nicht erreichbar"); expect(result.current.tideError).toBeNull(); expect(result.current.error).toBeNull(); expect(result.current.queryPosition).toEqual({ lat: 53.22, lon: 6.58 }); expect(result.current.refreshedAt).toEqual(expect.any(String)); }); });