Files
watermaps/apps/web/tests/geolocation.test.tsx
2026-07-24 11:29:24 +02:00

62 lines
2.0 KiB
TypeScript

import { act, cleanup, renderHook } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { useGeolocation } from "../src/hooks/useGeolocation";
const originalGeolocation = Object.getOwnPropertyDescriptor(navigator, "geolocation");
afterEach(() => {
cleanup();
if (originalGeolocation) Object.defineProperty(navigator, "geolocation", originalGeolocation);
else Reflect.deleteProperty(navigator, "geolocation");
});
describe("useGeolocation", () => {
it("derives COG only after movement exceeds GPS noise and clears the watch on stop", () => {
let success: PositionCallback | undefined;
const watchPosition = vi.fn((callback: PositionCallback) => {
success = callback;
return 23;
});
const clearWatch = vi.fn();
Object.defineProperty(navigator, "geolocation", {
configurable: true,
value: { watchPosition, clearWatch, getCurrentPosition: vi.fn() }
});
const { result } = renderHook(() => useGeolocation());
act(() => result.current.start());
act(() => success?.(positionAt(53, 7, 5, 1_000)));
expect(result.current.courseDeg).toBeNull();
act(() => success?.(positionAt(53, 7.00001, 5, 2_000)));
expect(result.current.courseDeg).toBeNull();
const receivedAtMs = Date.now();
act(() => success?.(positionAt(53, 7.0001, 5, 3_000)));
expect(result.current.courseDeg).toBeCloseTo(90, 0);
expect(result.current.timestampMs).toBeGreaterThanOrEqual(receivedAtMs);
act(() => result.current.stop());
expect(clearWatch).toHaveBeenCalledWith(23);
expect(result.current.status).toBe("idle");
expect(result.current.position).toBeNull();
});
});
function positionAt(lat: number, lon: number, accuracy: number, timestamp: number): GeolocationPosition {
return {
coords: {
latitude: lat,
longitude: lon,
accuracy,
altitude: null,
altitudeAccuracy: null,
heading: null,
speed: 3,
toJSON: () => ({})
},
timestamp,
toJSON: () => ({})
};
}