mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 15:36:33 +00:00
Add "use my current location" button to Journey entry editor
- One-shot getCurrentPositionOnce() helper in useGeolocation with typed error codes (unsupported, insecure context, permission denied, timeout) - Button inside the entry editor Location field fills coordinates from the device and reverse-geocodes the place name via /api/maps/reverse, falling back to formatted coordinates when no name is available - New journey.editor.* i18n keys in all 23 locales - Unit tests for the helper and component tests for the button flow
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// FE-HOOK-GEO-001 to FE-HOOK-GEO-014
|
||||
// FE-HOOK-GEO-001 to FE-HOOK-GEO-023
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { useGeolocation } from './useGeolocation';
|
||||
import { GeoOnceError, getCurrentPositionOnce, useGeolocation } from './useGeolocation';
|
||||
|
||||
type WatchSuccess = (pos: GeolocationPosition) => void;
|
||||
type WatchFailure = (err: GeolocationPositionError) => void;
|
||||
@@ -259,3 +259,52 @@ describe('useGeolocation', () => {
|
||||
expect(clearWatch).toHaveBeenCalledWith(7);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCurrentPositionOnce', () => {
|
||||
/** Swaps the watch-based stub from beforeEach for a one-shot one. */
|
||||
function stubOnce(getCurrentPosition: (ok: PositionCallback, fail?: PositionErrorCallback) => void): void {
|
||||
Object.defineProperty(navigator, 'geolocation', {
|
||||
value: { getCurrentPosition: vi.fn(getCurrentPosition) },
|
||||
configurable: true,
|
||||
});
|
||||
}
|
||||
|
||||
it('FE-HOOK-GEO-020: resolves a mapped position from a single fix', async () => {
|
||||
stubOnce(ok => ok(fix({ latitude: 41.9, longitude: 12.5, accuracy: 10 })));
|
||||
|
||||
await expect(getCurrentPositionOnce()).resolves.toEqual({
|
||||
lat: 41.9, lng: 12.5, accuracy: 10, heading: null, speed: null, timestamp: 1_700_000_000_000,
|
||||
});
|
||||
});
|
||||
|
||||
it('FE-HOOK-GEO-021: rejects as unsupported when the browser has no geolocation', async () => {
|
||||
// Same guard as FE-HOOK-GEO-008: the key has to go away entirely.
|
||||
delete (navigator as { geolocation?: Geolocation }).geolocation;
|
||||
|
||||
await expect(getCurrentPositionOnce()).rejects.toMatchObject({
|
||||
name: 'GeoOnceError',
|
||||
code: 'unsupported',
|
||||
});
|
||||
});
|
||||
|
||||
it('FE-HOOK-GEO-022: rejects outside a secure context without asking the browser', async () => {
|
||||
const getCurrentPosition = vi.fn();
|
||||
stubOnce(getCurrentPosition);
|
||||
vi.stubGlobal('isSecureContext', false);
|
||||
|
||||
await expect(getCurrentPositionOnce()).rejects.toMatchObject({ code: 'insecure-context' });
|
||||
expect(getCurrentPosition).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each([
|
||||
[1, 'permission-denied'],
|
||||
[2, 'unavailable'],
|
||||
[3, 'timeout'],
|
||||
])('FE-HOOK-GEO-023: maps error code %i to "%s"', async (code, expected) => {
|
||||
stubOnce((_ok, fail) => fail?.(geoError(code)));
|
||||
|
||||
const rejection = await getCurrentPositionOnce().catch((e: unknown) => e);
|
||||
expect(rejection).toBeInstanceOf(GeoOnceError);
|
||||
expect((rejection as GeoOnceError).code).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user