@trackunit/react-vite-test-setup 0.0.1

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 ADDED
@@ -0,0 +1,1242 @@
1
+ 'use strict';
2
+
3
+ require('vitest-canvas-mock');
4
+ var failOnConsole = require('vitest-fail-on-console');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var vitest = require('vitest');
7
+ require('@testing-library/jest-dom/vitest');
8
+ var react$1 = require('@testing-library/react');
9
+ var util = require('util');
10
+ var react = require('react');
11
+ var reactI18next = require('react-i18next');
12
+ var polyfill = require('@js-temporal/polyfill');
13
+ var web = require('@react-spring/web');
14
+ var webStreamsPolyfill = require('web-streams-polyfill');
15
+
16
+ function _interopNamespaceDefault(e) {
17
+ var n = Object.create(null);
18
+ if (e) {
19
+ Object.keys(e).forEach(function (k) {
20
+ if (k !== 'default') {
21
+ var d = Object.getOwnPropertyDescriptor(e, k);
22
+ Object.defineProperty(n, k, d.get ? d : {
23
+ enumerable: true,
24
+ get: function () { return e[k]; }
25
+ });
26
+ }
27
+ });
28
+ }
29
+ n.default = e;
30
+ return Object.freeze(n);
31
+ }
32
+
33
+ var reactI18next__namespace = /*#__PURE__*/_interopNamespaceDefault(reactI18next);
34
+
35
+ /**
36
+ * Sets up a mock implementation for HTML Canvas API in testing environments.
37
+ *
38
+ * This function uses vitest-canvas-mock to provide a mock implementation of the
39
+ * HTML Canvas API, allowing tests to run without requiring a real DOM canvas.
40
+ * Useful for testing components that use canvas rendering.
41
+ *
42
+ * Importing vitest-canvas-mock at module load time has the side effect of
43
+ * patching HTMLCanvasElement.prototype.getContext globally; calling this
44
+ * function is a no-op kept for API compatibility with the previous Jest
45
+ * setup.
46
+ *
47
+ * @example
48
+ * import { setupCanvasMock } from '@trackunit/react-vite-test-setup';
49
+ *
50
+ * setupCanvasMock();
51
+ */
52
+ const setupCanvasMock = () => {
53
+ // The canvas mock is registered via the side-effect import at module
54
+ // load time; nothing further is needed here.
55
+ };
56
+
57
+ /**
58
+ * This will make your tests fail if they log to console.error during the tests.
59
+ * See more details here: https://www.npmjs.com/package/vitest-fail-on-console
60
+ *
61
+ * This setup also automatically suppresses jsdom CSS parsing errors for modern CSS features
62
+ * that jsdom doesn't support (@container queries and :has() selector). Other CSS parsing
63
+ * errors will still cause tests to fail.
64
+ *
65
+ * If your tests logs to console.error on purpose, you should spy on the console like so:
66
+ * ```
67
+ * vi.spyOn(console, 'error').mockImplementation()
68
+ * // Do your logic ...
69
+ * expect(console.error).toHaveBeenCalledWith('your error message')
70
+ * ```
71
+ */
72
+ const setupFailOnConsole = (overrides = {}) => {
73
+ failOnConsole({
74
+ shouldFailOnError: true,
75
+ shouldFailOnWarn: false,
76
+ silenceMessage: (message) => {
77
+ // Suppress ONLY jsdom CSS parsing errors for specific modern CSS features
78
+ // that we know jsdom doesn't support (@container queries, :has() selector)
79
+ if (typeof message === "string" &&
80
+ message.includes("Could not parse CSS stylesheet") &&
81
+ (message.includes("@container") || message.includes(":has("))) {
82
+ return true;
83
+ }
84
+ return false;
85
+ },
86
+ ...overrides,
87
+ });
88
+ };
89
+
90
+ /**
91
+ * `@googlemaps/jest-mocks` still calls `jest.fn()` internally; aliasing the
92
+ * vitest namespace under the legacy `jest` global keeps it working without
93
+ * pulling in the real Jest runtime.
94
+ */
95
+ const ensureJestGlobalAlias = () => {
96
+ const globalAny = globalThis;
97
+ if (globalAny.jest === undefined) {
98
+ globalAny.jest = vitest.vi;
99
+ }
100
+ };
101
+ /**
102
+ * Sets up mocks for Google Maps API and @vis.gl/react-google-maps components
103
+ * in testing environments.
104
+ *
105
+ * Replaces the `@vis.gl/react-google-maps` hooks/components (`APIProvider`,
106
+ * `useMap`, `Map`, `Marker`, `AdvancedMarker`, `InfoWindow`,
107
+ * `useApiIsLoaded`, `useApiLoadingStatus`) with deterministic fakes and
108
+ * registers a `beforeEach` hook that re-populates `global.window.google.maps`
109
+ * with mocked `Geocoder` / `geometry` / `AutocompleteService` implementations
110
+ * via `@googlemaps/jest-mocks`.
111
+ *
112
+ * Opt-in: the mock is registered via `vi.doMock` so it is
113
+ * NOT hoisted and only takes effect once `setupGoogleMaps()` is called from
114
+ * a test setup file. Tests that need the real `@vis.gl/react-google-maps`
115
+ * (or their own mock) are unaffected unless they explicitly call this.
116
+ *
117
+ * The factory body uses `require()` rather than `import` for
118
+ * `@vis.gl/react-google-maps` and `@googlemaps/jest-mocks` so neither
119
+ * package is evaluated until the factory actually runs (the latter still
120
+ * uses Jest globals internally, so a static import would crash any
121
+ * consumer that doesn't actually use the Google Maps mock).
122
+ *
123
+ * @example
124
+ * import { setupGoogleMaps } from '@trackunit/react-vite-test-setup';
125
+ * setupGoogleMaps();
126
+ */
127
+ const setupGoogleMaps = () => {
128
+ ensureJestGlobalAlias();
129
+ const getPlacePredictionsMock = vitest.vi.fn();
130
+ vitest.vi.doMock("@vis.gl/react-google-maps", async () => {
131
+ const originalModule = await vitest.vi.importActual("@vis.gl/react-google-maps");
132
+ ensureJestGlobalAlias();
133
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
134
+ const { APILoadingStatus } = require("@vis.gl/react-google-maps");
135
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
136
+ const { initialize } = require("@googlemaps/jest-mocks");
137
+ // The Google Maps API needs to be initialized before constructing a map
138
+ // instance so the mocked constructors / classes exist on `global.google`.
139
+ initialize();
140
+ const mapOptions = { center: { lat: 0, lng: 0 }, zoom: 10 };
141
+ const mapContainer = document.createElement("div");
142
+ const map = new google.maps.Map(mapContainer, mapOptions);
143
+ vitest.vi.spyOn(map, "getDiv").mockReturnValue(mapContainer);
144
+ const AdvancedMarkerMock = vitest.vi.fn(function (props) {
145
+ return (jsxRuntime.jsx("div", { "data-position": JSON.stringify(props.position), "data-testid": "marker", children: props.children }));
146
+ });
147
+ const MapMock = vitest.vi.fn(function (props) {
148
+ return jsxRuntime.jsx("div", { "data-testid": "map", children: props.children });
149
+ });
150
+ const MapMarkerMock = vitest.vi.fn(function (props) {
151
+ return jsxRuntime.jsx("div", { "data-testid": "map", children: props.children });
152
+ });
153
+ const InfoWindowMock = vitest.vi.fn(function (props) {
154
+ return jsxRuntime.jsx("div", { "data-testid": "google-info-window", children: props.children });
155
+ });
156
+ const APIProviderMock = vitest.vi.fn(function (props) {
157
+ return jsxRuntime.jsx(jsxRuntime.Fragment, { children: props.children });
158
+ });
159
+ return {
160
+ ...originalModule,
161
+ APIProvider: APIProviderMock,
162
+ useApiLoadingStatus: () => APILoadingStatus.LOADED,
163
+ useApiIsLoaded: () => true,
164
+ useMap: () => map,
165
+ AdvancedMarker: AdvancedMarkerMock,
166
+ Map: MapMock,
167
+ Marker: MapMarkerMock,
168
+ InfoWindow: InfoWindowMock,
169
+ };
170
+ });
171
+ vitest.beforeEach(() => {
172
+ ensureJestGlobalAlias();
173
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
174
+ const { initialize } = require("@googlemaps/jest-mocks");
175
+ initialize();
176
+ global.window.google.maps = {
177
+ ...global.window.google.maps,
178
+ Geocoder: vitest.vi.fn(),
179
+ geometry: {
180
+ ...global.window.google.maps.geometry,
181
+ spherical: {
182
+ computeDistanceBetween: vitest.vi.fn(),
183
+ computeArea: vitest.vi.fn(),
184
+ computeHeading: vitest.vi.fn(),
185
+ computeLength: vitest.vi.fn(),
186
+ computeOffset: vitest.vi.fn(),
187
+ computeOffsetOrigin: vitest.vi.fn(),
188
+ computeSignedArea: vitest.vi.fn(),
189
+ interpolate: vitest.vi.fn(),
190
+ },
191
+ },
192
+ places: {
193
+ ...global.window.google.maps.places,
194
+ // `new google.maps.places.AutocompleteService()` invokes this mock
195
+ // with `[[Construct]]`. Vitest 4's spy enforces that the impl is
196
+ // a real function (not an arrow), so we use a `function` expression
197
+ // to expose the construct slot. Cast to never to satisfy the
198
+ // strict mocked-class type that vi.fn() infers for arrow impls.
199
+ AutocompleteService: vitest.vi.fn().mockImplementation(function () {
200
+ return {
201
+ ...global.window.google.maps.places,
202
+ getPlacePredictions: getPlacePredictionsMock,
203
+ };
204
+ }),
205
+ },
206
+ };
207
+ });
208
+ };
209
+
210
+ /**
211
+ * Replaces `react-helmet-async`'s `Helmet` and `HelmetProvider` with no-op
212
+ * components so head-mutating components rendered under test do not touch
213
+ * the jsdom document.
214
+ *
215
+ * Opt-in: the mock is registered via `vi.doMock` so it is
216
+ * NOT hoisted and only takes effect once `setupHelmetMock()` is called from
217
+ * a test setup file. Tests that need the real `react-helmet-async` (or
218
+ * their own mock) are unaffected unless they explicitly call this.
219
+ *
220
+ * See more details here: https://www.npmjs.com/package/react-helmet-async
221
+ *
222
+ * @example
223
+ * import { setupHelmetMock } from '@trackunit/react-vite-test-setup';
224
+ * setupHelmetMock();
225
+ */
226
+ const setupHelmetMock = () => {
227
+ vitest.vi.doMock("react-helmet-async", () => ({
228
+ Helmet: () => null,
229
+ HelmetProvider: () => null,
230
+ }));
231
+ };
232
+
233
+ /**
234
+ * Mocks the IntersectionObserver API for testing environments.
235
+ *
236
+ * This function adds a mock implementation of the IntersectionObserver API to the global window object.
237
+ * The mock implementation provides all the necessary methods (observe, unobserve, disconnect, takeRecords)
238
+ * but with empty implementations, allowing tests of components that use IntersectionObserver to run
239
+ * without errors in Jest's JSDOM environment.
240
+ *
241
+ * Useful for testing components that rely on:
242
+ * - Lazy loading
243
+ * - Infinite scrolling
244
+ * - Visibility-based rendering
245
+ * - Any other intersection-based functionality
246
+ *
247
+ * @example
248
+ * // In your jest setup file
249
+ * import { setupIntersectionObserver } from '@trackunit/react-vite-test-setup';
250
+ *
251
+ * setupIntersectionObserver();
252
+ */
253
+ const setupIntersectionObserver = () => (window.IntersectionObserver = MockIntersectionObserver);
254
+ class MockIntersectionObserver {
255
+ constructor() {
256
+ this.root = null;
257
+ this.rootMargin = "";
258
+ this.scrollMargin = "";
259
+ this.thresholds = [];
260
+ }
261
+ disconnect() {
262
+ // Empty
263
+ }
264
+ observe() {
265
+ // Empty
266
+ }
267
+ takeRecords() {
268
+ return [];
269
+ }
270
+ unobserve() {
271
+ // Empty
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Creates a new listener registry for tracking event subscriptions.
277
+ */
278
+ const createListenerRegistry = () => ({
279
+ regular: new Map(),
280
+ once: new Map(),
281
+ });
282
+ /**
283
+ * Mock LngLat class matching Mapbox GL's interface.
284
+ * Returns actual objects with lng/lat properties rather than class instances.
285
+ */
286
+ const createMockLngLat = (lng, lat) => ({
287
+ lng,
288
+ lat,
289
+ wrap: () => createMockLngLat(lng, lat),
290
+ toArray: () => [lng, lat],
291
+ distanceTo: () => 0,
292
+ toBounds: () => createMockLngLatBounds(lng - 1, lat - 1, lng + 1, lat + 1),
293
+ toString: () => `LngLat(${lng}, ${lat})`,
294
+ toEcef: () => [0, 0, 0],
295
+ });
296
+ /**
297
+ * Mock LngLatBounds matching Mapbox GL's interface.
298
+ */
299
+ const createMockLngLatBounds = (west, south, east, north) => {
300
+ const sw = createMockLngLat(west, south);
301
+ const ne = createMockLngLat(east, north);
302
+ const bounds = {
303
+ _sw: sw,
304
+ _ne: ne,
305
+ getSouthWest: () => sw,
306
+ getNorthEast: () => ne,
307
+ getNorthWest: () => createMockLngLat(west, north),
308
+ getSouthEast: () => createMockLngLat(east, south),
309
+ getWest: () => west,
310
+ getSouth: () => south,
311
+ getEast: () => east,
312
+ getNorth: () => north,
313
+ getCenter: () => createMockLngLat((west + east) / 2, (south + north) / 2),
314
+ toArray: () => [
315
+ [west, south],
316
+ [east, north],
317
+ ],
318
+ toString: () => `LngLatBounds(${sw.toString()}, ${ne.toString()})`,
319
+ isEmpty: () => false,
320
+ contains: () => true,
321
+ extend: () => bounds,
322
+ setNorthEast: () => bounds,
323
+ setSouthWest: () => bounds,
324
+ };
325
+ return bounds;
326
+ };
327
+ /**
328
+ * Creates a mock Mapbox map instance for unit testing adapter instances directly.
329
+ *
330
+ * Use this when you need fine-grained control over mock behavior, such as:
331
+ * - Testing adapter instance methods (connect, setCenter, etc.)
332
+ * - Simulating map events (idle, click, movestart)
333
+ * - Verifying method calls on the map
334
+ *
335
+ * @example
336
+ * ```typescript
337
+ * import { createMockMapboxMap } from "@trackunit/react-vite-test-setup";
338
+ *
339
+ * it("should connect to map", () => {
340
+ * const { map, triggerEvent } = createMockMapboxMap();
341
+ * const adapter = new MapboxAdapterInstance(config);
342
+ *
343
+ * adapter.connect(map as unknown as mapboxgl.Map);
344
+ * triggerEvent("idle");
345
+ *
346
+ * expect(adapter.getState().isReady).toBe(true);
347
+ * });
348
+ * ```
349
+ */
350
+ const createMockMapboxMap = () => {
351
+ const registry = createListenerRegistry();
352
+ const addListener = (store, event, handler) => {
353
+ const handlers = store.get(event);
354
+ if (handlers === undefined) {
355
+ store.set(event, new Set([handler]));
356
+ }
357
+ else {
358
+ handlers.add(handler);
359
+ }
360
+ return map;
361
+ };
362
+ const map = {
363
+ on: vitest.vi.fn((event, handler) => addListener(registry.regular, event, handler)),
364
+ once: vitest.vi.fn((event, handler) => addListener(registry.once, event, handler)),
365
+ off: vitest.vi.fn((event, handler) => {
366
+ registry.regular.get(event)?.delete(handler);
367
+ return map;
368
+ }),
369
+ remove: vitest.vi.fn(),
370
+ getCenter: vitest.vi.fn(() => createMockLngLat(0, 0)),
371
+ getZoom: vitest.vi.fn(() => 10),
372
+ getBounds: vitest.vi.fn(() => createMockLngLatBounds(-1, -1, 1, 1)),
373
+ setCenter: vitest.vi.fn((_center) => map),
374
+ setZoom: vitest.vi.fn((_zoom) => map),
375
+ panTo: vitest.vi.fn((_lngLat) => map),
376
+ panBy: vitest.vi.fn((_offset) => map),
377
+ fitBounds: vitest.vi.fn((_bounds, _options) => map),
378
+ setStyle: vitest.vi.fn((_style) => map),
379
+ isStyleLoaded: vitest.vi.fn(() => true),
380
+ isMoving: vitest.vi.fn(() => false),
381
+ addSource: vitest.vi.fn(),
382
+ removeSource: vitest.vi.fn(),
383
+ getSource: vitest.vi.fn(() => undefined),
384
+ addLayer: vitest.vi.fn(),
385
+ removeLayer: vitest.vi.fn(),
386
+ getLayer: vitest.vi.fn(() => undefined),
387
+ setPaintProperty: vitest.vi.fn(),
388
+ hasImage: vitest.vi.fn(() => false),
389
+ addImage: vitest.vi.fn(),
390
+ getCanvas: vitest.vi.fn(() => ({ style: {} })),
391
+ getCanvasContainer: vitest.vi.fn(() => document.createElement("div")),
392
+ };
393
+ const triggerEvent = (eventName, event) => {
394
+ // Fire regular listeners
395
+ const regularHandlers = registry.regular.get(eventName);
396
+ if (regularHandlers !== undefined) {
397
+ regularHandlers.forEach((handler) => handler(event));
398
+ }
399
+ // Fire and clear once listeners
400
+ const onceHandlers = registry.once.get(eventName);
401
+ if (onceHandlers !== undefined) {
402
+ onceHandlers.forEach((handler) => handler(event));
403
+ registry.once.delete(eventName);
404
+ }
405
+ };
406
+ return { map, triggerEvent };
407
+ };
408
+ /**
409
+ * Mock click event matching Mapbox GL's MapMouseEvent shape.
410
+ * Use with triggerEvent("click", createMockClickEvent(...))
411
+ *
412
+ * Note: The `point` property is intentionally omitted because:
413
+ * 1. It requires the full Point class from @mapbox/point-geometry with 30+ methods
414
+ * 2. No tests currently use the point property
415
+ * 3. Since this returns Partial<MapMouseEvent>, point is optional
416
+ */
417
+ const createMockClickEvent = (lng, lat) => ({
418
+ lngLat: createMockLngLat(lng, lat),
419
+ originalEvent: new MouseEvent("click"),
420
+ type: "click",
421
+ });
422
+ /**
423
+ * Sets up mocks for Mapbox GL JS in testing environments.
424
+ *
425
+ * Replaces `mapbox-gl`'s `Map` and `Marker` constructors with deterministic
426
+ * fakes (the `Map` mock auto-fires the `load` event so renderer tests don't
427
+ * have to wait for it) and registers a `beforeEach` hook that clears mock
428
+ * call records between tests.
429
+ *
430
+ * Opt-in: the mock is registered via `vi.doMock` so it is
431
+ * NOT hoisted and only takes effect once `setupMapbox()` is called from a
432
+ * test setup file. Tests that need the real `mapbox-gl` (or their own mock)
433
+ * are unaffected unless they explicitly call this.
434
+ *
435
+ * @example
436
+ * ```typescript
437
+ * // In your test file
438
+ * import { setupMapbox } from "@trackunit/react-vite-test-setup";
439
+ *
440
+ * setupMapbox();
441
+ *
442
+ * describe("MapboxRenderer", () => {
443
+ * it("renders map when loaded", async () => {
444
+ * // The mock will auto-fire "load" event
445
+ * render(<MapboxRenderer adapterInstance={instance} />);
446
+ * await waitFor(() => expect(screen.getByRole("application")).toBeInTheDocument());
447
+ * });
448
+ * });
449
+ * ```
450
+ */
451
+ const setupMapbox = () => {
452
+ vitest.vi.doMock("mapbox-gl", () => {
453
+ // SUT does `new mapboxgl.Map(...)` and `new mapboxgl.Marker(...)`. Vitest
454
+ // 4's spy invokes the implementation with `new` when the mock itself is
455
+ // called with `new`, and arrow functions are not constructable. Use
456
+ // `function` expressions so the implementation has a working
457
+ // `[[Construct]]` slot.
458
+ const createMockMapWithAutoLoad = function () {
459
+ const result = createMockMapboxMap();
460
+ const originalOn = result.map.on;
461
+ result.map.on = vitest.vi.fn((event, handler) => {
462
+ originalOn(event, handler);
463
+ if (event === "load") {
464
+ setTimeout(() => handler(undefined), 0);
465
+ }
466
+ return result.map;
467
+ });
468
+ return result.map;
469
+ };
470
+ const createMockMarker = function (options) {
471
+ const el = options?.element ?? document.createElement("div");
472
+ const marker = {
473
+ setLngLat: vitest.vi.fn(() => marker),
474
+ addTo: vitest.vi.fn(() => marker),
475
+ remove: vitest.vi.fn(),
476
+ getElement: vitest.vi.fn(() => el),
477
+ setOffset: vitest.vi.fn(() => marker),
478
+ getLngLat: vitest.vi.fn(() => createMockLngLat(0, 0)),
479
+ setPopup: vitest.vi.fn(() => marker),
480
+ getPopup: vitest.vi.fn(() => null),
481
+ togglePopup: vitest.vi.fn(() => marker),
482
+ setDraggable: vitest.vi.fn(() => marker),
483
+ isDraggable: vitest.vi.fn(() => false),
484
+ };
485
+ return marker;
486
+ };
487
+ return {
488
+ __esModule: true,
489
+ default: {
490
+ Map: vitest.vi.fn(createMockMapWithAutoLoad),
491
+ Marker: vitest.vi.fn(createMockMarker),
492
+ accessToken: "",
493
+ },
494
+ };
495
+ });
496
+ vitest.beforeEach(() => {
497
+ vitest.vi.clearAllMocks();
498
+ });
499
+ };
500
+
501
+ /**
502
+ * Mocks the window.matchMedia API for testing environments.
503
+ *
504
+ * This function creates a mock implementation of the window.matchMedia method that
505
+ * is commonly used for responsive design and media queries. The mock always returns
506
+ * a MediaQueryList-like object with standard methods and properties, but with default
507
+ * values (matches set to false).
508
+ *
509
+ * This allows tests of components that use media queries to run without errors in
510
+ * Jest's JSDOM environment, which doesn't implement matchMedia natively.
511
+ *
512
+ * Ideal for testing:
513
+ * - Responsive components
514
+ * - Components that adapt to screen size changes
515
+ * - Components that use CSS media query matching in JavaScript
516
+ *
517
+ * @example
518
+ * // In your jest setup file
519
+ * import { setupMatchMediaMock } from '@trackunit/react-vite-test-setup';
520
+ *
521
+ * setupMatchMediaMock();
522
+ */
523
+ const setupMatchMediaMock = () => {
524
+ Object.defineProperty(window, "matchMedia", {
525
+ writable: true,
526
+ value: vitest.vi.fn().mockImplementation(query => ({
527
+ matches: false,
528
+ media: query,
529
+ onchange: null,
530
+ addListener: vitest.vi.fn(), // Deprecated
531
+ removeListener: vitest.vi.fn(), // Deprecated
532
+ addEventListener: vitest.vi.fn(),
533
+ removeEventListener: vitest.vi.fn(),
534
+ dispatchEvent: vitest.vi.fn(),
535
+ })),
536
+ });
537
+ };
538
+
539
+ /**
540
+ * Default factory for `new ResizeObserver(...)` invocations. Returns a
541
+ * fresh object per call so tests can spy on / replace per-instance
542
+ * methods without affecting later constructions.
543
+ */
544
+ const buildResizeObserverInstance = () => ({
545
+ observe: vitest.vi.fn(),
546
+ unobserve: vitest.vi.fn(),
547
+ disconnect: vitest.vi.fn(),
548
+ });
549
+ /**
550
+ * Mock the ResizeObserver to be able to test components that use a resize
551
+ * observer (e.g. useMeasure, useContainerBreakpoints, and components that
552
+ * use them like BaseInput). Recommended for all React libs.
553
+ *
554
+ * Implementation note: under Vitest the mock was a single
555
+ * `vi.fn().mockImplementation(...)` so callers could:
556
+ * 1. `new global.ResizeObserver(cb)` from the SUT (the implementation
557
+ * function is callable with `new` because it's a `function` expression
558
+ * which has a working `[[Construct]]` slot), AND
559
+ * 2. `(global.ResizeObserver as Mock).mockImplementation(...)` from the
560
+ * spec to capture the callback / inspect constructor arguments.
561
+ *
562
+ * Vitest 4's `vi.fn()` is also constructable when the impl is a `function`
563
+ * expression (arrow functions are *not* constructable), so we keep the
564
+ * same shape - this preserves both call sites that the migration would
565
+ * otherwise have to rewrite.
566
+ */
567
+ const setupResizeObserver = () => {
568
+ global.ResizeObserver = vitest.vi.fn().mockImplementation(function (_callback) {
569
+ return buildResizeObserverInstance();
570
+ });
571
+ };
572
+
573
+ if (typeof global !== "undefined") {
574
+ global.TextEncoder = global.TextEncoder || util.TextEncoder;
575
+ global.TextDecoder = global.TextDecoder || util.TextDecoder;
576
+ }
577
+ /**
578
+ * Sets up internationalization and translation mocks for testing environments.
579
+ *
580
+ * Mock translations for react-i18next and @trackunit/i18n-library-translation. It creates
581
+ * simple mock implementations that return the key string as the translation, which allows
582
+ * for testing internationalized components without the complexity of actual translations.
583
+ *
584
+ * The mocks support common translation components and hooks like Trans, useTranslation,
585
+ * NamespaceTrans, and useNamespaceTranslation.
586
+ *
587
+ * @example
588
+ * // In your jest setup file
589
+ * import { setupTranslations } from '@trackunit/react-vite-test-setup';
590
+ *
591
+ * setupTranslations();
592
+ */
593
+ const setupTranslations = () => {
594
+ const hasChildren = (node) => node && (node.children || (node.props && node.props.children));
595
+ const getChildren = (node) => (node && node.children ? node.children : node.props && node.props.children);
596
+ const renderNodes = (reactNodes) => {
597
+ if (typeof reactNodes === "string") {
598
+ return reactNodes;
599
+ }
600
+ return Object.keys(reactNodes).map((key, i) => {
601
+ const child = reactNodes[key];
602
+ const isElement = react.isValidElement(child);
603
+ if (typeof child === "string") {
604
+ return child;
605
+ }
606
+ if (hasChildren(child)) {
607
+ const inner = renderNodes(getChildren(child));
608
+ return react.cloneElement(child, { ...child.props, key: i }, inner);
609
+ }
610
+ if (typeof child === "object" && !isElement) {
611
+ return Object.keys(child).reduce((str, childKey) => `${str}${child[childKey]}`, "");
612
+ }
613
+ return child;
614
+ });
615
+ };
616
+ const i18n = { language: "en", exists: () => true };
617
+ const useMock = [
618
+ (k, extra) => k + (extra ? " props: " + JSON.stringify(extra) : ""),
619
+ i18n,
620
+ ];
621
+ useMock.t = (k, extra) => k + (extra ? " props: " + JSON.stringify(extra) : "");
622
+ useMock.i18n = i18n;
623
+ const useTranslation = () => useMock;
624
+ const Trans = ({ i18nKey, components, children }) => {
625
+ const result = [];
626
+ result.push(renderNodes([i18nKey]));
627
+ if (Array.isArray(children)) {
628
+ result.push(...renderNodes(children));
629
+ }
630
+ else if (children && "toArray" in children) {
631
+ result.push(...renderNodes(children.toArray()));
632
+ }
633
+ else {
634
+ result.push(...renderNodes([children]));
635
+ }
636
+ if (components) {
637
+ result.push(...Object.keys(components).map(key => jsxRuntime.jsx("span", { children: components[key] }, key)));
638
+ }
639
+ return jsxRuntime.jsx(jsxRuntime.Fragment, { children: result });
640
+ };
641
+ const Translation = ({ children }) => children((k) => k, { i18n });
642
+ vitest.vi.doMock("react-i18next", () => {
643
+ return {
644
+ // this mock makes sure any components using the translate HoC receive the t function as a prop
645
+ Trans,
646
+ Translation,
647
+ useTranslation,
648
+ // mock if needed
649
+ I18nextProvider: reactI18next__namespace.I18nextProvider,
650
+ initReactI18next: reactI18next__namespace.initReactI18next,
651
+ setDefaults: reactI18next__namespace.setDefaults,
652
+ getDefaults: reactI18next__namespace.getDefaults,
653
+ setI18n: reactI18next__namespace.setI18n,
654
+ getI18n: reactI18next__namespace.getI18n,
655
+ };
656
+ });
657
+ vitest.vi.doMock("@trackunit/i18n-library-translation", async () => ({
658
+ ...(await vitest.vi.importActual("@trackunit/i18n-library-translation")),
659
+ NamespaceTrans: (props) => jsxRuntime.jsx(Trans, { ...props }),
660
+ useNamespaceTranslation: useTranslation,
661
+ }));
662
+ };
663
+
664
+ /**
665
+ * Flushes all promises in the queue.
666
+ * This is useful when testing async code.
667
+ *
668
+ * @param waitTimeInMS - The amount of time to wait before resolving the promise.
669
+ * @returns {Promise<void>} - Returns a promise that resolves after the wait time.
670
+ */
671
+ const flushPromisesInAct = (waitTimeInMS = 0) => {
672
+ return react$1.act(() => {
673
+ return new Promise(resolve => {
674
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
675
+ if (global.ORG_setTimeout) {
676
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
677
+ return global.ORG_setTimeout(() => global.ORG_setTimeout(resolve, waitTimeInMS), 1);
678
+ }
679
+ else {
680
+ setTimeout(() => setTimeout(resolve, waitTimeInMS), 1);
681
+ }
682
+ });
683
+ });
684
+ };
685
+ vitest.afterEach(async () => {
686
+ // Restore real timers BEFORE cleanup / flushPromisesInAct. Both rely on
687
+ // React's internal scheduler resolving microtasks; under Vitest 4 fake
688
+ // timers that scheduler waits on the fake clock, so an act-wrapped promise
689
+ // chained through `setTimeout` will never resolve until the clock is
690
+ // advanced. Putting `vi.useRealTimers()` first guarantees cleanup is not
691
+ // gated on whatever fake-timer state the test left installed.
692
+ // `vi.useRealTimers()` is a no-op when no fake timers are installed.
693
+ vitest.vi.useRealTimers();
694
+ vitest.vi.clearAllMocks();
695
+ react$1.cleanup();
696
+ await flushPromisesInAct();
697
+ });
698
+ const setupResponseForTanstackRouter = () => {
699
+ // Polyfill for Response global that TanStack Router v1+ requires
700
+ if (typeof globalThis.Response === "undefined") {
701
+ // Simple polyfill for Response in test environment
702
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
703
+ globalThis.Response = class Response {
704
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
705
+ constructor(body, init) {
706
+ this.status = init?.status ?? 200;
707
+ this.statusText = init?.statusText ?? "";
708
+ this.headers = new Headers(init?.headers);
709
+ this.body = body;
710
+ this.ok = this.status >= 200 && this.status < 300;
711
+ this.redirected = false;
712
+ this.type = "default";
713
+ this.url = "";
714
+ }
715
+ static redirect(url, status = 302) {
716
+ return new Response(null, { status, headers: { Location: url } });
717
+ }
718
+ static error() {
719
+ return new Response(null, { status: 0 });
720
+ }
721
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
722
+ static json(data, init) {
723
+ return new Response(JSON.stringify(data), {
724
+ ...init,
725
+ headers: {
726
+ "Content-Type": "application/json",
727
+ ...init?.headers,
728
+ },
729
+ });
730
+ }
731
+ clone() {
732
+ return new Response(this.body, {
733
+ status: this.status,
734
+ statusText: this.statusText,
735
+ headers: this.headers,
736
+ });
737
+ }
738
+ };
739
+ }
740
+ if (typeof globalThis.Headers === "undefined") {
741
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
742
+ globalThis.Headers = class Headers {
743
+ constructor(init) {
744
+ this.map = new Map();
745
+ if (init) {
746
+ if (Array.isArray(init)) {
747
+ init.forEach(([key, value]) => this.set(key, value));
748
+ }
749
+ else if (init instanceof Headers) {
750
+ init.forEach((value, key) => this.set(key, value));
751
+ }
752
+ else {
753
+ Object.entries(init).forEach(([key, value]) => this.set(key, value));
754
+ }
755
+ }
756
+ }
757
+ append(name, value) {
758
+ this.map.set(name.toLowerCase(), value);
759
+ }
760
+ delete(name) {
761
+ this.map.delete(name.toLowerCase());
762
+ }
763
+ get(name) {
764
+ return this.map.get(name.toLowerCase()) || null;
765
+ }
766
+ has(name) {
767
+ return this.map.has(name.toLowerCase());
768
+ }
769
+ set(name, value) {
770
+ this.map.set(name.toLowerCase(), value);
771
+ }
772
+ forEach(callback) {
773
+ this.map.forEach((value, key) => callback(value, key, this));
774
+ }
775
+ entries() {
776
+ return this.map.entries();
777
+ }
778
+ keys() {
779
+ return this.map.keys();
780
+ }
781
+ values() {
782
+ return this.map.values();
783
+ }
784
+ [Symbol.iterator]() {
785
+ return this.map.entries();
786
+ }
787
+ };
788
+ }
789
+ };
790
+ /**
791
+ * Sets up React Testing Library and Okta authentication mocks for testing.
792
+ *
793
+ * Behaviour:
794
+ * - The top-level `afterEach` further up this file (which clears mocks
795
+ * and runs RTL `cleanup()`) is registered as a side effect of importing
796
+ * this module - that's intentional and existed before this refactor.
797
+ * - Calling `setupReactTestingLibrary()` additionally:
798
+ * 1. Registers the `@okta/okta-react` mock via `vi.doMock` so
799
+ * `useOktaAuth()` returns a fixed authenticated session. Opt-in:
800
+ * the mock is NOT hoisted, so tests that need the real okta
801
+ * hook (or their own mock) are unaffected unless they explicitly
802
+ * call this.
803
+ * 2. Installs the tanstack-router Response polyfill (keeps it
804
+ * opt-in because it mutates `globalThis.Response`);
805
+ * 3. Calls `setupTranslations()` so the
806
+ * `useTranslation`/`useNamespaceTranslation` hooks always
807
+ * return a mocked i18next instance. Under Vitest, the
808
+ * shared jest-preset's `setupFilesAfterEnv` is no longer
809
+ * loaded, so projects that previously got translation
810
+ * setup "for free" otherwise blow up at the first
811
+ * `i18NextInstance.exists is not a function` call. Baking
812
+ * it in here means projects can't forget it - the cost is
813
+ * mocking i18n in tests that don't use it (harmless: the
814
+ * mock only kicks in if the spec imports react-i18next).
815
+ * 4. Installs `setupMatchMediaMock()`, `setupResizeObserver()`
816
+ * and `setupIntersectionObserver()`. Every component test
817
+ * needs these three under jsdom, and missing any of them
818
+ * causes React's render to throw, which `vitest-fail-on-console`
819
+ * then re-raises as "Expected test not to call console.error()".
820
+ * Under jest the workspace preset installed them via
821
+ * `setupFilesAfterEnv`; under vitest only what each project's
822
+ * setup file explicitly imports runs, so most setup files
823
+ * called `setupReactTestingLibrary()` plus `setupFailOnConsole()`
824
+ * but forgot the basic-DOM triplet. Folding the triplet in
825
+ * here means "set up RTL" reads naturally as "set up
826
+ * everything you need to render a component".
827
+ *
828
+ * @example
829
+ * import { setupReactTestingLibrary } from '@trackunit/react-vite-test-setup';
830
+ * setupReactTestingLibrary();
831
+ */
832
+ const setupReactTestingLibrary = () => {
833
+ const mockedClaims = {
834
+ sub: "sub",
835
+ name: "user",
836
+ };
837
+ const mockedIdToken = {
838
+ idToken: "idToken",
839
+ claims: mockedClaims,
840
+ expiresAt: Date.now(),
841
+ authorizeUrl: "authorize.url",
842
+ scopes: [],
843
+ issuer: "issuer",
844
+ clientId: "clientId",
845
+ };
846
+ const mockedAccessToken = {
847
+ accessToken: "accessToken",
848
+ claims: mockedClaims,
849
+ tokenType: "tokenType",
850
+ userinfoUrl: "userinfo.url",
851
+ expiresAt: Date.now(),
852
+ authorizeUrl: "authorize.url",
853
+ scopes: [],
854
+ };
855
+ const mockedAuthState = {
856
+ accessToken: mockedAccessToken,
857
+ idToken: mockedIdToken,
858
+ isAuthenticated: true,
859
+ };
860
+ const mockedOktaAuth = () => {
861
+ return {
862
+ oktaAuth: {
863
+ tokenManager: {
864
+ on: vitest.vi.fn(),
865
+ renew: vitest.vi.fn(),
866
+ setTokens: vitest.vi.fn(),
867
+ clear: vitest.vi.fn(),
868
+ },
869
+ getOriginalUri: vitest.vi.fn(),
870
+ signOut: vitest.vi.fn().mockResolvedValue({ postLogoutRedirectUri: "mocked-url", clearTokensBeforeRedirect: true }),
871
+ token: {
872
+ getWithRedirect: vitest.vi.fn(),
873
+ getWithoutPrompt: vitest.vi.fn(),
874
+ },
875
+ session: {
876
+ get: () => {
877
+ return { status: "ACTIVE" };
878
+ },
879
+ },
880
+ authStateManager: {
881
+ getAuthState: vitest.vi.fn(),
882
+ subscribe: vitest.vi.fn(),
883
+ unsubscribe: vitest.vi.fn(),
884
+ },
885
+ closeSession: vitest.vi.fn(),
886
+ options: {
887
+ restoreOriginalUri: "",
888
+ },
889
+ start: vitest.vi.fn(),
890
+ setOriginalUri: vitest.vi.fn(),
891
+ },
892
+ authState: mockedAuthState,
893
+ };
894
+ };
895
+ vitest.vi.doMock("@okta/okta-react", () => ({
896
+ useOktaAuth: () => mockedOktaAuth(),
897
+ }));
898
+ setupResponseForTanstackRouter();
899
+ setupTranslations();
900
+ setupMatchMediaMock();
901
+ setupResizeObserver();
902
+ setupIntersectionObserver();
903
+ };
904
+
905
+ /**
906
+ * Mocks the `react-virtualized-auto-sizer` component for testing
907
+ * environments.
908
+ *
909
+ * Provides fixed dimensions (600x600) so tests don't need real DOM
910
+ * measurements. Especially useful for components using virtualized lists or
911
+ * grids.
912
+ *
913
+ * Opt-in: the mock is registered via `vi.doMock` so it is
914
+ * NOT hoisted and only takes effect once `setupReactVirtualizedAutoSizer()`
915
+ * is called from a test setup file. Tests that need the real component (or
916
+ * their own mock) are unaffected unless they explicitly call this.
917
+ *
918
+ * @example
919
+ * import { setupReactVirtualizedAutoSizer } from '@trackunit/react-vite-test-setup';
920
+ * setupReactVirtualizedAutoSizer();
921
+ */
922
+ const setupReactVirtualizedAutoSizer = () => {
923
+ vitest.vi.doMock("react-virtualized-auto-sizer", () => ({ children }) => children({ height: 600, width: 600, scaledWidth: 600, scaledHeight: 600 }));
924
+ };
925
+
926
+ /**
927
+ * Mocks the `@tanstack/react-virtual` library for testing environments.
928
+ *
929
+ * Replaces `useVirtualizer` with a deterministic implementation that
930
+ * returns every item (no real DOM virtualisation), so tests can assert on
931
+ * the full rendered list without worrying about scroll-driven windowing.
932
+ *
933
+ * Opt-in: the mock is registered via `vi.doMock` so it is
934
+ * NOT hoisted and only takes effect once `setupTanstackReactVirtual()` is
935
+ * called from a test setup file. Tests that need the real virtualizer (or
936
+ * their own mock) are unaffected unless they explicitly call this.
937
+ *
938
+ * @example
939
+ * import { setupTanstackReactVirtual } from '@trackunit/react-vite-test-setup';
940
+ * setupTanstackReactVirtual();
941
+ */
942
+ const setupTanstackReactVirtual = () => {
943
+ vitest.vi.doMock("@tanstack/react-virtual", () => ({
944
+ useVirtualizer: ({ count }) => ({
945
+ getVirtualItems: () => {
946
+ const result = [];
947
+ for (let i = 0; i < count; i++) {
948
+ result.push({
949
+ index: i,
950
+ start: i * 40,
951
+ key: i,
952
+ measureRef: () => {
953
+ /* noop */
954
+ },
955
+ });
956
+ }
957
+ return result;
958
+ },
959
+ getTotalSize: () => count,
960
+ scrollToIndex: () => {
961
+ /* noop */
962
+ },
963
+ scrollToOffset: () => {
964
+ /* noop */
965
+ },
966
+ scrollToAlign: () => {
967
+ /* noop */
968
+ },
969
+ scrollToItem: () => {
970
+ /* noop */
971
+ },
972
+ resetAfterIndex: () => {
973
+ /* noop */
974
+ },
975
+ resetAfterItem: () => {
976
+ /* noop */
977
+ },
978
+ scrollTo: () => {
979
+ /* noop */
980
+ },
981
+ measure: () => {
982
+ /* noop */
983
+ },
984
+ measureElement: () => 42,
985
+ }),
986
+ }));
987
+ };
988
+
989
+ /* eslint-disable @typescript-eslint/no-explicit-any */
990
+ // Pin the test process timezone to UTC at module load (before the imports
991
+ // below evaluate, since ESM hoists `import` statements above this assignment
992
+ // only inside this module's body, and Node reads `process.env.TZ` lazily for
993
+ // each new Date). This mirrors what `vitest.preset.js` did for the Vitest setup
994
+ // and keeps Date#toISOString / time-of-day assertions stable across machines.
995
+ // Without this, dev environments with non-UTC TZ produce off-by-an-hour
996
+ // failures in tests that build dates from numeric components (e.g. the
997
+ // `InsightsHelper` ISO-string assertions).
998
+ process.env.TZ = "UTC";
999
+ /**
1000
+ * Mocks the Temporal API to use a fixed time zone for testing.
1001
+ *
1002
+ * This function specifically mocks the Temporal.Now.timeZoneId method from the
1003
+ * Temporal library to always return "Europe/Copenhagen", ensuring consistent
1004
+ * time zone-dependent behavior in tests regardless of where they run.
1005
+ *
1006
+ * Note: the host process timezone is also pinned to UTC at module load via
1007
+ * `process.env.TZ` above, so non-Temporal Date arithmetic is deterministic.
1008
+ *
1009
+ * @example
1010
+ * // In your vitest setup file
1011
+ * import { setupTimeZone } from '@trackunit/react-vite-test-setup';
1012
+ *
1013
+ * setupTimeZone();
1014
+ */
1015
+ const setupTimeZone = () => {
1016
+ vitest.vi.spyOn(polyfill.Temporal.Now, "timeZoneId").mockImplementation(function () {
1017
+ return "Europe/Copenhagen";
1018
+ });
1019
+ };
1020
+ /**
1021
+ * Sets up time, animation, and language-related mocks for testing environments.
1022
+ *
1023
+ * This function configures multiple aspects of the testing environment:
1024
+ * 1. Sets a fixed time zone using setupTimeZone()
1025
+ * 2. Disables React Spring animations for faster, deterministic tests
1026
+ * 3. Overrides setTimeout and setInterval to execute immediately (0ms delay)
1027
+ *
1028
+ * These changes make tests faster and more predictable by eliminating real-time
1029
+ * delays, animations, and time zone dependencies that could cause flaky tests.
1030
+ *
1031
+ * @example
1032
+ * // In your jest setup file
1033
+ * import { setupTimeAndLanguage } from '@trackunit/react-vite-test-setup';
1034
+ *
1035
+ * setupTimeAndLanguage();
1036
+ */
1037
+ const setupTimeAndLanguage = () => {
1038
+ setupTimeZone();
1039
+ web.Globals.assign({
1040
+ skipAnimation: true,
1041
+ });
1042
+ const globalSetTimeout = global.setTimeout;
1043
+ global.ORG_setTimeout = globalSetTimeout;
1044
+ const globalSetInterval = global.setInterval;
1045
+ global.ORG_setInterval = globalSetInterval;
1046
+ global.setTimeout = function testSetTimeout(callback,
1047
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1048
+ ms,
1049
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1050
+ ...args) {
1051
+ return globalSetTimeout.apply(this, [callback, 0]);
1052
+ };
1053
+ global.setInterval = function testSetInterval(callback,
1054
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1055
+ ms,
1056
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1057
+ ...args) {
1058
+ return globalSetInterval.apply(this, [callback, 0]);
1059
+ };
1060
+ };
1061
+
1062
+ /**
1063
+ * Sets up Web Streams API polyfills for testing environments.
1064
+ *
1065
+ * Web Streams API polyfills from web-streams-polyfill. These polyfills provide implementations
1066
+ * of modern streaming APIs that may not be available in the Jest/JSDOM testing environment.
1067
+ *
1068
+ * The setup enables testing of components and utilities that rely on the Web Streams API,
1069
+ * such as those that process streaming data or implement custom stream transformations.
1070
+ *
1071
+ * @example
1072
+ * // In your jest setup file
1073
+ * import { setupWebStreams } from '@trackunit/react-vite-test-setup';
1074
+ *
1075
+ * setupWebStreams();
1076
+ */
1077
+ const setupWebStreams = () => {
1078
+ Object.assign(global, { TransformStream: webStreamsPolyfill.TransformStream, WritableStream: webStreamsPolyfill.WritableStream });
1079
+ };
1080
+
1081
+ /**
1082
+ * Sets up all available testing mocks in a single function call.
1083
+ *
1084
+ * This convenience function sets up all the test mocks provided by the library:
1085
+ * - Canvas API mocks
1086
+ * - Console error reporting to fail tests (includes automatic CSS parser error suppression)
1087
+ * - Google Maps API and components mocks
1088
+ * - Mapbox GL JS mocks
1089
+ * - React Helmet mocks
1090
+ * - IntersectionObserver mocks
1091
+ * - MatchMedia API mocks
1092
+ * - React Testing Library and Okta authentication mocks
1093
+ * - React Virtualized AutoSizer mocks
1094
+ * - ResizeObserver mocks
1095
+ * - Tanstack React Virtual mocks
1096
+ * - Time and language related mocks (timezone, timers, etc.)
1097
+ * - Translation mocks (i18n)
1098
+ * - Web Streams API mocks
1099
+ *
1100
+ * Using this function is equivalent to calling each setup function individually.
1101
+ *
1102
+ * @param options Configuration options for individual mocks
1103
+ * @param options.failOnConsoleOptions Options for setupFailOnConsole
1104
+ * @example
1105
+ * // In your jest setup file
1106
+ * import { setupAllMocks } from '@trackunit/react-vite-test-setup';
1107
+ *
1108
+ * setupAllMocks();
1109
+ *
1110
+ * // Or with options for specific mocks:
1111
+ * setupAllMocks({
1112
+ * failOnConsoleOptions: { shouldFailOnWarn: true }
1113
+ * });
1114
+ */
1115
+ const setupAllMocks = (options = {}) => {
1116
+ setupFailOnConsole(options.failOnConsoleOptions);
1117
+ setupGoogleMaps();
1118
+ setupMapbox();
1119
+ setupHelmetMock();
1120
+ setupIntersectionObserver();
1121
+ setupMatchMediaMock();
1122
+ setupReactTestingLibrary();
1123
+ setupReactVirtualizedAutoSizer();
1124
+ setupResizeObserver();
1125
+ setupTanstackReactVirtual();
1126
+ setupTimeAndLanguage();
1127
+ setupTranslations();
1128
+ setupWebStreams();
1129
+ };
1130
+
1131
+ /**
1132
+ * Sets up essential testing mocks with no external library dependencies.
1133
+ *
1134
+ * @param options Configuration options for the mocks
1135
+ */
1136
+ const setupBasicMocks = (options = {}) => {
1137
+ setupIntersectionObserver();
1138
+ setupMatchMediaMock();
1139
+ setupResizeObserver();
1140
+ setupWebStreams();
1141
+ // Time and error handling
1142
+ setupTimeAndLanguage();
1143
+ setupFailOnConsole(options.failOnConsoleOptions);
1144
+ };
1145
+
1146
+ /**
1147
+ * Sets up default testing mocks that cover most common React testing needs.
1148
+ *
1149
+ * This convenience function provides a balanced set of mocks that cover most React
1150
+ * application testing needs. It includes all the basic mocks plus essential React
1151
+ * testing utilities. Specifically, it includes:
1152
+ *
1153
+ * From setupBasicMocks:
1154
+ * - Canvas API mocks
1155
+ * - Console error reporting to fail tests
1156
+ * - IntersectionObserver mocks
1157
+ * - MatchMedia API mocks
1158
+ * - ResizeObserver mocks
1159
+ * - Timer mocks (setTimeout, setInterval)
1160
+ * - Web Streams API mocks
1161
+ *
1162
+ * Plus these additional mocks:
1163
+ * - React Testing Library and Okta authentication mocks
1164
+ * - Translation mocks (i18n)
1165
+ *
1166
+ * This is ideal for most React applications that use i18n translations and need
1167
+ * standard testing environment setup, without requiring specialized mocks for things
1168
+ * like Google Maps or virtualized lists.
1169
+ *
1170
+ * @param options Configuration options for individual mocks
1171
+ * @param options.failOnConsoleOptions Options for setupFailOnConsole
1172
+ * @example
1173
+ * // In your jest setup file
1174
+ * import { setupDefaultMocks } from '@trackunit/react-vite-test-setup';
1175
+ *
1176
+ * setupDefaultMocks();
1177
+ *
1178
+ * // Or with options for specific mocks:
1179
+ * setupDefaultMocks({
1180
+ * failOnConsoleOptions: { shouldFailOnWarn: true }
1181
+ * });
1182
+ */
1183
+ /**
1184
+ * Sets up default testing mocks covering most common React testing scenarios.
1185
+ *
1186
+ * @param options Configuration options for the mocks
1187
+ */
1188
+ const setupDefaultMocks = (options = {}) => {
1189
+ // Set up all basic mocks first
1190
+ setupBasicMocks(options);
1191
+ // Add standard React testing utilities
1192
+ setupReactTestingLibrary();
1193
+ setupTranslations();
1194
+ };
1195
+
1196
+ /**
1197
+ * Mocks the `@tanstack/react-router` library for testing environments.
1198
+ *
1199
+ * Replaces `useNavigate` with a no-op so components that call the
1200
+ * navigation function during a test do not throw outside a router context.
1201
+ *
1202
+ * Opt-in: the mock is registered via `vi.doMock` so it is
1203
+ * NOT hoisted and only takes effect once `setupTanstackReactRouter()` is
1204
+ * called from a test setup file. Tests that need the real `useNavigate`
1205
+ * (or their own mock) are unaffected unless they explicitly call this.
1206
+ *
1207
+ * @example
1208
+ * // In your vitest setup file
1209
+ * import { setupTanstackReactRouter } from '@trackunit/react-vite-test-setup';
1210
+ *
1211
+ * setupTanstackReactRouter();
1212
+ */
1213
+ const setupTanstackReactRouter = () => {
1214
+ vitest.vi.doMock("@tanstack/react-router", async () => ({
1215
+ ...(await vitest.vi.importActual("@tanstack/react-router")),
1216
+ useNavigate: () => () => {
1217
+ /* noop */
1218
+ },
1219
+ }));
1220
+ };
1221
+
1222
+ exports.createMockClickEvent = createMockClickEvent;
1223
+ exports.createMockMapboxMap = createMockMapboxMap;
1224
+ exports.setupAllMocks = setupAllMocks;
1225
+ exports.setupBasicMocks = setupBasicMocks;
1226
+ exports.setupCanvasMock = setupCanvasMock;
1227
+ exports.setupDefaultMocks = setupDefaultMocks;
1228
+ exports.setupFailOnConsole = setupFailOnConsole;
1229
+ exports.setupGoogleMaps = setupGoogleMaps;
1230
+ exports.setupHelmetMock = setupHelmetMock;
1231
+ exports.setupIntersectionObserver = setupIntersectionObserver;
1232
+ exports.setupMapbox = setupMapbox;
1233
+ exports.setupMatchMediaMock = setupMatchMediaMock;
1234
+ exports.setupReactTestingLibrary = setupReactTestingLibrary;
1235
+ exports.setupReactVirtualizedAutoSizer = setupReactVirtualizedAutoSizer;
1236
+ exports.setupResizeObserver = setupResizeObserver;
1237
+ exports.setupTanstackReactRouter = setupTanstackReactRouter;
1238
+ exports.setupTanstackReactVirtual = setupTanstackReactVirtual;
1239
+ exports.setupTimeAndLanguage = setupTimeAndLanguage;
1240
+ exports.setupTimeZone = setupTimeZone;
1241
+ exports.setupTranslations = setupTranslations;
1242
+ exports.setupWebStreams = setupWebStreams;