homeio
PublikFork dari doctor-io/homeio
useCurrentWeather.test.tsx
/* @vitest-environment jsdom */
import { renderHook, waitFor } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { useCurrentWeather } from "@/modules/system/hooks/useCurrentWeather";
import { createTestQueryClient, createWrapper } from "@/test/query-client-wrapper";
const mockUseUserLocation = vi.fn();
vi.mock("@/modules/system/hooks/useUserLocation", () => ({
useUserLocation: () => mockUseUserLocation(),
}));
describe("useCurrentWeather", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("loads weather from Open-Meteo when browser location is available", async () => {
mockUseUserLocation.mockReturnValue({
location: {
latitude: 36.8,
longitude: 10.1,
city: "Tunis",
country: "Tunisia",
},
loading: false,
error: null,
permissionStatus: "granted",
requestLocation: vi.fn(),
setManualLocation: vi.fn(),
});
vi.stubGlobal(
"fetch",
vi.fn().mockResolvedValue({
ok: true,
json: async () => ({
current: {
temperature_2m: 21.4,
apparent_temperature: 20.6,
relative_humidity_2m: 67,
wind_speed_10m: 18.5,
weather_code: 2,
},
daily: {
time: [
"2026-02-22",
"2026-02-23",
"2026-02-24",
"2026-02-25",
"2026-02-26",
"2026-02-27",
],
weather_code: [0, 1, 2, 3, 61, 95],
temperature_2m_max: [20, 21, 22, 23, 24, 25],
temperature_2m_min: [11, 12, 13, 14, 15, 16],
},
}),
}),
);
const client = createTestQueryClient();
const { result } = renderHook(() => useCurrentWeather(), {
wrapper: createWrapper(client),
});
await waitFor(() => {
expect(result.current.isSuccess).toBe(true);
});
const [weatherRequestUrl, weatherRequestInit] = vi.mocked(fetch).mock.calls[0] as [
string,
RequestInit,
];
const weatherUrl = new URL(weatherRequestUrl);
expect(weatherUrl.origin + weatherUrl.pathname).toBe("https://api.open-meteo.com/v1/forecast");
expect(weatherUrl.searchParams.get("latitude")).toBe("36.8");
expect(weatherUrl.searchParams.get("longitude")).toBe("10.1");
expect(weatherUrl.searchParams.get("timezone")).toBe("auto");
expect(weatherUrl.searchParams.get("current")).toBe(
"temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,weather_code",
);
expect(weatherUrl.searchParams.get("daily")).toBe(
"weather_code,temperature_2m_max,temperature_2m_min",
);
expect(weatherRequestInit).toEqual({
cache: "no-store",
});
expect(vi.mocked(fetch)).toHaveBeenCalledTimes(1);
expect(result.current.data?.source).toBe("navigator");
expect(result.current.data?.location.label).toBe("Tunis, Tunisia");
expect(result.current.data?.current.condition).toBe("Partly cloudy");
expect(result.current.data?.dailyForecast).toHaveLength(5);
expect(result.current.data?.dailyForecast[0]?.condition).toBe("Mainly clear");
});
it("does not fetch weather when location is unavailable", async () => {
mockUseUserLocation.mockReturnValue({
location: null,
loading: false,
error: "Location permission denied",
permissionStatus: "denied",
requestLocation: vi.fn(),
setManualLocation: vi.fn(),
});
vi.stubGlobal("fetch", vi.fn());
const client = createTestQueryClient();
const { result } = renderHook(() => useCurrentWeather(), {
wrapper: createWrapper(client),
});
await waitFor(() => {
expect(result.current.fetchStatus).toBe("idle");
});
expect(result.current.data).toBeUndefined();
expect(fetch).not.toHaveBeenCalled();
});
});