Files
flights_web/src/shared/hooks/useGeoCityDefault.test.ts
T

142 lines
4.7 KiB
TypeScript

// @vitest-environment jsdom
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import { renderHook } from "@testing-library/react";
import { useGeoCityDefault } from "./useGeoCityDefault.js";
import type { IDictionaries } from "@/shared/dictionaries/index.js";
const mockDictionaries: IDictionaries = {
cities: [
{
code: "MOW",
name: "Москва",
location: { lat: 55.7558, lon: 37.6173 },
country_code: "RU",
countryName: "Russia",
has_afl_flights: true,
airports: [],
},
{
code: "LED",
name: "Санкт-Петербург",
location: { lat: 59.9311, lon: 30.3609 },
country_code: "RU",
countryName: "Russia",
has_afl_flights: true,
airports: [],
},
],
airports: [],
regions: [],
countries: [],
cityByCode: new Map([
[
"MOW",
{
code: "MOW",
name: "Москва",
location: { lat: 55.7558, lon: 37.6173 },
country_code: "RU",
countryName: "Russia",
has_afl_flights: true,
airports: [],
},
],
[
"LED",
{
code: "LED",
name: "Санкт-Петербург",
location: { lat: 59.9311, lon: 30.3609 },
country_code: "RU",
countryName: "Russia",
has_afl_flights: true,
airports: [],
},
],
]),
airportByCode: new Map(),
ruCityCodes: new Set(["MOW", "LED"]),
otherCityCodes: new Set(),
} as unknown as IDictionaries;
describe("useGeoCityDefault", () => {
let getCurrentPositionMock: ReturnType<typeof vi.fn>;
beforeEach(() => {
getCurrentPositionMock = vi.fn();
Object.defineProperty(navigator, "geolocation", {
value: { getCurrentPosition: getCurrentPositionMock },
configurable: true,
});
});
afterEach(() => { vi.restoreAllMocks(); });
it("calls onCity with nearest city code when geolocation succeeds and shouldApply returns true", () => {
getCurrentPositionMock.mockImplementation((onSuccess: (pos: GeolocationPosition) => void) => {
onSuccess({ coords: { latitude: 55.75, longitude: 37.61 } } as GeolocationPosition);
});
const onCity = vi.fn();
renderHook(() =>
useGeoCityDefault({ dictionaries: mockDictionaries, shouldApply: () => true, onCity }),
);
expect(onCity).toHaveBeenCalledWith("MOW");
});
it("does not call onCity when shouldApply returns false", () => {
getCurrentPositionMock.mockImplementation((onSuccess: (pos: GeolocationPosition) => void) => {
onSuccess({ coords: { latitude: 55.75, longitude: 37.61 } } as GeolocationPosition);
});
const onCity = vi.fn();
renderHook(() =>
useGeoCityDefault({ dictionaries: mockDictionaries, shouldApply: () => false, onCity }),
);
expect(onCity).not.toHaveBeenCalled();
});
it("is silent when geolocation permission is denied", () => {
getCurrentPositionMock.mockImplementation((_s: unknown, onErr: (err: GeolocationPositionError) => void) => {
onErr({ code: 1, message: "denied" } as GeolocationPositionError);
});
const onCity = vi.fn();
renderHook(() =>
useGeoCityDefault({ dictionaries: mockDictionaries, shouldApply: () => true, onCity }),
);
expect(onCity).not.toHaveBeenCalled();
});
it("is silent when navigator.geolocation is undefined", () => {
Object.defineProperty(navigator, "geolocation", { value: undefined, configurable: true });
const onCity = vi.fn();
renderHook(() =>
useGeoCityDefault({ dictionaries: mockDictionaries, shouldApply: () => true, onCity }),
);
expect(onCity).not.toHaveBeenCalled();
});
it("fires only once per mount even if dictionaries change", () => {
getCurrentPositionMock.mockImplementation((onSuccess: (pos: GeolocationPosition) => void) => {
onSuccess({ coords: { latitude: 55.75, longitude: 37.61 } } as GeolocationPosition);
});
const onCity = vi.fn();
const { rerender } = renderHook(
({ dict }: { dict: IDictionaries }) =>
useGeoCityDefault({ dictionaries: dict, shouldApply: () => true, onCity }),
{ initialProps: { dict: mockDictionaries } },
);
rerender({ dict: { ...mockDictionaries } as IDictionaries });
expect(onCity).toHaveBeenCalledTimes(1);
});
it("waits for dictionaries to be non-null before computing city", () => {
getCurrentPositionMock.mockImplementation((onSuccess: (pos: GeolocationPosition) => void) => {
onSuccess({ coords: { latitude: 55.75, longitude: 37.61 } } as GeolocationPosition);
});
const onCity = vi.fn();
renderHook(() =>
useGeoCityDefault({ dictionaries: null, shouldApply: () => true, onCity }),
);
expect(onCity).not.toHaveBeenCalled();
});
});