@servicetitan/web-components 29.0.0 → 30.1.0

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.
Files changed (40) hide show
  1. package/dist/__mocks__/test-component.d.ts.map +1 -1
  2. package/dist/__mocks__/test-component.js +2 -1
  3. package/dist/__mocks__/test-component.js.map +1 -1
  4. package/dist/contexts/mfe-data-context.d.ts +2 -4
  5. package/dist/contexts/mfe-data-context.d.ts.map +1 -1
  6. package/dist/contexts/mfe-data-context.js.map +1 -1
  7. package/dist/contexts/tests/mfe-data-context.test.js.map +1 -1
  8. package/dist/contexts/tests/mfe-metadata-context.test.js.map +1 -1
  9. package/dist/event-bus.d.ts +51 -15
  10. package/dist/event-bus.d.ts.map +1 -1
  11. package/dist/event-bus.js +46 -17
  12. package/dist/event-bus.js.map +1 -1
  13. package/dist/get-value-for-environment.d.ts +5 -1
  14. package/dist/get-value-for-environment.d.ts.map +1 -1
  15. package/dist/get-value-for-environment.js +2 -1
  16. package/dist/get-value-for-environment.js.map +1 -1
  17. package/dist/loader.d.ts +1 -1
  18. package/dist/loader.d.ts.map +1 -1
  19. package/dist/loader.js +5 -2
  20. package/dist/loader.js.map +1 -1
  21. package/dist/render.js +1 -1
  22. package/dist/render.js.map +1 -1
  23. package/dist/types.d.ts +3 -0
  24. package/dist/types.d.ts.map +1 -1
  25. package/dist/utils/get-ld-service-token.js +1 -1
  26. package/dist/utils/get-ld-service-token.js.map +1 -1
  27. package/package.json +14 -13
  28. package/src/__mocks__/test-component.tsx +3 -1
  29. package/src/__tests__/event-bus-types.test.ts +507 -0
  30. package/src/__tests__/event-bus.test.ts +5 -18
  31. package/src/__tests__/get-value-for-environment.test.ts +40 -22
  32. package/src/contexts/mfe-data-context.ts +2 -11
  33. package/src/contexts/tests/mfe-data-context.test.tsx +2 -2
  34. package/src/contexts/tests/mfe-metadata-context.test.tsx +2 -2
  35. package/src/event-bus.ts +105 -32
  36. package/src/get-value-for-environment.ts +9 -2
  37. package/src/loader.tsx +6 -1
  38. package/src/render.ts +1 -1
  39. package/src/types.ts +10 -0
  40. package/src/utils/get-ld-service-token.ts +1 -1
@@ -0,0 +1,507 @@
1
+ import { symbolToken } from '@servicetitan/react-ioc';
2
+ import { EventBus } from '../event-bus';
3
+
4
+ jest.mock('../event-bus', () => {
5
+ return {
6
+ EventBus: jest.fn().mockImplementation(() => {
7
+ return {
8
+ job: {
9
+ emit: jest.fn(),
10
+ on: jest.fn(),
11
+ off: jest.fn(),
12
+ },
13
+ emit: jest.fn(),
14
+ on: jest.fn(),
15
+ off: jest.fn(),
16
+ };
17
+ }),
18
+ };
19
+ });
20
+
21
+ describe(`[web-components] ${EventBus.name}`, () => {
22
+ describe('type definitions', () => {
23
+ describe('EventBus<T>', () => {
24
+ test('rejects invalid data type', () => {
25
+ // @ts-expect-error Types of property 'foo' are incompatible.
26
+ eventBus = EventBus<{ foo: () => void }>;
27
+ });
28
+ });
29
+
30
+ describe('when eventBus is not typed', () => {
31
+ const eventBus = new EventBus();
32
+
33
+ describe('emit', () => {
34
+ test('accepts valid event with valid data', () => {
35
+ eventBus.emit('foo', '');
36
+ eventBus.emit('bar', { value: 1 });
37
+ eventBus.emit(Symbol(), '');
38
+ eventBus.emit(123, '');
39
+ });
40
+
41
+ test('accepts event with no data', () => {
42
+ eventBus.emit('foo');
43
+ });
44
+
45
+ test('rejects invalid event', () => {
46
+ // @ts-expect-error Argument of type '() => void' is not assignable to parameter of type 'string | number | symbol | SymbolToken<any>'.
47
+ eventBus.emit(() => {}, '');
48
+
49
+ // @ts-expect-error Argument of type 'Date' is not assignable to parameter of type 'string | number | symbol | SymbolToken<any>'.
50
+ eventBus.emit(new Date(), '');
51
+
52
+ // @ts-expect-error Argument of type 'boolean' is not assignable to parameter of type 'string | number | symbol | SymbolToken<any>'.
53
+ eventBus.emit(true, '');
54
+ });
55
+
56
+ test('rejects invalid data', () => {
57
+ // @ts-expect-error Argument of type '() => void' is not assignable to parameter of type 'JSON | JSONPrimitive'
58
+ eventBus.emit('foo', () => {});
59
+
60
+ // @ts-expect-error Argument of type 'Date' is not assignable to parameter of type 'JSON | JSONPrimitive'.
61
+ eventBus.emit('foo', new Date());
62
+
63
+ eventBus.emit('foo', {
64
+ value: 1,
65
+ // @ts-expect-error Type '() => void' is not assignable to type 'JSON | JSONPrimitive | JSON[]'.
66
+ otherValue: () => {},
67
+ });
68
+ });
69
+ });
70
+
71
+ describe('on', () => {
72
+ test('accepts valid event with any handler', () => {
73
+ eventBus.on('foo', () => {});
74
+ eventBus.on('bar', (_value: string) => {});
75
+ eventBus.on('baz', ({ value: _value }: { value: Date }) => {});
76
+ eventBus.on(Symbol(), () => {});
77
+ eventBus.on(123, () => {});
78
+ });
79
+
80
+ test('rejects invalid event', () => {
81
+ // @ts-expect-error Argument of type 'Date' is not assignable to parameter of type 'JSON | JSONPrimitive'.
82
+ eventBus.on(new Date(), () => {});
83
+ });
84
+ });
85
+
86
+ describe('off', () => {
87
+ test('accepts valid event with any handler', () => {
88
+ eventBus.off('foo', () => {});
89
+ eventBus.off('bar', (_value: string) => {});
90
+ eventBus.off('baz', ({ value: _value }: { value: Date }) => {});
91
+ eventBus.off(Symbol(), () => {});
92
+ eventBus.off(123, () => {});
93
+ });
94
+
95
+ test('rejects invalid event', () => {
96
+ // @ts-expect-error Argument of type 'Date' is not assignable to parameter of type 'JSON | JSONPrimitive'.
97
+ eventBus.off(new Date(), () => {});
98
+ });
99
+ });
100
+
101
+ describe('job.emit', () => {
102
+ test('accepts valid event', () => {
103
+ eventBus.job.emit('foo');
104
+ });
105
+
106
+ test('rejects invalid event', () => {
107
+ // @ts-expect-error Argument of type 'Date' is not assignable to parameter of type 'JSON | JSONPrimitive'.
108
+ eventBus.job.emit(new Date());
109
+ });
110
+ });
111
+
112
+ describe('job.on', () => {
113
+ test('accepts valid event with valid handler', () => {
114
+ eventBus.job.on('foo', () => {});
115
+ eventBus.job.on('foo', ({ resolve }) => resolve({ value: '' }));
116
+ eventBus.job.on('foo', ({ reject }) => reject(new Error('Oops!')));
117
+ eventBus.job.on('foo', ({ reject }) => reject('Oops!'));
118
+ eventBus.job.on(Symbol(), () => {});
119
+ eventBus.job.on(123, () => {});
120
+ });
121
+
122
+ test('rejects invalid event', () => {
123
+ // @ts-expect-error Argument of type 'Date' is not assignable to parameter of type 'JSON | JSONPrimitive'.
124
+ eventBus.job.on(new Date(), () => {});
125
+ });
126
+
127
+ test('rejects invalid handler', () => {
128
+ eventBus.job.on('foo', ({ reject }) => {
129
+ // @ts-expect-error Argument of type '123' is not assignable to parameter of type 'string | Error | undefined'.
130
+ reject(123);
131
+ });
132
+ });
133
+ });
134
+
135
+ describe('job.off', () => {
136
+ test('accepts valid event with valid handler', () => {
137
+ eventBus.job.off('foo', () => {});
138
+ eventBus.job.off('foo', ({ resolve }) => resolve({ value: '' }));
139
+ eventBus.job.off('foo', ({ reject }) => reject(new Error('Oops!')));
140
+ eventBus.job.off('foo', ({ reject }) => reject('Oops!'));
141
+ eventBus.job.off(Symbol(), () => {});
142
+ eventBus.job.off(123, () => {});
143
+ });
144
+
145
+ test('rejects invalid event', () => {
146
+ // @ts-expect-error Argument of type 'Date' is not assignable to parameter of type 'JSON | JSONPrimitive'.
147
+ eventBus.job.off(new Date(), () => {});
148
+ });
149
+
150
+ test('rejects invalid handler', () => {
151
+ eventBus.job.off('foo', ({ reject }) => {
152
+ // @ts-expect-error Argument of type '123' is not assignable to parameter of type 'string | Error | undefined'.
153
+ reject(123);
154
+ });
155
+ });
156
+ });
157
+
158
+ describe('when event is symbolToken', () => {
159
+ const token = symbolToken<{ value: number }>('token');
160
+ const optionalPayloadToken = symbolToken<{ value: number } | undefined>(
161
+ 'optionalToken'
162
+ );
163
+
164
+ describe('emit', () => {
165
+ test('accepts valid data', () => {
166
+ eventBus.emit(token, { value: 1 });
167
+ eventBus.emit(optionalPayloadToken, { value: 1 });
168
+ eventBus.emit(optionalPayloadToken);
169
+ });
170
+
171
+ test('rejects invalid data', () => {
172
+ // @ts-expect-error Type 'string' is not assignable to type 'number'
173
+ eventBus.emit(token, { value: '' });
174
+ });
175
+
176
+ test('rejects missing data', () => {
177
+ // @ts-expect-error Expected 2 arguments, but got 1
178
+ eventBus.emit(token);
179
+ });
180
+
181
+ test('rejects invalid data type', () => {
182
+ const token = symbolToken<{ value: () => void }>('token');
183
+
184
+ // @ts-expect-error Argument of type 'SymbolToken<{ value: () => void; }>' is not assignable to parameter of type ...
185
+ eventBus.emit(token, { value: () => {} });
186
+ });
187
+ });
188
+
189
+ describe('on', () => {
190
+ test('accepts valid handler', () => {
191
+ eventBus.on(token, () => {});
192
+ eventBus.on(token, data => data.value++);
193
+ });
194
+
195
+ test('rejects invalid handler', () => {
196
+ // @ts-expect-error Argument of type '(_: number) => void' is not assignable to parameter of type ...
197
+ eventBus.on(token, (_: number) => {});
198
+
199
+ // @ts-expect-error Argument of type '(data: { value: string; }) => number' is not assignable to parameter of type ...
200
+ eventBus.on(token, (data: { value: string }) => data.value.split(''));
201
+ });
202
+ });
203
+
204
+ describe('off', () => {
205
+ test('accepts valid handler', () => {
206
+ eventBus.off(token, () => {});
207
+ eventBus.off(token, (data: { value: number }) => data.value++);
208
+ });
209
+
210
+ test('rejects invalid handler', () => {
211
+ // @ts-expect-error Argument of type '(_: number) => void' is not assignable to parameter of type ...
212
+ eventBus.off(token, (_: number) => {});
213
+
214
+ // @ts-expect-error Argument of type '(data: { value: string; }) => number' is not assignable to parameter of type ...
215
+ eventBus.off(token, (data: { value: string }) => data.value.split(''));
216
+ });
217
+ });
218
+
219
+ describe('job.emit', () => {
220
+ test('accepts token', () => {
221
+ eventBus.job.emit(token);
222
+ });
223
+ });
224
+
225
+ describe('job.on', () => {
226
+ test('accepts valid handler', () => {
227
+ eventBus.job.on(token, ({ resolve }) => resolve({ value: 1 }));
228
+ eventBus.job.on(token, ({ reject }) => reject(new Error('Oops!')));
229
+ eventBus.job.on(token, ({ reject }) => reject('Oops!'));
230
+ eventBus.job.on(optionalPayloadToken, ({ resolve }) =>
231
+ resolve({ value: 1 })
232
+ );
233
+ eventBus.job.on(optionalPayloadToken, ({ resolve }) => resolve());
234
+ });
235
+
236
+ test('rejects invalid handler', () => {
237
+ eventBus.job.on(token, ({ resolve }) => {
238
+ // @ts-expect-error Type 'string' is not assignable to type 'number'.ts(2322)
239
+ resolve({ value: '' });
240
+ });
241
+ eventBus.job.on(token, ({ reject }) => {
242
+ // @ts-expect-error Argument of type '123' is not assignable to parameter of type 'string | Error | undefined'.
243
+ reject(123);
244
+ });
245
+ });
246
+ });
247
+
248
+ describe('job.off', () => {
249
+ test('accepts valid handler', () => {
250
+ eventBus.job.off(token, ({ resolve }) => resolve({ value: 1 }));
251
+ eventBus.job.off(token, ({ reject }) => reject(new Error('Oops!')));
252
+ eventBus.job.off(token, ({ reject }) => reject('Oops!'));
253
+ });
254
+
255
+ test('rejects invalid handler', () => {
256
+ eventBus.job.off(token, ({ resolve }) => {
257
+ // @ts-expect-error Type 'string' is not assignable to type 'number'.ts(2322)
258
+ resolve({ value: '' });
259
+ });
260
+ eventBus.job.off(token, ({ reject }) => {
261
+ // @ts-expect-error Argument of type '123' is not assignable to parameter of type 'string | Error | undefined'.
262
+ reject(123);
263
+ });
264
+ });
265
+ });
266
+ });
267
+ });
268
+
269
+ describe('when eventBus is typed', () => {
270
+ interface TestEvents {
271
+ 'number-event': { value: number };
272
+ 'string-event': { value: string };
273
+ 'optional-payload-event': { value: number } | undefined;
274
+ }
275
+
276
+ const eventBus = new EventBus<TestEvents>();
277
+
278
+ describe('emit', () => {
279
+ test('accepts valid events and data', () => {
280
+ eventBus.emit('number-event', { value: 1 });
281
+ eventBus.emit('string-event', { value: '' });
282
+ eventBus.emit('optional-payload-event', { value: 1 });
283
+ eventBus.emit('optional-payload-event');
284
+ });
285
+
286
+ test('rejects invalid event', () => {
287
+ // @ts-expect-error '"foo"' is not assignable to parameter of type 'keyof TestEvents'
288
+ eventBus.emit('foo', { value: 1 });
289
+ });
290
+
291
+ test('rejects invalid data', () => {
292
+ // @ts-expect-error Type 'string' is not assignable to type 'number'
293
+ eventBus.emit('number-event', { value: '' });
294
+
295
+ // @ts-expect-error 'foo' does not exist in type '{ value: number; }'
296
+ eventBus.emit('number-event', { foo: 1 });
297
+ });
298
+
299
+ test('rejects missing data', () => {
300
+ // @ts-expect-error Expected 2 arguments, but got 1
301
+ eventBus.emit('number-event');
302
+ });
303
+
304
+ test('accepts event with no data', () => {
305
+ new EventBus<{ foo: never }>().emit('foo');
306
+ new EventBus<{ foo: undefined }>().emit('foo');
307
+ });
308
+ });
309
+
310
+ describe('on', () => {
311
+ test('accepts valid event and handler', () => {
312
+ eventBus.on('number-event', () => {});
313
+ eventBus.on('number-event', data => data.value++);
314
+ eventBus.on('string-event', data => data.value.split(''));
315
+ });
316
+
317
+ test('rejects invalid event', () => {
318
+ // @ts-expect-error Argument of type '"foo"' is not assignable to parameter of type 'keyof TestEvents'
319
+ eventBus.on('foo', () => {});
320
+ });
321
+
322
+ test('rejects invalid handler', () => {
323
+ // @ts-expect-error Property 'split' does not exist on type 'number'.
324
+ eventBus.on('number-event', data => data.value.split(''));
325
+
326
+ // @ts-expect-error An arithmetic operand must be of type 'any', 'number', 'bigint' or an enum type.
327
+ eventBus.on('string-event', data => data.value++);
328
+ });
329
+ });
330
+
331
+ describe('off', () => {
332
+ test('accepts valid event and handler', () => {
333
+ eventBus.off('number-event', () => {});
334
+ eventBus.off('number-event', data => data.value++);
335
+ eventBus.off('string-event', data => data.value.split(''));
336
+ });
337
+
338
+ test('rejects invalid event', () => {
339
+ // @ts-expect-error Argument of type '"foo"' is not assignable to parameter of type 'keyof TestEvents'
340
+ eventBus.off('foo', () => {});
341
+ });
342
+
343
+ test('rejects invalid handler', () => {
344
+ // @ts-expect-error Property 'split' does not exist on type 'number'.
345
+ eventBus.off('number-event', data => data.value.split(''));
346
+
347
+ // @ts-expect-error An arithmetic operand must be of type 'any', 'number', 'bigint' or an enum type.
348
+ eventBus.off('string-event', data => data.value++);
349
+ });
350
+ });
351
+
352
+ describe('job.emit', () => {
353
+ test('accepts valid event', () => {
354
+ eventBus.job.emit('number-event');
355
+ eventBus.job.emit('string-event');
356
+ });
357
+
358
+ test('rejects invalid event', () => {
359
+ // @ts-expect-error Argument of type '"foo"' is not assignable to parameter of type 'keyof TestEvents'.
360
+ eventBus.job.emit('foo');
361
+ });
362
+ });
363
+
364
+ describe('job.on', () => {
365
+ test('accepts valid event and handler', () => {
366
+ eventBus.job.on('number-event', () => {});
367
+ eventBus.job.on('number-event', ({ resolve }) => resolve({ foo: 1 }));
368
+ eventBus.job.on('string-event', ({ resolve }) => resolve({ foo: 1 }));
369
+ eventBus.job.on('string-event', ({ reject }) => reject(new Error('Oops!')));
370
+ eventBus.job.on('number-event', ({ reject }) => reject('Oops!'));
371
+ });
372
+
373
+ test('rejects invalid event', () => {
374
+ // @ts-expect-error Argument of type '"foo"' is not assignable to parameter of type 'keyof TestEvents'.
375
+ eventBus.job.on('foo', () => {});
376
+ });
377
+
378
+ test('rejects invalid handler', () => {
379
+ // @ts-expect-error Argument of type '123' is not assignable to parameter of type 'string | Error | undefined'
380
+ eventBus.job.on('number-event', ({ reject }) => reject(123));
381
+ });
382
+ });
383
+
384
+ describe('job.off', () => {
385
+ test('accepts valid event and handler', () => {
386
+ eventBus.job.off('number-event', () => {});
387
+ eventBus.job.off('number-event', ({ resolve }) => resolve({ foo: 1 }));
388
+ eventBus.job.off('string-event', ({ resolve }) => resolve({ foo: 1 }));
389
+ eventBus.job.off('string-event', ({ reject }) => reject(new Error('Oops!')));
390
+ eventBus.job.off('number-event', ({ reject }) => reject('Oops!'));
391
+ });
392
+
393
+ test('rejects invalid event', () => {
394
+ // @ts-expect-error Argument of type '"foo"' is not assignable to parameter of type 'keyof TestEvents'.
395
+ eventBus.job.off('foo', () => {});
396
+ });
397
+
398
+ test('rejects invalid handler', () => {
399
+ // @ts-expect-error Argument of type '123' is not assignable to parameter of type 'string | Error | undefined'
400
+ eventBus.job.off('number-event', ({ reject }) => reject(123));
401
+ });
402
+ });
403
+
404
+ describe('when event is symbolToken', () => {
405
+ const token = symbolToken<{ value: number }>('token');
406
+ const optionalPayloadToken = symbolToken<{ value: number } | undefined>(
407
+ 'optionalToken'
408
+ );
409
+
410
+ const eventBus = new EventBus<TestEvents>();
411
+
412
+ describe('emit', () => {
413
+ test('accepts valid data', () => {
414
+ eventBus.emit(token, { value: 1 });
415
+ eventBus.emit(optionalPayloadToken, { value: 1 });
416
+ eventBus.emit(optionalPayloadToken);
417
+ });
418
+
419
+ test('rejects invalid data', () => {
420
+ // @ts-expect-error Type 'string' is not assignable to type 'number'
421
+ eventBus.emit(token, { value: '' });
422
+ });
423
+
424
+ test('rejects missing value', () => {
425
+ // @ts-expect-error Expected 2 arguments, but got 1
426
+ eventBus.emit(token);
427
+ });
428
+ });
429
+
430
+ describe('on', () => {
431
+ test('accepts valid handler', () => {
432
+ eventBus.on(token, () => {});
433
+ eventBus.on(token, data => data.value++);
434
+ });
435
+
436
+ test('rejects invalid handler', () => {
437
+ // @ts-expect-error Argument of type '(_: number) => void' is not assignable to parameter of type ...
438
+ eventBus.on(token, (_: number) => {});
439
+
440
+ // @ts-expect-error Argument of type '(data: { value: string; }) => number' is not assignable to parameter of type ...
441
+ eventBus.on(token, (data: { value: string }) => data.value.split(''));
442
+ });
443
+ });
444
+
445
+ describe('off', () => {
446
+ test('accepts valid handler', () => {
447
+ eventBus.off(token, () => {});
448
+ eventBus.off(token, data => data.value++);
449
+ });
450
+
451
+ test('rejects invalid handler', () => {
452
+ // @ts-expect-error Argument of type '(_: number) => void' is not assignable to parameter of type ...
453
+ eventBus.off(token, (_: number) => {});
454
+
455
+ // @ts-expect-error Argument of type '(data: { value: string; }) => number' is not assignable to parameter of type ...
456
+ eventBus.off(token, (data: { value: string }) => data.value.split(''));
457
+ });
458
+ });
459
+
460
+ describe('job.emit', () => {
461
+ test('accepts token', () => {
462
+ eventBus.job.emit(token);
463
+ });
464
+ });
465
+
466
+ describe('job.on', () => {
467
+ test('accepts valid handler', () => {
468
+ eventBus.job.on(token, ({ resolve }) => resolve({ value: 1 }));
469
+ eventBus.job.on(token, ({ reject }) => reject(new Error('Oops!')));
470
+ eventBus.job.on(token, ({ reject }) => reject('Oops!'));
471
+ eventBus.job.on(optionalPayloadToken, ({ resolve }) => resolve());
472
+ });
473
+
474
+ test('rejects invalid handler', () => {
475
+ eventBus.job.on(token, ({ resolve }) => {
476
+ // @ts-expect-error Type 'string' is not assignable to type 'number'.ts(2322)
477
+ resolve({ value: '' });
478
+ });
479
+ eventBus.job.on(token, ({ reject }) => {
480
+ // @ts-expect-error Argument of type '123' is not assignable to parameter of type 'string | Error | undefined'.
481
+ reject(123);
482
+ });
483
+ });
484
+ });
485
+
486
+ describe('job.off', () => {
487
+ test('accepts valid handler', () => {
488
+ eventBus.job.off(token, ({ resolve }) => resolve({ value: 1 }));
489
+ eventBus.job.off(token, ({ reject }) => reject(new Error('Oops!')));
490
+ eventBus.job.off(token, ({ reject }) => reject('Oops!'));
491
+ });
492
+
493
+ test('rejects invalid handler', () => {
494
+ eventBus.job.off(token, ({ resolve }) => {
495
+ // @ts-expect-error Type 'string' is not assignable to type 'number'.ts(2322)
496
+ resolve({ value: '' });
497
+ });
498
+ eventBus.job.off(token, ({ reject }) => {
499
+ // @ts-expect-error Argument of type '123' is not assignable to parameter of type 'string | Error | undefined'.
500
+ reject(123);
501
+ });
502
+ });
503
+ });
504
+ });
505
+ });
506
+ });
507
+ });
@@ -1,6 +1,5 @@
1
1
  import * as mitt from 'mitt';
2
- import { renderHook } from '@testing-library/react';
3
- import { useEventBus, EventBus } from '../event-bus';
2
+ import { EventBus, typedEventBusToken, EVENT_BUS_TOKEN } from '../event-bus';
4
3
 
5
4
  jest.mock('mitt', () => jest.fn());
6
5
 
@@ -126,22 +125,10 @@ describe(`[web-components] ${EventBus.name}`, () => {
126
125
  });
127
126
  });
128
127
 
129
- describe(`[web-components] ${useEventBus.name}`, () => {
130
- function subject() {
131
- return renderHook(() => useEventBus());
132
- }
128
+ describe(`[web-components] ${typedEventBusToken.name}`, () => {
129
+ const subject = () => typedEventBusToken<any>();
133
130
 
134
- test('provides an event bus instance', () => {
135
- expect(subject().result.current).toBeInstanceOf(EventBus);
136
- });
137
-
138
- test('when the component using useEventBus is re-rendered, useEventBus provides the same event bus instance', () => {
139
- const { result, rerender } = subject();
140
-
141
- const firstInstance = result.current;
142
- rerender();
143
-
144
- const secondInstance = result.current;
145
- expect(firstInstance).toBe(secondInstance);
131
+ test('returns EVENT_BUS_TOKEN', () => {
132
+ expect(subject()).toBe(EVENT_BUS_TOKEN);
146
133
  });
147
134
  });
@@ -6,14 +6,7 @@ import {
6
6
 
7
7
  describe(`[web-components] ${getValueForEnvironment.name}`, () => {
8
8
  const originalLocation = window.location;
9
- const values: EnvironmentValues = {
10
- dev: 'dev-value',
11
- go: 'go-value',
12
- next: 'next-value',
13
- qa: 'qa-value',
14
- stage: 'stage-value',
15
- test: 'test-value',
16
- };
9
+ let values: EnvironmentValues & { [key: string]: string };
17
10
  let hostname: string | undefined;
18
11
  let defaultEnvironment: Environment | undefined;
19
12
 
@@ -33,14 +26,19 @@ describe(`[web-components] ${getValueForEnvironment.name}`, () => {
33
26
  });
34
27
 
35
28
  beforeEach(() => {
29
+ values = { dev: 'dev-value', qa: 'qa-value', test: 'test-value' };
36
30
  hostname = undefined;
37
31
  defaultEnvironment = undefined;
38
32
  process.env.NODE_ENV = 'production';
39
33
  });
40
34
 
41
- test('returns id for "qa" environment', () => {
42
- expect(subject()).toBe(values.qa);
43
- });
35
+ function itReturnsQAEnvironment() {
36
+ test('returns id for "qa" environment', () => {
37
+ expect(subject()).toBe(values.qa);
38
+ });
39
+ }
40
+
41
+ itReturnsQAEnvironment();
44
42
 
45
43
  describe('when in test environment', () => {
46
44
  beforeEach(() => (process.env.NODE_ENV = 'test'));
@@ -58,20 +56,40 @@ describe(`[web-components] ${getValueForEnvironment.name}`, () => {
58
56
  });
59
57
  });
60
58
 
61
- describe.each([
62
- { host: '127.0.0.1', key: 'dev' },
63
- { host: 'localhost', key: 'dev' },
64
- { host: 'go.servicetitan.com', key: 'go' },
65
- { host: 'next.servicetitan.com', key: 'next' },
66
- { host: 'stage.servicetitan.com', key: 'stage' },
67
- ] as {
68
- host: string;
69
- key: Environment;
70
- }[])('when hostname contains "$host"', ({ host, key }) => {
59
+ describe.each(['127.0.0.1', 'localhost'])('when hostname contains "%s"', host => {
71
60
  beforeEach(() => (hostname = host));
72
61
 
73
- test(`returns id for "${key}" environment`, () => {
62
+ test('returns id for "dev" environment', () => {
63
+ expect(subject()).toBe(values.dev);
64
+ });
65
+ });
66
+
67
+ describe('when hostname contains "{key}.servicetitan.com"', () => {
68
+ const key = 'foo-bar';
69
+
70
+ beforeEach(() => {
71
+ hostname = `${key}.servicetitan.com`;
72
+ values = { [key]: 'key-value' };
73
+ });
74
+
75
+ test('returns key value', () => {
74
76
  expect(subject()).toBe(values[key]);
75
77
  });
76
78
  });
79
+
80
+ describe('when hostname contains ".st.dev"', () => {
81
+ beforeEach(() => (hostname = 'foo-bar-stage.st.dev'));
82
+
83
+ itReturnsQAEnvironment();
84
+
85
+ describe('when key matches ".st.dev" domain', () => {
86
+ const key = 'stage.st.dev';
87
+
88
+ beforeEach(() => (values = { [key]: 'key-value' }));
89
+
90
+ test('returns key value', () => {
91
+ expect(subject()).toBe(values[key]);
92
+ });
93
+ });
94
+ });
77
95
  });
@@ -3,18 +3,9 @@
3
3
  */
4
4
 
5
5
  import { createContext, useContext } from 'react';
6
- import { JSON, JSONPrimitive } from '../types';
6
+ import { JSON, Serializable } from '../types';
7
7
 
8
- // Type complexity below is required to support Interfaces being used
9
- export type MFEContextData<T> = {
10
- [P in keyof T]: T[P] extends JSONPrimitive
11
- ? T[P]
12
- : T[P] extends (infer U)[]
13
- ? MFEContextData<U>[]
14
- : T[P] extends JSON
15
- ? MFEContextData<T[P]>
16
- : never;
17
- };
8
+ export type MFEContextData<T> = Serializable<T>;
18
9
 
19
10
  export const MFEDataContext = createContext<JSON>({});
20
11
 
@@ -7,7 +7,7 @@ describe(`[web-components] ${useMFEDataContext.name}`, () => {
7
7
 
8
8
  describe('when the MFEDataContext is provided by a parent', () => {
9
9
  const data = { foo: 'bar' };
10
- const Wrapper: FC<PropsWithChildren<{}>> = ({ children }) => {
10
+ const Wrapper: FC<PropsWithChildren> = ({ children }) => {
11
11
  return <MFEDataContext.Provider value={data}>{children}</MFEDataContext.Provider>;
12
12
  };
13
13
 
@@ -17,7 +17,7 @@ describe(`[web-components] ${useMFEDataContext.name}`, () => {
17
17
  });
18
18
 
19
19
  describe('when the MFEDataContext is not provided by a parent', () => {
20
- const Wrapper: FC<PropsWithChildren<{}>> = ({ children }) => {
20
+ const Wrapper: FC<PropsWithChildren> = ({ children }) => {
21
21
  return <div>{children}</div>;
22
22
  };
23
23