@trackunit/react-test-setup 1.8.58 → 1.8.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs.js CHANGED
@@ -125,8 +125,10 @@ const setupGoogleMaps = () => {
125
125
  const InfoWindowMock = jest.fn(props => {
126
126
  return jsxRuntime.jsx("div", { "data-testid": "google-info-window", children: props.children });
127
127
  });
128
+ const APIProviderMock = jest.fn(props => jsxRuntime.jsx(jsxRuntime.Fragment, { children: props.children }));
128
129
  return {
129
130
  ...originalModule,
131
+ APIProvider: APIProviderMock,
130
132
  useApiLoadingStatus: () => APILoadingStatus.LOADED,
131
133
  useApiIsLoaded: () => true,
132
134
  useMap: () => map,
@@ -219,6 +221,227 @@ class MockIntersectionObserver {
219
221
  }
220
222
  }
221
223
 
224
+ /**
225
+ * Creates a new listener registry for tracking event subscriptions.
226
+ */
227
+ const createListenerRegistry = () => ({
228
+ regular: new Map(),
229
+ once: new Map(),
230
+ });
231
+ /**
232
+ * Mock LngLat class matching Mapbox GL's interface.
233
+ * Returns actual objects with lng/lat properties rather than class instances.
234
+ */
235
+ const createMockLngLat = (lng, lat) => ({
236
+ lng,
237
+ lat,
238
+ wrap: () => createMockLngLat(lng, lat),
239
+ toArray: () => [lng, lat],
240
+ distanceTo: () => 0,
241
+ toBounds: () => createMockLngLatBounds(lng - 1, lat - 1, lng + 1, lat + 1),
242
+ toString: () => `LngLat(${lng}, ${lat})`,
243
+ toEcef: () => [0, 0, 0],
244
+ });
245
+ /**
246
+ * Mock LngLatBounds matching Mapbox GL's interface.
247
+ */
248
+ const createMockLngLatBounds = (west, south, east, north) => {
249
+ const sw = createMockLngLat(west, south);
250
+ const ne = createMockLngLat(east, north);
251
+ const bounds = {
252
+ _sw: sw,
253
+ _ne: ne,
254
+ getSouthWest: () => sw,
255
+ getNorthEast: () => ne,
256
+ getNorthWest: () => createMockLngLat(west, north),
257
+ getSouthEast: () => createMockLngLat(east, south),
258
+ getWest: () => west,
259
+ getSouth: () => south,
260
+ getEast: () => east,
261
+ getNorth: () => north,
262
+ getCenter: () => createMockLngLat((west + east) / 2, (south + north) / 2),
263
+ toArray: () => [
264
+ [west, south],
265
+ [east, north],
266
+ ],
267
+ toString: () => `LngLatBounds(${sw.toString()}, ${ne.toString()})`,
268
+ isEmpty: () => false,
269
+ contains: () => true,
270
+ extend: () => bounds,
271
+ setNorthEast: () => bounds,
272
+ setSouthWest: () => bounds,
273
+ };
274
+ return bounds;
275
+ };
276
+ /**
277
+ * Creates a mock Mapbox map instance for unit testing adapter instances directly.
278
+ *
279
+ * Use this when you need fine-grained control over mock behavior, such as:
280
+ * - Testing adapter instance methods (connect, setCenter, etc.)
281
+ * - Simulating map events (idle, click, movestart)
282
+ * - Verifying method calls on the map
283
+ *
284
+ * @example
285
+ * ```typescript
286
+ * import { createMockMapboxMap } from "@trackunit/react-test-setup";
287
+ *
288
+ * it("should connect to map", () => {
289
+ * const { map, triggerEvent } = createMockMapboxMap();
290
+ * const adapter = new MapboxAdapterInstance(config);
291
+ *
292
+ * adapter.connect(map as unknown as mapboxgl.Map);
293
+ * triggerEvent("idle");
294
+ *
295
+ * expect(adapter.getState().isReady).toBe(true);
296
+ * });
297
+ * ```
298
+ */
299
+ const createMockMapboxMap = () => {
300
+ const registry = createListenerRegistry();
301
+ const addListener = (store, event, handler) => {
302
+ const handlers = store.get(event);
303
+ if (handlers === undefined) {
304
+ store.set(event, new Set([handler]));
305
+ }
306
+ else {
307
+ handlers.add(handler);
308
+ }
309
+ return map;
310
+ };
311
+ const map = {
312
+ on: jest.fn((event, handler) => addListener(registry.regular, event, handler)),
313
+ once: jest.fn((event, handler) => addListener(registry.once, event, handler)),
314
+ off: jest.fn((event, handler) => {
315
+ registry.regular.get(event)?.delete(handler);
316
+ return map;
317
+ }),
318
+ remove: jest.fn(),
319
+ getCenter: jest.fn(() => createMockLngLat(0, 0)),
320
+ getZoom: jest.fn(() => 10),
321
+ getBounds: jest.fn(() => createMockLngLatBounds(-1, -1, 1, 1)),
322
+ setCenter: jest.fn((_center) => map),
323
+ setZoom: jest.fn((_zoom) => map),
324
+ panTo: jest.fn((_lngLat) => map),
325
+ panBy: jest.fn((_offset) => map),
326
+ fitBounds: jest.fn((_bounds, _options) => map),
327
+ setStyle: jest.fn((_style) => map),
328
+ isStyleLoaded: jest.fn(() => true),
329
+ isMoving: jest.fn(() => false),
330
+ addSource: jest.fn(),
331
+ removeSource: jest.fn(),
332
+ getSource: jest.fn(() => undefined),
333
+ addLayer: jest.fn(),
334
+ removeLayer: jest.fn(),
335
+ getLayer: jest.fn(() => undefined),
336
+ hasImage: jest.fn(() => false),
337
+ addImage: jest.fn(),
338
+ getCanvas: jest.fn(() => ({ style: {} })),
339
+ };
340
+ const triggerEvent = (eventName, event) => {
341
+ // Fire regular listeners
342
+ const regularHandlers = registry.regular.get(eventName);
343
+ if (regularHandlers !== undefined) {
344
+ regularHandlers.forEach((handler) => handler(event));
345
+ }
346
+ // Fire and clear once listeners
347
+ const onceHandlers = registry.once.get(eventName);
348
+ if (onceHandlers !== undefined) {
349
+ onceHandlers.forEach((handler) => handler(event));
350
+ registry.once.delete(eventName);
351
+ }
352
+ };
353
+ return { map, triggerEvent };
354
+ };
355
+ /**
356
+ * Mock click event matching Mapbox GL's MapMouseEvent shape.
357
+ * Use with triggerEvent("click", createMockClickEvent(...))
358
+ *
359
+ * Note: The `point` property is intentionally omitted because:
360
+ * 1. It requires the full Point class from @mapbox/point-geometry with 30+ methods
361
+ * 2. No tests currently use the point property
362
+ * 3. Since this returns Partial<MapMouseEvent>, point is optional
363
+ */
364
+ const createMockClickEvent = (lng, lat) => ({
365
+ lngLat: createMockLngLat(lng, lat),
366
+ originalEvent: new MouseEvent("click"),
367
+ type: "click",
368
+ });
369
+ /**
370
+ * Sets up mocks for Mapbox GL JS in testing environments.
371
+ *
372
+ * This function mocks the mapbox-gl module with functional mock implementations,
373
+ * allowing tests of map-dependent components to run without WebGL or actual
374
+ * network requests.
375
+ *
376
+ * Key features:
377
+ * - Mocks mapboxgl.Map constructor to return a trackable mock instance
378
+ * - Auto-fires the "load" event after map construction (for renderer tests)
379
+ * - Provides mock implementations for all common map methods
380
+ * - Resets mock state between tests via beforeEach
381
+ *
382
+ * @example
383
+ * ```typescript
384
+ * // In your test file
385
+ * import { setupMapbox } from "@trackunit/react-test-setup";
386
+ *
387
+ * setupMapbox();
388
+ *
389
+ * describe("MapboxRenderer", () => {
390
+ * it("renders map when loaded", async () => {
391
+ * // The mock will auto-fire "load" event
392
+ * render(<MapboxRenderer adapterInstance={instance} />);
393
+ * await waitFor(() => expect(screen.getByRole("application")).toBeInTheDocument());
394
+ * });
395
+ * });
396
+ * ```
397
+ */
398
+ const setupMapbox = () => {
399
+ jest.mock("mapbox-gl", () => {
400
+ const createMockMapWithAutoLoad = () => {
401
+ const result = createMockMapboxMap();
402
+ // Override "on" to auto-fire "load" event for renderer compatibility
403
+ const originalOn = result.map.on;
404
+ result.map.on = jest.fn((event, handler) => {
405
+ originalOn(event, handler);
406
+ // Auto-fire load event asynchronously to simulate real Mapbox behavior
407
+ if (event === "load") {
408
+ setTimeout(() => handler(undefined), 0);
409
+ }
410
+ return result.map;
411
+ });
412
+ return result.map;
413
+ };
414
+ const createMockMarker = () => {
415
+ const el = document.createElement("div");
416
+ const marker = {
417
+ setLngLat: jest.fn(() => marker),
418
+ addTo: jest.fn(() => marker),
419
+ remove: jest.fn(),
420
+ getElement: jest.fn(() => el),
421
+ setOffset: jest.fn(() => marker),
422
+ getLngLat: jest.fn(() => createMockLngLat(0, 0)),
423
+ setPopup: jest.fn(() => marker),
424
+ getPopup: jest.fn(() => null),
425
+ togglePopup: jest.fn(() => marker),
426
+ setDraggable: jest.fn(() => marker),
427
+ isDraggable: jest.fn(() => false),
428
+ };
429
+ return marker;
430
+ };
431
+ return {
432
+ __esModule: true,
433
+ default: {
434
+ Map: jest.fn(createMockMapWithAutoLoad),
435
+ Marker: jest.fn(createMockMarker),
436
+ accessToken: "",
437
+ },
438
+ };
439
+ });
440
+ beforeEach(() => {
441
+ jest.clearAllMocks();
442
+ });
443
+ };
444
+
222
445
  /**
223
446
  * Mocks the window.matchMedia API for testing environments.
224
447
  *
@@ -737,6 +960,7 @@ const setupWebStreams = () => {
737
960
  * - Canvas API mocks
738
961
  * - Console error reporting to fail tests (includes automatic CSS parser error suppression)
739
962
  * - Google Maps API and components mocks
963
+ * - Mapbox GL JS mocks
740
964
  * - React Helmet mocks
741
965
  * - IntersectionObserver mocks
742
966
  * - MatchMedia API mocks
@@ -767,6 +991,7 @@ const setupAllMocks = (options = {}) => {
767
991
  setupCanvasMock();
768
992
  setupFailOnConsole(options.failOnConsoleOptions);
769
993
  setupGoogleMaps();
994
+ setupMapbox();
770
995
  setupHelmetMock();
771
996
  setupIntersectionObserver();
772
997
  setupMatchMediaMock();
@@ -898,6 +1123,8 @@ const setupTanstackReactRouter = () => {
898
1123
  }));
899
1124
  };
900
1125
 
1126
+ exports.createMockClickEvent = createMockClickEvent;
1127
+ exports.createMockMapboxMap = createMockMapboxMap;
901
1128
  exports.setupAllMocks = setupAllMocks;
902
1129
  exports.setupBasicMocks = setupBasicMocks;
903
1130
  exports.setupCanvasMock = setupCanvasMock;
@@ -906,6 +1133,7 @@ exports.setupFailOnConsole = setupFailOnConsole;
906
1133
  exports.setupGoogleMaps = setupGoogleMaps;
907
1134
  exports.setupHelmetMock = setupHelmetMock;
908
1135
  exports.setupIntersectionObserver = setupIntersectionObserver;
1136
+ exports.setupMapbox = setupMapbox;
909
1137
  exports.setupMatchMediaMock = setupMatchMediaMock;
910
1138
  exports.setupReactTestingLibrary = setupReactTestingLibrary;
911
1139
  exports.setupReactVirtualizedAutoSizer = setupReactVirtualizedAutoSizer;
package/index.esm.js CHANGED
@@ -104,8 +104,10 @@ const setupGoogleMaps = () => {
104
104
  const InfoWindowMock = jest.fn(props => {
105
105
  return jsx("div", { "data-testid": "google-info-window", children: props.children });
106
106
  });
107
+ const APIProviderMock = jest.fn(props => jsx(Fragment, { children: props.children }));
107
108
  return {
108
109
  ...originalModule,
110
+ APIProvider: APIProviderMock,
109
111
  useApiLoadingStatus: () => APILoadingStatus.LOADED,
110
112
  useApiIsLoaded: () => true,
111
113
  useMap: () => map,
@@ -198,6 +200,227 @@ class MockIntersectionObserver {
198
200
  }
199
201
  }
200
202
 
203
+ /**
204
+ * Creates a new listener registry for tracking event subscriptions.
205
+ */
206
+ const createListenerRegistry = () => ({
207
+ regular: new Map(),
208
+ once: new Map(),
209
+ });
210
+ /**
211
+ * Mock LngLat class matching Mapbox GL's interface.
212
+ * Returns actual objects with lng/lat properties rather than class instances.
213
+ */
214
+ const createMockLngLat = (lng, lat) => ({
215
+ lng,
216
+ lat,
217
+ wrap: () => createMockLngLat(lng, lat),
218
+ toArray: () => [lng, lat],
219
+ distanceTo: () => 0,
220
+ toBounds: () => createMockLngLatBounds(lng - 1, lat - 1, lng + 1, lat + 1),
221
+ toString: () => `LngLat(${lng}, ${lat})`,
222
+ toEcef: () => [0, 0, 0],
223
+ });
224
+ /**
225
+ * Mock LngLatBounds matching Mapbox GL's interface.
226
+ */
227
+ const createMockLngLatBounds = (west, south, east, north) => {
228
+ const sw = createMockLngLat(west, south);
229
+ const ne = createMockLngLat(east, north);
230
+ const bounds = {
231
+ _sw: sw,
232
+ _ne: ne,
233
+ getSouthWest: () => sw,
234
+ getNorthEast: () => ne,
235
+ getNorthWest: () => createMockLngLat(west, north),
236
+ getSouthEast: () => createMockLngLat(east, south),
237
+ getWest: () => west,
238
+ getSouth: () => south,
239
+ getEast: () => east,
240
+ getNorth: () => north,
241
+ getCenter: () => createMockLngLat((west + east) / 2, (south + north) / 2),
242
+ toArray: () => [
243
+ [west, south],
244
+ [east, north],
245
+ ],
246
+ toString: () => `LngLatBounds(${sw.toString()}, ${ne.toString()})`,
247
+ isEmpty: () => false,
248
+ contains: () => true,
249
+ extend: () => bounds,
250
+ setNorthEast: () => bounds,
251
+ setSouthWest: () => bounds,
252
+ };
253
+ return bounds;
254
+ };
255
+ /**
256
+ * Creates a mock Mapbox map instance for unit testing adapter instances directly.
257
+ *
258
+ * Use this when you need fine-grained control over mock behavior, such as:
259
+ * - Testing adapter instance methods (connect, setCenter, etc.)
260
+ * - Simulating map events (idle, click, movestart)
261
+ * - Verifying method calls on the map
262
+ *
263
+ * @example
264
+ * ```typescript
265
+ * import { createMockMapboxMap } from "@trackunit/react-test-setup";
266
+ *
267
+ * it("should connect to map", () => {
268
+ * const { map, triggerEvent } = createMockMapboxMap();
269
+ * const adapter = new MapboxAdapterInstance(config);
270
+ *
271
+ * adapter.connect(map as unknown as mapboxgl.Map);
272
+ * triggerEvent("idle");
273
+ *
274
+ * expect(adapter.getState().isReady).toBe(true);
275
+ * });
276
+ * ```
277
+ */
278
+ const createMockMapboxMap = () => {
279
+ const registry = createListenerRegistry();
280
+ const addListener = (store, event, handler) => {
281
+ const handlers = store.get(event);
282
+ if (handlers === undefined) {
283
+ store.set(event, new Set([handler]));
284
+ }
285
+ else {
286
+ handlers.add(handler);
287
+ }
288
+ return map;
289
+ };
290
+ const map = {
291
+ on: jest.fn((event, handler) => addListener(registry.regular, event, handler)),
292
+ once: jest.fn((event, handler) => addListener(registry.once, event, handler)),
293
+ off: jest.fn((event, handler) => {
294
+ registry.regular.get(event)?.delete(handler);
295
+ return map;
296
+ }),
297
+ remove: jest.fn(),
298
+ getCenter: jest.fn(() => createMockLngLat(0, 0)),
299
+ getZoom: jest.fn(() => 10),
300
+ getBounds: jest.fn(() => createMockLngLatBounds(-1, -1, 1, 1)),
301
+ setCenter: jest.fn((_center) => map),
302
+ setZoom: jest.fn((_zoom) => map),
303
+ panTo: jest.fn((_lngLat) => map),
304
+ panBy: jest.fn((_offset) => map),
305
+ fitBounds: jest.fn((_bounds, _options) => map),
306
+ setStyle: jest.fn((_style) => map),
307
+ isStyleLoaded: jest.fn(() => true),
308
+ isMoving: jest.fn(() => false),
309
+ addSource: jest.fn(),
310
+ removeSource: jest.fn(),
311
+ getSource: jest.fn(() => undefined),
312
+ addLayer: jest.fn(),
313
+ removeLayer: jest.fn(),
314
+ getLayer: jest.fn(() => undefined),
315
+ hasImage: jest.fn(() => false),
316
+ addImage: jest.fn(),
317
+ getCanvas: jest.fn(() => ({ style: {} })),
318
+ };
319
+ const triggerEvent = (eventName, event) => {
320
+ // Fire regular listeners
321
+ const regularHandlers = registry.regular.get(eventName);
322
+ if (regularHandlers !== undefined) {
323
+ regularHandlers.forEach((handler) => handler(event));
324
+ }
325
+ // Fire and clear once listeners
326
+ const onceHandlers = registry.once.get(eventName);
327
+ if (onceHandlers !== undefined) {
328
+ onceHandlers.forEach((handler) => handler(event));
329
+ registry.once.delete(eventName);
330
+ }
331
+ };
332
+ return { map, triggerEvent };
333
+ };
334
+ /**
335
+ * Mock click event matching Mapbox GL's MapMouseEvent shape.
336
+ * Use with triggerEvent("click", createMockClickEvent(...))
337
+ *
338
+ * Note: The `point` property is intentionally omitted because:
339
+ * 1. It requires the full Point class from @mapbox/point-geometry with 30+ methods
340
+ * 2. No tests currently use the point property
341
+ * 3. Since this returns Partial<MapMouseEvent>, point is optional
342
+ */
343
+ const createMockClickEvent = (lng, lat) => ({
344
+ lngLat: createMockLngLat(lng, lat),
345
+ originalEvent: new MouseEvent("click"),
346
+ type: "click",
347
+ });
348
+ /**
349
+ * Sets up mocks for Mapbox GL JS in testing environments.
350
+ *
351
+ * This function mocks the mapbox-gl module with functional mock implementations,
352
+ * allowing tests of map-dependent components to run without WebGL or actual
353
+ * network requests.
354
+ *
355
+ * Key features:
356
+ * - Mocks mapboxgl.Map constructor to return a trackable mock instance
357
+ * - Auto-fires the "load" event after map construction (for renderer tests)
358
+ * - Provides mock implementations for all common map methods
359
+ * - Resets mock state between tests via beforeEach
360
+ *
361
+ * @example
362
+ * ```typescript
363
+ * // In your test file
364
+ * import { setupMapbox } from "@trackunit/react-test-setup";
365
+ *
366
+ * setupMapbox();
367
+ *
368
+ * describe("MapboxRenderer", () => {
369
+ * it("renders map when loaded", async () => {
370
+ * // The mock will auto-fire "load" event
371
+ * render(<MapboxRenderer adapterInstance={instance} />);
372
+ * await waitFor(() => expect(screen.getByRole("application")).toBeInTheDocument());
373
+ * });
374
+ * });
375
+ * ```
376
+ */
377
+ const setupMapbox = () => {
378
+ jest.mock("mapbox-gl", () => {
379
+ const createMockMapWithAutoLoad = () => {
380
+ const result = createMockMapboxMap();
381
+ // Override "on" to auto-fire "load" event for renderer compatibility
382
+ const originalOn = result.map.on;
383
+ result.map.on = jest.fn((event, handler) => {
384
+ originalOn(event, handler);
385
+ // Auto-fire load event asynchronously to simulate real Mapbox behavior
386
+ if (event === "load") {
387
+ setTimeout(() => handler(undefined), 0);
388
+ }
389
+ return result.map;
390
+ });
391
+ return result.map;
392
+ };
393
+ const createMockMarker = () => {
394
+ const el = document.createElement("div");
395
+ const marker = {
396
+ setLngLat: jest.fn(() => marker),
397
+ addTo: jest.fn(() => marker),
398
+ remove: jest.fn(),
399
+ getElement: jest.fn(() => el),
400
+ setOffset: jest.fn(() => marker),
401
+ getLngLat: jest.fn(() => createMockLngLat(0, 0)),
402
+ setPopup: jest.fn(() => marker),
403
+ getPopup: jest.fn(() => null),
404
+ togglePopup: jest.fn(() => marker),
405
+ setDraggable: jest.fn(() => marker),
406
+ isDraggable: jest.fn(() => false),
407
+ };
408
+ return marker;
409
+ };
410
+ return {
411
+ __esModule: true,
412
+ default: {
413
+ Map: jest.fn(createMockMapWithAutoLoad),
414
+ Marker: jest.fn(createMockMarker),
415
+ accessToken: "",
416
+ },
417
+ };
418
+ });
419
+ beforeEach(() => {
420
+ jest.clearAllMocks();
421
+ });
422
+ };
423
+
201
424
  /**
202
425
  * Mocks the window.matchMedia API for testing environments.
203
426
  *
@@ -716,6 +939,7 @@ const setupWebStreams = () => {
716
939
  * - Canvas API mocks
717
940
  * - Console error reporting to fail tests (includes automatic CSS parser error suppression)
718
941
  * - Google Maps API and components mocks
942
+ * - Mapbox GL JS mocks
719
943
  * - React Helmet mocks
720
944
  * - IntersectionObserver mocks
721
945
  * - MatchMedia API mocks
@@ -746,6 +970,7 @@ const setupAllMocks = (options = {}) => {
746
970
  setupCanvasMock();
747
971
  setupFailOnConsole(options.failOnConsoleOptions);
748
972
  setupGoogleMaps();
973
+ setupMapbox();
749
974
  setupHelmetMock();
750
975
  setupIntersectionObserver();
751
976
  setupMatchMediaMock();
@@ -877,4 +1102,4 @@ const setupTanstackReactRouter = () => {
877
1102
  }));
878
1103
  };
879
1104
 
880
- export { setupAllMocks, setupBasicMocks, setupCanvasMock, setupDefaultMocks, setupFailOnConsole, setupGoogleMaps, setupHelmetMock, setupIntersectionObserver, setupMatchMediaMock, setupReactTestingLibrary, setupReactVirtualizedAutoSizer, setupResizeObserver, setupTanstackReactRouter, setupTanstackReactVirtual, setupTimeAndLanguage, setupTimeZone, setupTranslations, setupWebStreams };
1105
+ export { createMockClickEvent, createMockMapboxMap, setupAllMocks, setupBasicMocks, setupCanvasMock, setupDefaultMocks, setupFailOnConsole, setupGoogleMaps, setupHelmetMock, setupIntersectionObserver, setupMapbox, setupMatchMediaMock, setupReactTestingLibrary, setupReactVirtualizedAutoSizer, setupResizeObserver, setupTanstackReactRouter, setupTanstackReactVirtual, setupTimeAndLanguage, setupTimeZone, setupTranslations, setupWebStreams };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@trackunit/react-test-setup",
3
3
  "description": "Test setup utilities for React applications",
4
- "version": "1.8.58",
4
+ "version": "1.8.61",
5
5
  "repository": "https://github.com/Trackunit/manager",
6
6
  "license": "SEE LICENSE IN LICENSE.txt",
7
7
  "engines": {
@@ -21,18 +21,19 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@googlemaps/jest-mocks": "2.22.6",
24
+ "@js-temporal/polyfill": "^0.5.1",
24
25
  "@react-spring/web": "9.7.5",
25
26
  "@testing-library/jest-dom": "^6.9.1",
26
27
  "@testing-library/react": "16.2.0",
27
28
  "@vis.gl/react-google-maps": "^1.7.1",
29
+ "mapbox-gl": "^3.18.1",
30
+ "jest": "30.0.5",
28
31
  "jest-canvas-mock": "^2.5.2",
29
32
  "jest-fail-on-console": "^3.1.2",
30
33
  "react": "19.0.0",
31
34
  "react-i18next": "^15.5.1",
32
35
  "react-virtualized-auto-sizer": "^1.0.20",
33
- "web-streams-polyfill": "^4.2.0",
34
- "jest": "30.0.5",
35
- "@js-temporal/polyfill": "^0.5.1"
36
+ "web-streams-polyfill": "^4.2.0"
36
37
  },
37
38
  "module": "./index.esm.js",
38
39
  "main": "./index.cjs.js",
package/preset.cjs.js CHANGED
@@ -19,7 +19,7 @@ const irisPreset = {
19
19
  "@trackunit/ui-icons/icons-sprite-micro.svg": "jest-transform-stub",
20
20
  },
21
21
  transformIgnorePatterns: [
22
- "/node_modules/(?!.*react-dnd.*|@tanstack/.*|.*dnd-core.*|.*react-calendar.*|.*get-user-locale.*|.*memoize.*|.*mimic-function.*|.*@wojtekmaj/date-utils.*)",
22
+ "/node_modules/(?!.*react-dnd.*|@tanstack[/-].*|.*dnd-core.*|.*react-calendar.*|.*get-user-locale.*|.*memoize.*|.*mimic-function.*|.*@wojtekmaj[/-]date-utils.*|.*supercluster.*|.*kdbush.*)",
23
23
  ],
24
24
  };
25
25
 
package/preset.esm.js CHANGED
@@ -15,7 +15,7 @@ const irisPreset = {
15
15
  "@trackunit/ui-icons/icons-sprite-micro.svg": "jest-transform-stub",
16
16
  },
17
17
  transformIgnorePatterns: [
18
- "/node_modules/(?!.*react-dnd.*|@tanstack/.*|.*dnd-core.*|.*react-calendar.*|.*get-user-locale.*|.*memoize.*|.*mimic-function.*|.*@wojtekmaj/date-utils.*)",
18
+ "/node_modules/(?!.*react-dnd.*|@tanstack[/-].*|.*dnd-core.*|.*react-calendar.*|.*get-user-locale.*|.*memoize.*|.*mimic-function.*|.*@wojtekmaj[/-]date-utils.*|.*supercluster.*|.*kdbush.*)",
19
19
  ],
20
20
  };
21
21
 
package/src/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export * from "./setupFailOnConsole";
6
6
  export * from "./setupGoogleMaps";
7
7
  export * from "./setupHelmetMock";
8
8
  export * from "./setupIntersectionObserver";
9
+ export * from "./setupMapbox";
9
10
  export * from "./setupMatchMediaMock";
10
11
  export * from "./setupReactTestingLibrary";
11
12
  export * from "./setupReactVirtualizedAutoSizer";
@@ -9,6 +9,7 @@ export interface SetupAllMocksOptions {
9
9
  * - Canvas API mocks
10
10
  * - Console error reporting to fail tests (includes automatic CSS parser error suppression)
11
11
  * - Google Maps API and components mocks
12
+ * - Mapbox GL JS mocks
12
13
  * - React Helmet mocks
13
14
  * - IntersectionObserver mocks
14
15
  * - MatchMedia API mocks
@@ -0,0 +1,117 @@
1
+ import type { LngLat, LngLatBounds, LngLatLike, MapMouseEvent } from "mapbox-gl";
2
+ /**
3
+ * Type for event handlers stored in our mock map's listener registry.
4
+ * Using a generic to avoid `as` assertions when retrieving handlers.
5
+ */
6
+ type MapEventHandler<TEvent = unknown> = (event: TEvent) => void;
7
+ /**
8
+ * Shape of the mock map returned by createMockMapboxMap.
9
+ * Extends the necessary parts of mapboxgl.Map for adapter testing.
10
+ */
11
+ type MockMapboxMap = {
12
+ on: jest.Mock<MockMapboxMap, [string, MapEventHandler]>;
13
+ once: jest.Mock<MockMapboxMap, [string, MapEventHandler]>;
14
+ off: jest.Mock<MockMapboxMap, [string, MapEventHandler]>;
15
+ remove: jest.Mock<void, []>;
16
+ getCenter: jest.Mock<LngLat, []>;
17
+ getZoom: jest.Mock<number, []>;
18
+ getBounds: jest.Mock<LngLatBounds, []>;
19
+ setCenter: jest.Mock<MockMapboxMap, [LngLatLike]>;
20
+ setZoom: jest.Mock<MockMapboxMap, [number]>;
21
+ panTo: jest.Mock<MockMapboxMap, [LngLatLike]>;
22
+ panBy: jest.Mock<MockMapboxMap, [[number, number]]>;
23
+ fitBounds: jest.Mock<MockMapboxMap, [LngLatBounds, unknown]>;
24
+ setStyle: jest.Mock<MockMapboxMap, [string]>;
25
+ isStyleLoaded: jest.Mock<boolean, []>;
26
+ isMoving: jest.Mock<boolean, []>;
27
+ addSource: jest.Mock;
28
+ removeSource: jest.Mock;
29
+ getSource: jest.Mock;
30
+ addLayer: jest.Mock;
31
+ removeLayer: jest.Mock;
32
+ getLayer: jest.Mock;
33
+ hasImage: jest.Mock<boolean, []>;
34
+ addImage: jest.Mock;
35
+ getCanvas: jest.Mock;
36
+ };
37
+ /**
38
+ * Return type of createMockMapboxMap - provides both the mock map
39
+ * and a triggerEvent helper for simulating Mapbox events in tests.
40
+ */
41
+ type MockMapboxMapResult = {
42
+ /**
43
+ * The mock map instance. Cast to mapboxgl.Map when passing to adapter.connect().
44
+ * We return MockMapboxMap to preserve access to jest mock methods in tests.
45
+ */
46
+ readonly map: MockMapboxMap;
47
+ /**
48
+ * Triggers an event on the mock map, calling all registered handlers.
49
+ * Handles both regular (on) and one-time (once) listeners.
50
+ */
51
+ readonly triggerEvent: <TEvent = unknown>(eventName: string, event?: TEvent) => void;
52
+ };
53
+ /**
54
+ * Creates a mock Mapbox map instance for unit testing adapter instances directly.
55
+ *
56
+ * Use this when you need fine-grained control over mock behavior, such as:
57
+ * - Testing adapter instance methods (connect, setCenter, etc.)
58
+ * - Simulating map events (idle, click, movestart)
59
+ * - Verifying method calls on the map
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * import { createMockMapboxMap } from "@trackunit/react-test-setup";
64
+ *
65
+ * it("should connect to map", () => {
66
+ * const { map, triggerEvent } = createMockMapboxMap();
67
+ * const adapter = new MapboxAdapterInstance(config);
68
+ *
69
+ * adapter.connect(map as unknown as mapboxgl.Map);
70
+ * triggerEvent("idle");
71
+ *
72
+ * expect(adapter.getState().isReady).toBe(true);
73
+ * });
74
+ * ```
75
+ */
76
+ export declare const createMockMapboxMap: () => MockMapboxMapResult;
77
+ /**
78
+ * Mock click event matching Mapbox GL's MapMouseEvent shape.
79
+ * Use with triggerEvent("click", createMockClickEvent(...))
80
+ *
81
+ * Note: The `point` property is intentionally omitted because:
82
+ * 1. It requires the full Point class from @mapbox/point-geometry with 30+ methods
83
+ * 2. No tests currently use the point property
84
+ * 3. Since this returns Partial<MapMouseEvent>, point is optional
85
+ */
86
+ export declare const createMockClickEvent: (lng: number, lat: number) => Partial<MapMouseEvent>;
87
+ /**
88
+ * Sets up mocks for Mapbox GL JS in testing environments.
89
+ *
90
+ * This function mocks the mapbox-gl module with functional mock implementations,
91
+ * allowing tests of map-dependent components to run without WebGL or actual
92
+ * network requests.
93
+ *
94
+ * Key features:
95
+ * - Mocks mapboxgl.Map constructor to return a trackable mock instance
96
+ * - Auto-fires the "load" event after map construction (for renderer tests)
97
+ * - Provides mock implementations for all common map methods
98
+ * - Resets mock state between tests via beforeEach
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * // In your test file
103
+ * import { setupMapbox } from "@trackunit/react-test-setup";
104
+ *
105
+ * setupMapbox();
106
+ *
107
+ * describe("MapboxRenderer", () => {
108
+ * it("renders map when loaded", async () => {
109
+ * // The mock will auto-fire "load" event
110
+ * render(<MapboxRenderer adapterInstance={instance} />);
111
+ * await waitFor(() => expect(screen.getByRole("application")).toBeInTheDocument());
112
+ * });
113
+ * });
114
+ * ```
115
+ */
116
+ export declare const setupMapbox: () => void;
117
+ export {};