@openmrs/esm-dynamic-loading 9.0.3-pre.4533 → 9.0.3-pre.4550
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/.turbo/turbo-build.log +1 -1
- package/dist/dynamic-loading.d.ts.map +1 -1
- package/dist/dynamic-loading.js +4 -4
- package/dist/import-maps.d.ts +50 -0
- package/dist/import-maps.d.ts.map +1 -0
- package/dist/import-maps.js +246 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/route-maps.d.ts +46 -0
- package/dist/route-maps.d.ts.map +1 -0
- package/dist/route-maps.js +252 -0
- package/package.json +7 -4
- package/src/dynamic-loading.test.ts +426 -0
- package/src/dynamic-loading.ts +4 -4
- package/src/import-maps.test.ts +363 -0
- package/src/import-maps.ts +274 -0
- package/src/index.ts +2 -0
- package/src/route-maps.test.ts +481 -0
- package/src/route-maps.ts +285 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import createFetchMock from 'vitest-fetch-mock';
|
|
3
|
+
|
|
4
|
+
createFetchMock(vi).enableMocks();
|
|
5
|
+
|
|
6
|
+
describe('route-maps', () => {
|
|
7
|
+
function setDomRouteMaps(maps: Array<Record<string, unknown>>) {
|
|
8
|
+
document.querySelectorAll("script[type='openmrs-routes']").forEach((el) => el.remove());
|
|
9
|
+
|
|
10
|
+
for (const map of maps) {
|
|
11
|
+
const script = document.createElement('script');
|
|
12
|
+
script.type = 'openmrs-routes';
|
|
13
|
+
script.textContent = JSON.stringify(map);
|
|
14
|
+
document.head.appendChild(script);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
localStorage.clear();
|
|
20
|
+
fetchMock.resetMocks();
|
|
21
|
+
document.querySelectorAll("script[type='openmrs-routes']").forEach((el) => el.remove());
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
vi.restoreAllMocks();
|
|
26
|
+
Object.defineProperty(window, 'spaEnv', { value: undefined, writable: true, configurable: true });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('production mode', () => {
|
|
30
|
+
beforeEach(async () => {
|
|
31
|
+
(window as any).spaEnv = 'production';
|
|
32
|
+
vi.resetModules();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('getCurrentRouteMap returns base map without overrides', async () => {
|
|
36
|
+
const { setupRouteMapOverrides, getCurrentRouteMap } = await import('./route-maps');
|
|
37
|
+
await setupRouteMapOverrides();
|
|
38
|
+
|
|
39
|
+
setDomRouteMaps([{ '@openmrs/esm-foo': { pages: [] }, '@openmrs/esm-bar': { extensions: [] } }]);
|
|
40
|
+
localStorage.setItem(
|
|
41
|
+
'openmrs-routes:@openmrs/esm-foo',
|
|
42
|
+
JSON.stringify({ pages: [{ component: 'evil', route: '/' }] }),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const map = await getCurrentRouteMap();
|
|
46
|
+
expect(map['@openmrs/esm-foo']).toEqual({ pages: [] });
|
|
47
|
+
expect(map['@openmrs/esm-bar']).toEqual({ extensions: [] });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('getRouteMapNextPageMap returns base map only', async () => {
|
|
51
|
+
const { setupRouteMapOverrides, getRouteMapNextPageMap } = await import('./route-maps');
|
|
52
|
+
await setupRouteMapOverrides();
|
|
53
|
+
|
|
54
|
+
setDomRouteMaps([{ '@openmrs/esm-foo': { pages: [] } }]);
|
|
55
|
+
localStorage.setItem(
|
|
56
|
+
'openmrs-routes:@openmrs/esm-foo',
|
|
57
|
+
JSON.stringify({ pages: [{ component: 'evil', route: '/' }] }),
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const map = await getRouteMapNextPageMap();
|
|
61
|
+
expect(map['@openmrs/esm-foo']).toEqual({ pages: [] });
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('getRouteMapOverrideMap returns empty object', async () => {
|
|
65
|
+
const { setupRouteMapOverrides, getRouteMapOverrideMap } = await import('./route-maps');
|
|
66
|
+
await setupRouteMapOverrides();
|
|
67
|
+
|
|
68
|
+
localStorage.setItem('openmrs-routes:@openmrs/esm-foo', JSON.stringify({ pages: [] }));
|
|
69
|
+
expect(getRouteMapOverrideMap()).toEqual({});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('addRouteMapOverride is a no-op', async () => {
|
|
73
|
+
const { setupRouteMapOverrides, addRouteMapOverride } = await import('./route-maps');
|
|
74
|
+
await setupRouteMapOverrides();
|
|
75
|
+
|
|
76
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
77
|
+
addRouteMapOverride('@openmrs/esm-foo', { pages: [] });
|
|
78
|
+
expect(localStorage.getItem('openmrs-routes:@openmrs/esm-foo')).toBeNull();
|
|
79
|
+
expect(warn).toHaveBeenCalledWith(expect.stringContaining('disabled outside development'));
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('removeRouteMapOverride is a no-op', async () => {
|
|
83
|
+
const { setupRouteMapOverrides, removeRouteMapOverride } = await import('./route-maps');
|
|
84
|
+
await setupRouteMapOverrides();
|
|
85
|
+
|
|
86
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
87
|
+
removeRouteMapOverride('@openmrs/esm-foo');
|
|
88
|
+
expect(warn).toHaveBeenCalledWith(expect.stringContaining('disabled outside development'));
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('resetRouteMapOverrides is a no-op', async () => {
|
|
92
|
+
const { setupRouteMapOverrides, resetRouteMapOverrides } = await import('./route-maps');
|
|
93
|
+
await setupRouteMapOverrides();
|
|
94
|
+
|
|
95
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
96
|
+
resetRouteMapOverrides();
|
|
97
|
+
expect(warn).toHaveBeenCalledWith(expect.stringContaining('disabled outside development'));
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe('development mode', () => {
|
|
102
|
+
beforeEach(async () => {
|
|
103
|
+
(window as any).spaEnv = 'development';
|
|
104
|
+
vi.resetModules();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('getCurrentRouteMap merges base map with JSON object overrides', async () => {
|
|
108
|
+
setDomRouteMaps([{ '@openmrs/esm-foo': { pages: [] }, '@openmrs/esm-bar': { extensions: [] } }]);
|
|
109
|
+
localStorage.setItem(
|
|
110
|
+
'openmrs-routes:@openmrs/esm-foo',
|
|
111
|
+
JSON.stringify({ pages: [{ component: 'root', route: '/' }] }),
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const { setupRouteMapOverrides, getCurrentRouteMap } = await import('./route-maps');
|
|
115
|
+
await setupRouteMapOverrides();
|
|
116
|
+
|
|
117
|
+
const map = await getCurrentRouteMap();
|
|
118
|
+
expect(map['@openmrs/esm-foo']).toEqual({ pages: [{ component: 'root', route: '/' }] });
|
|
119
|
+
expect(map['@openmrs/esm-bar']).toEqual({ extensions: [] });
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('getCurrentRouteMap merges base map with URL-fetched overrides', async () => {
|
|
123
|
+
setDomRouteMaps([{ '@openmrs/esm-foo': { pages: [] } }]);
|
|
124
|
+
localStorage.setItem('openmrs-routes:@openmrs/esm-foo', JSON.stringify('http://localhost:8080/routes.json'));
|
|
125
|
+
fetchMock.mockResponseOnce(JSON.stringify({ pages: [{ component: 'root', route: '/fetched' }] }));
|
|
126
|
+
|
|
127
|
+
const { setupRouteMapOverrides, getCurrentRouteMap } = await import('./route-maps');
|
|
128
|
+
await setupRouteMapOverrides();
|
|
129
|
+
|
|
130
|
+
const map = await getCurrentRouteMap();
|
|
131
|
+
expect(map['@openmrs/esm-foo']).toEqual({ pages: [{ component: 'root', route: '/fetched' }] });
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('getRouteMapDefaultMap returns only the base map', async () => {
|
|
135
|
+
setDomRouteMaps([{ '@openmrs/esm-foo': { pages: [] } }]);
|
|
136
|
+
localStorage.setItem(
|
|
137
|
+
'openmrs-routes:@openmrs/esm-foo',
|
|
138
|
+
JSON.stringify({ pages: [{ component: 'x', route: '/' }] }),
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
const { setupRouteMapOverrides, getRouteMapDefaultMap } = await import('./route-maps');
|
|
142
|
+
await setupRouteMapOverrides();
|
|
143
|
+
|
|
144
|
+
const map = await getRouteMapDefaultMap();
|
|
145
|
+
expect(map['@openmrs/esm-foo']).toEqual({ pages: [] });
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('addRouteMapOverride stores an object in localStorage', async () => {
|
|
149
|
+
const { setupRouteMapOverrides, addRouteMapOverride } = await import('./route-maps');
|
|
150
|
+
await setupRouteMapOverrides();
|
|
151
|
+
|
|
152
|
+
addRouteMapOverride('@openmrs/esm-foo', { pages: [{ component: 'root', route: '/' }] });
|
|
153
|
+
expect(localStorage.getItem('openmrs-routes:@openmrs/esm-foo')).toBe(
|
|
154
|
+
'{"pages":[{"component":"root","route":"/"}]}',
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('addRouteMapOverride stores a JSON string in localStorage', async () => {
|
|
159
|
+
const { setupRouteMapOverrides, addRouteMapOverride } = await import('./route-maps');
|
|
160
|
+
await setupRouteMapOverrides();
|
|
161
|
+
|
|
162
|
+
addRouteMapOverride('@openmrs/esm-foo', JSON.stringify({ pages: [{ component: 'root', route: '/' }] }));
|
|
163
|
+
expect(localStorage.getItem('openmrs-routes:@openmrs/esm-foo')).toBe(
|
|
164
|
+
'{"pages":[{"component":"root","route":"/"}]}',
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('addRouteMapOverride stores an HTTP URL string in localStorage', async () => {
|
|
169
|
+
const { setupRouteMapOverrides, addRouteMapOverride } = await import('./route-maps');
|
|
170
|
+
await setupRouteMapOverrides();
|
|
171
|
+
|
|
172
|
+
addRouteMapOverride('@openmrs/esm-foo', 'http://localhost/my-route-override.json');
|
|
173
|
+
expect(localStorage.getItem('openmrs-routes:@openmrs/esm-foo')).toBe('"http://localhost/my-route-override.json"');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('addRouteMapOverride stores a URL object in localStorage', async () => {
|
|
177
|
+
const { setupRouteMapOverrides, addRouteMapOverride } = await import('./route-maps');
|
|
178
|
+
await setupRouteMapOverrides();
|
|
179
|
+
|
|
180
|
+
addRouteMapOverride('@openmrs/esm-foo', new URL('http://localhost/my-route-override.json'));
|
|
181
|
+
expect(localStorage.getItem('openmrs-routes:@openmrs/esm-foo')).toBe('"http://localhost/my-route-override.json"');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('removeRouteMapOverride removes from localStorage', async () => {
|
|
185
|
+
localStorage.setItem('openmrs-routes:@openmrs/esm-foo', JSON.stringify({ pages: [] }));
|
|
186
|
+
|
|
187
|
+
const { setupRouteMapOverrides, removeRouteMapOverride } = await import('./route-maps');
|
|
188
|
+
await setupRouteMapOverrides();
|
|
189
|
+
|
|
190
|
+
removeRouteMapOverride('@openmrs/esm-foo');
|
|
191
|
+
expect(localStorage.getItem('openmrs-routes:@openmrs/esm-foo')).toBeNull();
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('resetRouteMapOverrides clears all override keys', async () => {
|
|
195
|
+
localStorage.setItem('openmrs-routes:@openmrs/esm-foo', JSON.stringify({ pages: [] }));
|
|
196
|
+
localStorage.setItem('openmrs-routes:@openmrs/esm-bar', JSON.stringify({ extensions: [] }));
|
|
197
|
+
localStorage.setItem('unrelated-key', 'value');
|
|
198
|
+
|
|
199
|
+
const { setupRouteMapOverrides, resetRouteMapOverrides } = await import('./route-maps');
|
|
200
|
+
await setupRouteMapOverrides();
|
|
201
|
+
|
|
202
|
+
resetRouteMapOverrides();
|
|
203
|
+
expect(localStorage.getItem('openmrs-routes:@openmrs/esm-foo')).toBeNull();
|
|
204
|
+
expect(localStorage.getItem('openmrs-routes:@openmrs/esm-bar')).toBeNull();
|
|
205
|
+
expect(localStorage.getItem('unrelated-key')).toBe('value');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('addRouteMapOverride fires change event', async () => {
|
|
209
|
+
const { setupRouteMapOverrides, addRouteMapOverride } = await import('./route-maps');
|
|
210
|
+
await setupRouteMapOverrides();
|
|
211
|
+
|
|
212
|
+
const handler = vi.fn();
|
|
213
|
+
window.addEventListener('openmrs-routes:change', handler);
|
|
214
|
+
|
|
215
|
+
addRouteMapOverride('@openmrs/esm-foo', { pages: [] });
|
|
216
|
+
expect(handler).toHaveBeenCalledTimes(1);
|
|
217
|
+
|
|
218
|
+
window.removeEventListener('openmrs-routes:change', handler);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('getRouteMapOverrideMap returns raw localStorage entries in dev mode', async () => {
|
|
222
|
+
localStorage.setItem('openmrs-routes:@openmrs/esm-foo', JSON.stringify({ pages: [] }));
|
|
223
|
+
localStorage.setItem('openmrs-routes:@openmrs/esm-bar', JSON.stringify('http://localhost/bar-routes.json'));
|
|
224
|
+
localStorage.setItem('unrelated-key', 'value');
|
|
225
|
+
|
|
226
|
+
const { setupRouteMapOverrides, getRouteMapOverrideMap } = await import('./route-maps');
|
|
227
|
+
await setupRouteMapOverrides();
|
|
228
|
+
|
|
229
|
+
const overrides = getRouteMapOverrideMap();
|
|
230
|
+
expect(Object.keys(overrides)).toHaveLength(2);
|
|
231
|
+
expect(overrides['@openmrs/esm-foo']).toBe(JSON.stringify({ pages: [] }));
|
|
232
|
+
expect(overrides['@openmrs/esm-bar']).toBe(JSON.stringify('http://localhost/bar-routes.json'));
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('addRouteMapOverride rejects an invalid JSON string', async () => {
|
|
236
|
+
const { setupRouteMapOverrides, addRouteMapOverride } = await import('./route-maps');
|
|
237
|
+
await setupRouteMapOverrides();
|
|
238
|
+
|
|
239
|
+
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
240
|
+
addRouteMapOverride('@openmrs/esm-foo', JSON.stringify({ pages: 'not an array' }));
|
|
241
|
+
expect(localStorage.getItem('openmrs-routes:@openmrs/esm-foo')).toBeNull();
|
|
242
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('not a valid OpenmrsAppRoutes'), expect.anything());
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('getRouteMapNextPageMap reflects overrides added after setup', async () => {
|
|
246
|
+
setDomRouteMaps([{ '@openmrs/esm-foo': { pages: [] } }]);
|
|
247
|
+
|
|
248
|
+
const { setupRouteMapOverrides, addRouteMapOverride, getCurrentRouteMap, getRouteMapNextPageMap } = await import(
|
|
249
|
+
'./route-maps'
|
|
250
|
+
);
|
|
251
|
+
await setupRouteMapOverrides();
|
|
252
|
+
|
|
253
|
+
addRouteMapOverride('@openmrs/esm-foo', { pages: [{ component: 'new', route: '/new' }] });
|
|
254
|
+
|
|
255
|
+
// getCurrentRouteMap uses the snapshot — doesn't reflect post-setup changes
|
|
256
|
+
const currentMap = await getCurrentRouteMap();
|
|
257
|
+
expect(currentMap['@openmrs/esm-foo']).toEqual({ pages: [] });
|
|
258
|
+
|
|
259
|
+
// getRouteMapNextPageMap reads live overrides
|
|
260
|
+
const nextMap = await getRouteMapNextPageMap();
|
|
261
|
+
expect(nextMap['@openmrs/esm-foo']).toEqual({ pages: [{ component: 'new', route: '/new' }] });
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
describe('merging behavior', () => {
|
|
266
|
+
it('later route maps override earlier ones', async () => {
|
|
267
|
+
(window as any).spaEnv = 'production';
|
|
268
|
+
vi.resetModules();
|
|
269
|
+
|
|
270
|
+
setDomRouteMaps([
|
|
271
|
+
{ '@openmrs/esm-foo': { pages: [{ component: 'v1', route: '/' }] } },
|
|
272
|
+
{ '@openmrs/esm-foo': { pages: [{ component: 'v2', route: '/' }] } },
|
|
273
|
+
]);
|
|
274
|
+
|
|
275
|
+
const { setupRouteMapOverrides, getCurrentRouteMap } = await import('./route-maps');
|
|
276
|
+
await setupRouteMapOverrides();
|
|
277
|
+
|
|
278
|
+
const map = await getCurrentRouteMap();
|
|
279
|
+
expect(map['@openmrs/esm-foo']).toEqual({ pages: [{ component: 'v2', route: '/' }] });
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
describe('error handling', () => {
|
|
284
|
+
it('reads route maps from remote script src attributes', async () => {
|
|
285
|
+
(window as any).spaEnv = 'production';
|
|
286
|
+
vi.resetModules();
|
|
287
|
+
|
|
288
|
+
const script = document.createElement('script');
|
|
289
|
+
script.type = 'openmrs-routes';
|
|
290
|
+
Object.defineProperty(script, 'src', { value: 'http://localhost/routes.json', writable: false });
|
|
291
|
+
document.head.appendChild(script);
|
|
292
|
+
|
|
293
|
+
fetchMock.mockResponseOnce(
|
|
294
|
+
JSON.stringify({ '@openmrs/esm-remote': { pages: [{ component: 'root', route: '/remote' }] } }),
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
const { setupRouteMapOverrides, getCurrentRouteMap } = await import('./route-maps');
|
|
298
|
+
await setupRouteMapOverrides();
|
|
299
|
+
|
|
300
|
+
const map = await getCurrentRouteMap();
|
|
301
|
+
expect(map['@openmrs/esm-remote']).toEqual({ pages: [{ component: 'root', route: '/remote' }] });
|
|
302
|
+
expect(fetchMock).toHaveBeenCalledWith('http://localhost/routes.json');
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('merges remote and inline route maps', async () => {
|
|
306
|
+
(window as any).spaEnv = 'production';
|
|
307
|
+
vi.resetModules();
|
|
308
|
+
|
|
309
|
+
const inline = document.createElement('script');
|
|
310
|
+
inline.type = 'openmrs-routes';
|
|
311
|
+
inline.textContent = JSON.stringify({ '@openmrs/esm-inline': { extensions: [] } });
|
|
312
|
+
document.head.appendChild(inline);
|
|
313
|
+
|
|
314
|
+
const remote = document.createElement('script');
|
|
315
|
+
remote.type = 'openmrs-routes';
|
|
316
|
+
Object.defineProperty(remote, 'src', { value: 'http://localhost/routes.json', writable: false });
|
|
317
|
+
document.head.appendChild(remote);
|
|
318
|
+
|
|
319
|
+
fetchMock.mockResponseOnce(JSON.stringify({ '@openmrs/esm-remote': { pages: [] } }));
|
|
320
|
+
|
|
321
|
+
const { setupRouteMapOverrides, getCurrentRouteMap } = await import('./route-maps');
|
|
322
|
+
await setupRouteMapOverrides();
|
|
323
|
+
|
|
324
|
+
const map = await getCurrentRouteMap();
|
|
325
|
+
expect(map['@openmrs/esm-inline']).toEqual({ extensions: [] });
|
|
326
|
+
expect(map['@openmrs/esm-remote']).toEqual({ pages: [] });
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('skips remote route maps that fail to fetch', async () => {
|
|
330
|
+
(window as any).spaEnv = 'production';
|
|
331
|
+
vi.resetModules();
|
|
332
|
+
|
|
333
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
334
|
+
|
|
335
|
+
const good = document.createElement('script');
|
|
336
|
+
good.type = 'openmrs-routes';
|
|
337
|
+
good.textContent = JSON.stringify({ '@openmrs/esm-foo': { pages: [] } });
|
|
338
|
+
document.head.appendChild(good);
|
|
339
|
+
|
|
340
|
+
const bad = document.createElement('script');
|
|
341
|
+
bad.type = 'openmrs-routes';
|
|
342
|
+
Object.defineProperty(bad, 'src', { value: 'http://localhost/broken.json', writable: false });
|
|
343
|
+
document.head.appendChild(bad);
|
|
344
|
+
|
|
345
|
+
fetchMock.mockRejectOnce(new Error('Network error'));
|
|
346
|
+
|
|
347
|
+
const { setupRouteMapOverrides, getCurrentRouteMap } = await import('./route-maps');
|
|
348
|
+
await setupRouteMapOverrides();
|
|
349
|
+
|
|
350
|
+
const map = await getCurrentRouteMap();
|
|
351
|
+
expect(map['@openmrs/esm-foo']).toEqual({ pages: [] });
|
|
352
|
+
expect(map['@openmrs/esm-broken']).toBeUndefined();
|
|
353
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to parse routes'), expect.anything());
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('skips remote route maps that return invalid JSON', async () => {
|
|
357
|
+
(window as any).spaEnv = 'production';
|
|
358
|
+
vi.resetModules();
|
|
359
|
+
|
|
360
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
361
|
+
|
|
362
|
+
const script = document.createElement('script');
|
|
363
|
+
script.type = 'openmrs-routes';
|
|
364
|
+
Object.defineProperty(script, 'src', { value: 'http://localhost/bad.json', writable: false });
|
|
365
|
+
document.head.appendChild(script);
|
|
366
|
+
|
|
367
|
+
fetchMock.mockResponseOnce('not json', { status: 200 });
|
|
368
|
+
|
|
369
|
+
const { setupRouteMapOverrides, getCurrentRouteMap } = await import('./route-maps');
|
|
370
|
+
await setupRouteMapOverrides();
|
|
371
|
+
|
|
372
|
+
const map = await getCurrentRouteMap();
|
|
373
|
+
expect(Object.keys(map)).toHaveLength(0);
|
|
374
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to parse routes'), expect.anything());
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
it('skips remote route maps that return non-OpenmrsRoutes data', async () => {
|
|
378
|
+
(window as any).spaEnv = 'production';
|
|
379
|
+
vi.resetModules();
|
|
380
|
+
|
|
381
|
+
const script = document.createElement('script');
|
|
382
|
+
script.type = 'openmrs-routes';
|
|
383
|
+
Object.defineProperty(script, 'src', { value: 'http://localhost/notroutes.json', writable: false });
|
|
384
|
+
document.head.appendChild(script);
|
|
385
|
+
|
|
386
|
+
fetchMock.mockResponseOnce(JSON.stringify({ something: 'unexpected' }));
|
|
387
|
+
|
|
388
|
+
const { setupRouteMapOverrides, getCurrentRouteMap } = await import('./route-maps');
|
|
389
|
+
await setupRouteMapOverrides();
|
|
390
|
+
|
|
391
|
+
const map = await getCurrentRouteMap();
|
|
392
|
+
expect(Object.keys(map)).toHaveLength(0);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('skips malformed inline route map script tags', async () => {
|
|
396
|
+
(window as any).spaEnv = 'production';
|
|
397
|
+
vi.resetModules();
|
|
398
|
+
|
|
399
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
400
|
+
|
|
401
|
+
const good = document.createElement('script');
|
|
402
|
+
good.type = 'openmrs-routes';
|
|
403
|
+
good.textContent = JSON.stringify({ '@openmrs/esm-foo': { pages: [] } });
|
|
404
|
+
document.head.appendChild(good);
|
|
405
|
+
|
|
406
|
+
const bad = document.createElement('script');
|
|
407
|
+
bad.type = 'openmrs-routes';
|
|
408
|
+
bad.textContent = '{ not valid json';
|
|
409
|
+
document.head.appendChild(bad);
|
|
410
|
+
|
|
411
|
+
const { setupRouteMapOverrides, getCurrentRouteMap } = await import('./route-maps');
|
|
412
|
+
await setupRouteMapOverrides();
|
|
413
|
+
|
|
414
|
+
const map = await getCurrentRouteMap();
|
|
415
|
+
expect(map['@openmrs/esm-foo']).toEqual({ pages: [] });
|
|
416
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to parse routes'), expect.anything());
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('skips failed URL override fetches gracefully', async () => {
|
|
420
|
+
(window as any).spaEnv = 'development';
|
|
421
|
+
vi.resetModules();
|
|
422
|
+
|
|
423
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
424
|
+
localStorage.setItem('openmrs-routes:@openmrs/esm-foo', JSON.stringify('http://localhost:9999/bad.json'));
|
|
425
|
+
fetchMock.mockRejectOnce(new Error('Network error'));
|
|
426
|
+
|
|
427
|
+
const { setupRouteMapOverrides, getCurrentRouteMap } = await import('./route-maps');
|
|
428
|
+
await setupRouteMapOverrides();
|
|
429
|
+
|
|
430
|
+
const map = await getCurrentRouteMap();
|
|
431
|
+
expect(map['@openmrs/esm-foo']).toBeUndefined();
|
|
432
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to load route override'), expect.anything());
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('skips URL overrides that return non-OpenmrsAppRoutes data', async () => {
|
|
436
|
+
(window as any).spaEnv = 'development';
|
|
437
|
+
vi.resetModules();
|
|
438
|
+
|
|
439
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
440
|
+
localStorage.setItem('openmrs-routes:@openmrs/esm-foo', JSON.stringify('http://localhost/bad-routes.json'));
|
|
441
|
+
fetchMock.mockResponseOnce(JSON.stringify({ pages: 'not an array' }));
|
|
442
|
+
|
|
443
|
+
const { setupRouteMapOverrides, getCurrentRouteMap } = await import('./route-maps');
|
|
444
|
+
await setupRouteMapOverrides();
|
|
445
|
+
|
|
446
|
+
const map = await getCurrentRouteMap();
|
|
447
|
+
expect(map['@openmrs/esm-foo']).toBeUndefined();
|
|
448
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to load route override'), expect.anything());
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it('skips overrides that are neither objects nor URL strings', async () => {
|
|
452
|
+
(window as any).spaEnv = 'development';
|
|
453
|
+
vi.resetModules();
|
|
454
|
+
|
|
455
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
456
|
+
localStorage.setItem('openmrs-routes:@openmrs/esm-foo', JSON.stringify(42));
|
|
457
|
+
|
|
458
|
+
const { setupRouteMapOverrides, getCurrentRouteMap } = await import('./route-maps');
|
|
459
|
+
await setupRouteMapOverrides();
|
|
460
|
+
|
|
461
|
+
const map = await getCurrentRouteMap();
|
|
462
|
+
expect(map['@openmrs/esm-foo']).toBeUndefined();
|
|
463
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to load route override'), expect.anything());
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it('handles invalid JSON in localStorage overrides', async () => {
|
|
467
|
+
(window as any).spaEnv = 'development';
|
|
468
|
+
vi.resetModules();
|
|
469
|
+
|
|
470
|
+
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
471
|
+
localStorage.setItem('openmrs-routes:@openmrs/esm-foo', 'not json at all');
|
|
472
|
+
|
|
473
|
+
const { setupRouteMapOverrides, getCurrentRouteMap } = await import('./route-maps');
|
|
474
|
+
await setupRouteMapOverrides();
|
|
475
|
+
|
|
476
|
+
const map = await getCurrentRouteMap();
|
|
477
|
+
expect(map['@openmrs/esm-foo']).toBeUndefined();
|
|
478
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('Failed to load route override'), expect.anything());
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
});
|