|
1 | | -import {test, beforeAll, beforeEach, expect, vi, createMap} from '../../../util/vitest'; |
| 1 | +import {test, describe, beforeAll, beforeEach, afterEach, expect, vi, createMap} from '../../../util/vitest'; |
2 | 2 | import GeolocateControl from '../../../../src/ui/control/geolocate_control'; |
3 | 3 | import {mockGeolocation} from '../../../util/mock_geolocation'; |
4 | 4 |
|
| 5 | +import type {GeolocateControlOptions} from '../../../../src/ui/control/geolocate_control'; |
| 6 | + |
5 | 7 | beforeAll(() => { |
6 | 8 | mockGeolocation.use(); |
7 | 9 | }); |
@@ -975,3 +977,133 @@ test('GeolocateControl button click centers camera even when followUserLocation |
975 | 977 | mockGeolocation.send({latitude: 10, longitude: 20, accuracy: 30}); |
976 | 978 | }); |
977 | 979 | }); |
| 980 | + |
| 981 | +describe('GeolocateControl geolocation timeout', () => { |
| 982 | + const DEFAULT_TIMEOUT_MS = 6000; |
| 983 | + const CUSTOM_TIMEOUT_MS = 3000; |
| 984 | + |
| 985 | + async function setupGeolocationTimeoutTest(controlOptions: GeolocateControlOptions = {}) { |
| 986 | + vi.useFakeTimers(); |
| 987 | + const map = createMap(); |
| 988 | + const geolocate = new GeolocateControl({ |
| 989 | + trackUserLocation: true, |
| 990 | + ...controlOptions |
| 991 | + }); |
| 992 | + map.addControl(geolocate); |
| 993 | + // Flush pending microtasks from control setup |
| 994 | + await vi.advanceTimersByTimeAsync(0); |
| 995 | + const errorHandler = vi.fn(); |
| 996 | + geolocate.on('error', errorHandler); |
| 997 | + return {map, geolocate, errorHandler}; |
| 998 | + } |
| 999 | + |
| 1000 | + afterEach(() => { |
| 1001 | + vi.useRealTimers(); |
| 1002 | + }); |
| 1003 | + |
| 1004 | + test('triggers error when no response', async () => { |
| 1005 | + const {geolocate, errorHandler} = await setupGeolocationTimeoutTest(); |
| 1006 | + |
| 1007 | + geolocate.trigger(); |
| 1008 | + |
| 1009 | + await vi.advanceTimersByTimeAsync(DEFAULT_TIMEOUT_MS); |
| 1010 | + |
| 1011 | + expect(errorHandler).toHaveBeenCalledTimes(1); |
| 1012 | + const error = errorHandler.mock.calls[0][0] as GeolocationPositionError; |
| 1013 | + expect(error.code).toEqual(3); // TIMEOUT |
| 1014 | + expect(error.message).toEqual('Geolocation request timed out'); |
| 1015 | + }); |
| 1016 | + |
| 1017 | + test('respects custom positionOptions.timeout', async () => { |
| 1018 | + const {errorHandler, geolocate} = await setupGeolocationTimeoutTest({ |
| 1019 | + positionOptions: {timeout: CUSTOM_TIMEOUT_MS} |
| 1020 | + }); |
| 1021 | + |
| 1022 | + geolocate.trigger(); |
| 1023 | + |
| 1024 | + // Advance less than custom timeout |
| 1025 | + await vi.advanceTimersByTimeAsync(CUSTOM_TIMEOUT_MS - 1000); |
| 1026 | + expect(errorHandler).not.toHaveBeenCalled(); |
| 1027 | + |
| 1028 | + // Advance past custom timeout |
| 1029 | + await vi.advanceTimersByTimeAsync(1000); |
| 1030 | + expect(errorHandler).toHaveBeenCalledTimes(1); |
| 1031 | + const error = errorHandler.mock.calls[0][0] as GeolocationPositionError; |
| 1032 | + expect(error.code).toEqual(3); |
| 1033 | + }); |
| 1034 | + |
| 1035 | + test('cleared on success', async () => { |
| 1036 | + const {geolocate, errorHandler} = await setupGeolocationTimeoutTest(); |
| 1037 | + |
| 1038 | + geolocate.trigger(); |
| 1039 | + |
| 1040 | + // Send success before timeout |
| 1041 | + await vi.advanceTimersByTimeAsync(CUSTOM_TIMEOUT_MS); |
| 1042 | + mockGeolocation.send({latitude: 10, longitude: 20, accuracy: 30}); |
| 1043 | + |
| 1044 | + // Advance well past the timeout |
| 1045 | + await vi.advanceTimersByTimeAsync(DEFAULT_TIMEOUT_MS); |
| 1046 | + |
| 1047 | + expect(errorHandler).not.toHaveBeenCalled(); |
| 1048 | + }); |
| 1049 | + |
| 1050 | + test('cleared on error', async () => { |
| 1051 | + const {geolocate, errorHandler} = await setupGeolocationTimeoutTest(); |
| 1052 | + |
| 1053 | + geolocate.trigger(); |
| 1054 | + |
| 1055 | + // Send error before timeout |
| 1056 | + await vi.advanceTimersByTimeAsync(CUSTOM_TIMEOUT_MS); |
| 1057 | + mockGeolocation.changeError({code: 2, message: 'position unavailable'}); |
| 1058 | + |
| 1059 | + // Advance well past the timeout |
| 1060 | + await vi.advanceTimersByTimeAsync(DEFAULT_TIMEOUT_MS); |
| 1061 | + |
| 1062 | + // Only one error (the explicit one, not the timeout) |
| 1063 | + expect(errorHandler).toHaveBeenCalledTimes(1); |
| 1064 | + const error = errorHandler.mock.calls[0][0] as GeolocationPositionError; |
| 1065 | + expect(error.code).toEqual(2); |
| 1066 | + }); |
| 1067 | + |
| 1068 | + test('cleared on remove', async () => { |
| 1069 | + const {map, geolocate, errorHandler} = await setupGeolocationTimeoutTest(); |
| 1070 | + |
| 1071 | + geolocate.trigger(); |
| 1072 | + |
| 1073 | + // Remove control before timeout |
| 1074 | + await vi.advanceTimersByTimeAsync(CUSTOM_TIMEOUT_MS); |
| 1075 | + map.removeControl(geolocate); |
| 1076 | + |
| 1077 | + // Advance well past the timeout |
| 1078 | + await vi.advanceTimersByTimeAsync(DEFAULT_TIMEOUT_MS); |
| 1079 | + |
| 1080 | + expect(errorHandler).not.toHaveBeenCalled(); |
| 1081 | + }); |
| 1082 | + |
| 1083 | + test('no timeout when positionOptions.timeout is 0', async () => { |
| 1084 | + const {geolocate, errorHandler} = await setupGeolocationTimeoutTest({ |
| 1085 | + positionOptions: {timeout: 0} |
| 1086 | + }); |
| 1087 | + |
| 1088 | + geolocate.trigger(); |
| 1089 | + |
| 1090 | + // Advance a very long time |
| 1091 | + await vi.advanceTimersByTimeAsync(60000); |
| 1092 | + |
| 1093 | + expect(errorHandler).not.toHaveBeenCalled(); |
| 1094 | + }); |
| 1095 | + |
| 1096 | + test('works in one-time mode', async () => { |
| 1097 | + const {geolocate, errorHandler} = await setupGeolocationTimeoutTest({ |
| 1098 | + trackUserLocation: false |
| 1099 | + }); |
| 1100 | + |
| 1101 | + geolocate.trigger(); |
| 1102 | + |
| 1103 | + await vi.advanceTimersByTimeAsync(DEFAULT_TIMEOUT_MS); |
| 1104 | + |
| 1105 | + expect(errorHandler).toHaveBeenCalledTimes(1); |
| 1106 | + const error = errorHandler.mock.calls[0][0] as GeolocationPositionError; |
| 1107 | + expect(error.code).toEqual(3); // TIMEOUT |
| 1108 | + }); |
| 1109 | +}); |
0 commit comments