@servicetitan/journey 2.0.0 → 2.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 (150) hide show
  1. package/dist/__test-utils__/test-runtime.d.ts.map +1 -0
  2. package/dist/__test-utils__/test-runtime.js +47 -0
  3. package/dist/__test-utils__/test-runtime.js.map +1 -0
  4. package/dist/__tests__/core/ambient.test.d.ts.map +1 -0
  5. package/dist/__tests__/core/api.test.d.ts +2 -0
  6. package/dist/__tests__/core/api.test.d.ts.map +1 -0
  7. package/dist/__tests__/core/define-journey.test.d.ts.map +1 -0
  8. package/dist/__tests__/core/requests.test.d.ts.map +1 -0
  9. package/dist/__tests__/core/runtime.test.d.ts.map +1 -0
  10. package/dist/__tests__/core/step.test.d.ts.map +1 -0
  11. package/dist/__tests__/core/timeouts.test.d.ts.map +1 -0
  12. package/dist/__tests__/global.test.d.ts +3 -0
  13. package/dist/__tests__/global.test.d.ts.map +1 -0
  14. package/dist/__tests__/integrations/axios.test.d.ts.map +1 -0
  15. package/dist/__tests__/integrations/context.test.d.ts.map +1 -0
  16. package/dist/__tests__/integrations/expose-app-journey.test.d.ts.map +1 -0
  17. package/dist/__tests__/integrations/fetch.test.d.ts.map +1 -0
  18. package/dist/__tests__/integrations/jquery.test.d.ts.map +1 -0
  19. package/dist/__tests__/integrations/request-key.test.d.ts.map +1 -0
  20. package/dist/__tests__/integrations/scope.test.d.ts.map +1 -0
  21. package/dist/__tests__/sinks/console.test.d.ts +2 -0
  22. package/dist/__tests__/sinks/console.test.d.ts.map +1 -0
  23. package/dist/__tests__/sinks/datadog.test.d.ts.map +1 -0
  24. package/dist/__tests__/sinks/sinks.test.d.ts.map +1 -0
  25. package/dist/config.d.ts +8 -4
  26. package/dist/config.d.ts.map +1 -1
  27. package/dist/config.js +19 -13
  28. package/dist/config.js.map +1 -1
  29. package/dist/core/{registry.d.ts → api.d.ts} +2 -22
  30. package/dist/core/api.d.ts.map +1 -0
  31. package/dist/core/{registry.js → api.js} +13 -40
  32. package/dist/core/api.js.map +1 -0
  33. package/dist/core/endpoint-policy.d.ts.map +1 -0
  34. package/dist/core/endpoint-policy.js.map +1 -0
  35. package/dist/core/index.d.ts +4 -4
  36. package/dist/core/index.d.ts.map +1 -1
  37. package/dist/core/index.js +3 -2
  38. package/dist/core/index.js.map +1 -1
  39. package/dist/core/runtime.d.ts +35 -7
  40. package/dist/core/runtime.d.ts.map +1 -1
  41. package/dist/core/runtime.js +39 -5
  42. package/dist/core/runtime.js.map +1 -1
  43. package/dist/core/step-tag.d.ts +6 -0
  44. package/dist/core/step-tag.d.ts.map +1 -1
  45. package/dist/core/step-tag.js +7 -0
  46. package/dist/core/step-tag.js.map +1 -1
  47. package/dist/core/types.d.ts +3 -1
  48. package/dist/core/types.d.ts.map +1 -1
  49. package/dist/core/types.js.map +1 -1
  50. package/dist/global.d.ts +67 -0
  51. package/dist/global.d.ts.map +1 -0
  52. package/dist/global.js +116 -0
  53. package/dist/global.js.map +1 -0
  54. package/dist/index.d.ts +8 -3
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +11 -5
  57. package/dist/index.js.map +1 -1
  58. package/dist/integrations/fetch.d.ts +3 -2
  59. package/dist/integrations/fetch.d.ts.map +1 -1
  60. package/dist/integrations/fetch.js.map +1 -1
  61. package/dist/integrations/jquery/index.d.ts +2 -2
  62. package/dist/integrations/jquery/index.d.ts.map +1 -1
  63. package/dist/integrations/jquery/index.js +3 -2
  64. package/dist/integrations/jquery/index.js.map +1 -1
  65. package/dist/integrations/react/scope.d.ts.map +1 -1
  66. package/dist/integrations/react/scope.js +3 -2
  67. package/dist/integrations/react/scope.js.map +1 -1
  68. package/dist/sinks/console.d.ts +11 -0
  69. package/dist/sinks/console.d.ts.map +1 -0
  70. package/dist/sinks/console.js +18 -0
  71. package/dist/sinks/console.js.map +1 -0
  72. package/dist/sinks/datadog.d.ts +12 -1
  73. package/dist/sinks/datadog.d.ts.map +1 -1
  74. package/dist/sinks/datadog.js +28 -7
  75. package/dist/sinks/datadog.js.map +1 -1
  76. package/package.json +2 -2
  77. package/src/{__mocks__ → __test-utils__}/test-runtime.ts +6 -2
  78. package/src/__tests__/config.test.ts +9 -12
  79. package/src/__tests__/{ambient.test.ts → core/ambient.test.ts} +8 -7
  80. package/src/__tests__/{registry.test.ts → core/api.test.ts} +30 -25
  81. package/src/__tests__/{define-journey.test.ts → core/define-journey.test.ts} +5 -4
  82. package/src/__tests__/{requests.test.ts → core/requests.test.ts} +12 -8
  83. package/src/__tests__/{runtime.test.ts → core/runtime.test.ts} +18 -18
  84. package/src/__tests__/{step.test.ts → core/step.test.ts} +16 -18
  85. package/src/__tests__/{timeouts.test.ts → core/timeouts.test.ts} +15 -10
  86. package/src/__tests__/global.test.ts +113 -0
  87. package/src/__tests__/{axios.test.ts → integrations/axios.test.ts} +46 -15
  88. package/src/__tests__/{context.test.tsx → integrations/context.test.tsx} +1 -1
  89. package/src/__tests__/{expose-app-journey.test.ts → integrations/expose-app-journey.test.ts} +2 -2
  90. package/src/__tests__/{fetch.test.ts → integrations/fetch.test.ts} +6 -6
  91. package/src/__tests__/{jquery.test.ts → integrations/jquery.test.ts} +49 -12
  92. package/src/__tests__/{request-key.test.ts → integrations/request-key.test.ts} +1 -1
  93. package/src/__tests__/{scope.test.tsx → integrations/scope.test.tsx} +4 -4
  94. package/src/__tests__/sinks/console.test.ts +98 -0
  95. package/src/__tests__/{datadog.test.ts → sinks/datadog.test.ts} +62 -5
  96. package/src/__tests__/{sinks.test.ts → sinks/sinks.test.ts} +13 -12
  97. package/src/config.ts +20 -12
  98. package/src/core/{registry.ts → api.ts} +12 -65
  99. package/src/core/index.ts +5 -14
  100. package/src/core/runtime.ts +56 -7
  101. package/src/core/step-tag.ts +9 -0
  102. package/src/core/types.ts +3 -1
  103. package/src/global.ts +196 -0
  104. package/src/index.ts +20 -7
  105. package/src/integrations/fetch.ts +2 -2
  106. package/src/integrations/jquery/index.ts +4 -9
  107. package/src/integrations/react/scope.tsx +3 -2
  108. package/src/sinks/console.ts +20 -0
  109. package/src/sinks/datadog.ts +31 -6
  110. package/dist/__mocks__/test-runtime.d.ts.map +0 -1
  111. package/dist/__tests__/ambient.test.d.ts.map +0 -1
  112. package/dist/__tests__/axios.test.d.ts.map +0 -1
  113. package/dist/__tests__/context.test.d.ts.map +0 -1
  114. package/dist/__tests__/datadog.test.d.ts.map +0 -1
  115. package/dist/__tests__/define-journey.test.d.ts.map +0 -1
  116. package/dist/__tests__/expose-app-journey.test.d.ts.map +0 -1
  117. package/dist/__tests__/fetch.test.d.ts.map +0 -1
  118. package/dist/__tests__/jquery.test.d.ts.map +0 -1
  119. package/dist/__tests__/registry.test.d.ts +0 -2
  120. package/dist/__tests__/registry.test.d.ts.map +0 -1
  121. package/dist/__tests__/request-key.test.d.ts.map +0 -1
  122. package/dist/__tests__/requests.test.d.ts.map +0 -1
  123. package/dist/__tests__/runtime.test.d.ts.map +0 -1
  124. package/dist/__tests__/scope.test.d.ts.map +0 -1
  125. package/dist/__tests__/sinks.test.d.ts.map +0 -1
  126. package/dist/__tests__/step.test.d.ts.map +0 -1
  127. package/dist/__tests__/timeouts.test.d.ts.map +0 -1
  128. package/dist/core/registry.d.ts.map +0 -1
  129. package/dist/core/registry.js.map +0 -1
  130. package/dist/endpoint-policy.d.ts.map +0 -1
  131. package/dist/endpoint-policy.js.map +0 -1
  132. /package/dist/{__mocks__ → __test-utils__}/test-runtime.d.ts +0 -0
  133. /package/dist/__tests__/{ambient.test.d.ts → core/ambient.test.d.ts} +0 -0
  134. /package/dist/__tests__/{define-journey.test.d.ts → core/define-journey.test.d.ts} +0 -0
  135. /package/dist/__tests__/{requests.test.d.ts → core/requests.test.d.ts} +0 -0
  136. /package/dist/__tests__/{runtime.test.d.ts → core/runtime.test.d.ts} +0 -0
  137. /package/dist/__tests__/{step.test.d.ts → core/step.test.d.ts} +0 -0
  138. /package/dist/__tests__/{timeouts.test.d.ts → core/timeouts.test.d.ts} +0 -0
  139. /package/dist/__tests__/{axios.test.d.ts → integrations/axios.test.d.ts} +0 -0
  140. /package/dist/__tests__/{context.test.d.ts → integrations/context.test.d.ts} +0 -0
  141. /package/dist/__tests__/{expose-app-journey.test.d.ts → integrations/expose-app-journey.test.d.ts} +0 -0
  142. /package/dist/__tests__/{fetch.test.d.ts → integrations/fetch.test.d.ts} +0 -0
  143. /package/dist/__tests__/{jquery.test.d.ts → integrations/jquery.test.d.ts} +0 -0
  144. /package/dist/__tests__/{request-key.test.d.ts → integrations/request-key.test.d.ts} +0 -0
  145. /package/dist/__tests__/{scope.test.d.ts → integrations/scope.test.d.ts} +0 -0
  146. /package/dist/__tests__/{datadog.test.d.ts → sinks/datadog.test.d.ts} +0 -0
  147. /package/dist/__tests__/{sinks.test.d.ts → sinks/sinks.test.d.ts} +0 -0
  148. /package/dist/{endpoint-policy.d.ts → core/endpoint-policy.d.ts} +0 -0
  149. /package/dist/{endpoint-policy.js → core/endpoint-policy.js} +0 -0
  150. /package/src/{endpoint-policy.ts → core/endpoint-policy.ts} +0 -0
@@ -1,26 +1,29 @@
1
1
  /** @jest-environment jsdom */
2
- import { baseJourneyDef, lastJourney, useJourneyTestRuntime } from '../__mocks__/test-runtime';
2
+ import {
3
+ baseJourneyDef,
4
+ lastJourney,
5
+ useJourneyTestRuntime,
6
+ } from '../../__test-utils__/test-runtime';
3
7
  import {
4
8
  completeJourney,
5
9
  excludeJourney,
6
10
  failJourney,
7
- firstOpenJourney,
8
11
  journeyStep,
9
12
  setJourneyTags,
10
- startJourney,
11
- } from '../core';
13
+ } from '../../core';
14
+ import { moduleRuntime } from '../../core/runtime';
12
15
 
13
16
  const emitMock = jest.fn();
14
- const baseDef = { ...baseJourneyDef, name: 'registry-api' };
15
- const otherDef = { ...baseJourneyDef, name: 'registry-other' };
17
+ const baseDef = { ...baseJourneyDef, name: 'api-lifecycle' };
18
+ const otherDef = { ...baseJourneyDef, name: 'api-other' };
16
19
 
17
- describe('[journey] registry', () => {
20
+ describe('[journey] api', () => {
18
21
  useJourneyTestRuntime(emitMock);
19
22
 
20
23
  describe('firstOpenJourney', () => {
21
24
  const names = [baseDef.name, otherDef.name];
22
25
 
23
- const subject = () => firstOpenJourney(names);
26
+ const subject = () => moduleRuntime.firstOpenJourney(names);
24
27
 
25
28
  describe('when none are open', () => {
26
29
  test('returns null', () => {
@@ -30,7 +33,7 @@ describe('[journey] registry', () => {
30
33
 
31
34
  describe('when only the second name is open', () => {
32
35
  beforeEach(() => {
33
- startJourney(otherDef);
36
+ moduleRuntime.startJourney(otherDef);
34
37
  });
35
38
 
36
39
  test('returns that name', () => {
@@ -40,13 +43,15 @@ describe('[journey] registry', () => {
40
43
 
41
44
  describe('when both are open', () => {
42
45
  beforeEach(() => {
43
- startJourney(otherDef);
44
- startJourney(baseDef);
46
+ moduleRuntime.startJourney(otherDef);
47
+ moduleRuntime.startJourney(baseDef);
45
48
  });
46
49
 
47
50
  test('returns the first match in order', () => {
48
51
  expect(subject()).toBe(baseDef.name);
49
- expect(firstOpenJourney([otherDef.name, baseDef.name])).toBe(otherDef.name);
52
+ expect(moduleRuntime.firstOpenJourney([otherDef.name, baseDef.name])).toBe(
53
+ otherDef.name
54
+ );
50
55
  });
51
56
  });
52
57
  });
@@ -54,7 +59,7 @@ describe('[journey] registry', () => {
54
59
  describe('completeJourney', () => {
55
60
  describe('with tags', () => {
56
61
  beforeEach(() => {
57
- startJourney(baseDef);
62
+ moduleRuntime.startJourney(baseDef);
58
63
  });
59
64
 
60
65
  const subject = () => completeJourney(baseDef.name, { 'lead.id': 7 });
@@ -78,7 +83,7 @@ describe('[journey] registry', () => {
78
83
 
79
84
  describe('when already closed', () => {
80
85
  beforeEach(async () => {
81
- startJourney(baseDef);
86
+ moduleRuntime.startJourney(baseDef);
82
87
  await journeyStep(baseDef.name, 'do-work', step => {
83
88
  step.complete();
84
89
  });
@@ -95,7 +100,7 @@ describe('[journey] registry', () => {
95
100
 
96
101
  describe('after a fail', () => {
97
102
  beforeEach(async () => {
98
- startJourney(baseDef);
103
+ moduleRuntime.startJourney(baseDef);
99
104
  await journeyStep(baseDef.name, 'do-work', step => {
100
105
  step.fail('validation');
101
106
  });
@@ -115,10 +120,10 @@ describe('[journey] registry', () => {
115
120
  describe('startJourney', () => {
116
121
  describe('when restarting the same name', () => {
117
122
  beforeEach(() => {
118
- startJourney(baseDef);
123
+ moduleRuntime.startJourney(baseDef);
119
124
  });
120
125
 
121
- const subject = () => startJourney(baseDef);
126
+ const subject = () => moduleRuntime.startJourney(baseDef);
122
127
 
123
128
  test('excludes the prior open journey', () => {
124
129
  subject();
@@ -129,13 +134,13 @@ describe('[journey] registry', () => {
129
134
 
130
135
  describe('when restarting after a bad close', () => {
131
136
  beforeEach(async () => {
132
- startJourney(baseDef);
137
+ moduleRuntime.startJourney(baseDef);
133
138
  await journeyStep(baseDef.name, 'do-work', step => {
134
139
  step.fail('validation');
135
140
  });
136
141
  });
137
142
 
138
- const subject = () => startJourney(baseDef);
143
+ const subject = () => moduleRuntime.startJourney(baseDef);
139
144
 
140
145
  test('opens fresh without a second exclude', () => {
141
146
  subject();
@@ -148,7 +153,7 @@ describe('[journey] registry', () => {
148
153
 
149
154
  describe('with static def tags', () => {
150
155
  const subject = () => {
151
- startJourney({ ...baseDef, tags: { plan: 'pro' } });
156
+ moduleRuntime.startJourney({ ...baseDef, tags: { plan: 'pro' } });
152
157
  completeJourney(baseDef.name, { 'job.id': 5 });
153
158
  };
154
159
 
@@ -162,7 +167,7 @@ describe('[journey] registry', () => {
162
167
 
163
168
  describe('failJourney', () => {
164
169
  beforeEach(() => {
165
- startJourney(baseDef);
170
+ moduleRuntime.startJourney(baseDef);
166
171
  });
167
172
 
168
173
  const subject = () => failJourney(baseDef.name, 'manual-bad');
@@ -188,7 +193,7 @@ describe('[journey] registry', () => {
188
193
  describe('excludeJourney', () => {
189
194
  describe('with a clean journey', () => {
190
195
  beforeEach(() => {
191
- startJourney(otherDef);
196
+ moduleRuntime.startJourney(otherDef);
192
197
  });
193
198
 
194
199
  const subject = () => excludeJourney(otherDef.name);
@@ -202,7 +207,7 @@ describe('[journey] registry', () => {
202
207
 
203
208
  describe('after a breach', () => {
204
209
  beforeEach(async () => {
205
- startJourney(baseDef);
210
+ moduleRuntime.startJourney(baseDef);
206
211
  await journeyStep(baseDef.name, 'do-work', step => {
207
212
  step.fail('validation');
208
213
  });
@@ -221,7 +226,7 @@ describe('[journey] registry', () => {
221
226
  describe('when past the journey budget', () => {
222
227
  beforeEach(() => {
223
228
  jest.spyOn(globalThis.performance, 'now').mockReturnValue(1_000);
224
- startJourney({ ...baseDef, timeoutMs: 50 });
229
+ moduleRuntime.startJourney({ ...baseDef, timeoutMs: 50 });
225
230
  (globalThis.performance.now as jest.Mock).mockReturnValue(1_100);
226
231
  });
227
232
 
@@ -251,7 +256,7 @@ describe('[journey] registry', () => {
251
256
 
252
257
  describe('with a closed journey', () => {
253
258
  beforeEach(() => {
254
- startJourney(baseDef);
259
+ moduleRuntime.startJourney(baseDef);
255
260
  completeJourney(baseDef.name);
256
261
  });
257
262
 
@@ -1,6 +1,7 @@
1
1
  /** @jest-environment jsdom */
2
- import { lastJourney, useJourneyTestRuntime } from '../__mocks__/test-runtime';
3
- import { defineJourney, journeyStep, startJourney } from '../core';
2
+ import { lastJourney, useJourneyTestRuntime } from '../../__test-utils__/test-runtime';
3
+ import { defineJourney, journeyStep } from '../../core';
4
+ import { moduleRuntime } from '../../core/runtime';
4
5
 
5
6
  const emitMock = jest.fn();
6
7
 
@@ -51,7 +52,7 @@ describe('[journey] defineJourney', () => {
51
52
 
52
53
  describe('when a JourneyScope already opened the journey', () => {
53
54
  beforeEach(() => {
54
- startJourney(base);
55
+ moduleRuntime.startJourney(base);
55
56
  });
56
57
 
57
58
  const subject = () =>
@@ -147,7 +148,7 @@ describe('[journey] defineJourney', () => {
147
148
 
148
149
  describe('when mountStep runs under an already-open journey', () => {
149
150
  beforeEach(() => {
150
- startJourney(base);
151
+ moduleRuntime.startJourney(base);
151
152
  });
152
153
 
153
154
  const subject = () =>
@@ -1,15 +1,19 @@
1
1
  /** @jest-environment jsdom */
2
- import { baseJourneyDef, lastJourney, useJourneyTestRuntime } from '../__mocks__/test-runtime';
3
- import { configureJourney } from '../config';
2
+ import {
3
+ baseJourneyDef,
4
+ lastJourney,
5
+ useJourneyTestRuntime,
6
+ } from '../../__test-utils__/test-runtime';
7
+ import { configureJourney } from '../../config';
4
8
  import {
5
9
  completeJourney,
6
10
  journeyStep,
7
11
  reportBackendRequest,
8
12
  resolveJourneyStep,
9
- startJourney,
10
13
  type JourneyDef,
11
14
  type JourneyStepRef,
12
- } from '../core';
15
+ } from '../../core';
16
+ import { moduleRuntime } from '../../core/runtime';
13
17
 
14
18
  const emitMock = jest.fn();
15
19
  const baseDef = { ...baseJourneyDef, name: 'requests-journey' };
@@ -35,7 +39,7 @@ describe('[journey] reportBackendRequest', () => {
35
39
  run: (ref: JourneyStepRef) => void | Promise<void>,
36
40
  def: JourneyDef = { ...baseDef, requestTimeoutMs: 5_000 }
37
41
  ) {
38
- startJourney(def);
42
+ moduleRuntime.startJourney(def);
39
43
  await journeyStep(baseDef.name, 'do-work', async step => {
40
44
  stepRef = resolveJourneyStep((step.tag() as { journeyStep: unknown }).journeyStep);
41
45
  await run(stepRef!);
@@ -168,7 +172,7 @@ describe('[journey] reportBackendRequest', () => {
168
172
 
169
173
  describe('when a journey endpoint timeoutMs is 0', () => {
170
174
  test('falls through to the journey requestTimeoutMs', async () => {
171
- startJourney({
175
+ moduleRuntime.startJourney({
172
176
  ...baseDef,
173
177
  requestTimeoutMs: 30,
174
178
  endpoints: [{ match: '/special', timeoutMs: 0 }],
@@ -281,7 +285,7 @@ describe('[journey] reportBackendRequest', () => {
281
285
  });
282
286
 
283
287
  test('falls through to defaultMs', async () => {
284
- startJourney(baseDef);
288
+ moduleRuntime.startJourney(baseDef);
285
289
 
286
290
  await journeyStep(baseDef.name, 'do-work', step => {
287
291
  stepRef = resolveJourneyStep((step.tag() as { journeyStep: unknown }).journeyStep);
@@ -301,7 +305,7 @@ describe('[journey] reportBackendRequest', () => {
301
305
  });
302
306
 
303
307
  test('uses the app defaultMs', async () => {
304
- startJourney(baseDef);
308
+ moduleRuntime.startJourney(baseDef);
305
309
 
306
310
  await journeyStep(baseDef.name, 'do-work', step => {
307
311
  stepRef = resolveJourneyStep((step.tag() as { journeyStep: unknown }).journeyStep);
@@ -1,18 +1,18 @@
1
1
  /** @jest-environment jsdom */
2
- import { lastJourney, useJourneyTestRuntime } from '../__mocks__/test-runtime';
3
- import { configureJourney, getRequestTimeouts } from '../config';
2
+ import { lastJourney, useJourneyTestRuntime } from '../../__test-utils__/test-runtime';
3
+ import { configureJourney, getRequestTimeouts } from '../../config';
4
4
  import {
5
5
  activeJourneyStep,
6
6
  completeJourney,
7
- createJourneyRuntime,
8
7
  defineJourney,
9
8
  journeyStep,
10
9
  resetJourney,
11
- startJourney,
12
- } from '../core';
13
- import { sendToDatadog } from '../sinks/datadog';
10
+ } from '../../core';
11
+ import { createJourneyRuntime, moduleRuntime } from '../../core/runtime';
12
+ import { sendToConsole } from '../../sinks/console';
13
+ import { sendToDatadog } from '../../sinks/datadog';
14
14
 
15
- jest.mock('../sinks/datadog', () => ({
15
+ jest.mock('../../sinks/datadog', () => ({
16
16
  sendToDatadog: jest.fn(),
17
17
  }));
18
18
 
@@ -31,7 +31,7 @@ describe('[journey] resetJourney', () => {
31
31
 
32
32
  describe('when a journey is open', () => {
33
33
  beforeEach(() => {
34
- startJourney(def);
34
+ moduleRuntime.startJourney(def);
35
35
  });
36
36
 
37
37
  const subject = () => resetJourney({ sinks: [emitMock] });
@@ -82,7 +82,7 @@ describe('[journey] resetJourney', () => {
82
82
  beforeEach(() => {
83
83
  resetJourney({ sinks: [emitMock] });
84
84
  emitMock.mockClear();
85
- startJourney(def);
85
+ moduleRuntime.startJourney(def);
86
86
  completeJourney(def.name);
87
87
  emitMock.mockClear();
88
88
  });
@@ -91,7 +91,7 @@ describe('[journey] resetJourney', () => {
91
91
 
92
92
  test('leaves sinks empty', () => {
93
93
  subject();
94
- startJourney(def);
94
+ moduleRuntime.startJourney(def);
95
95
  completeJourney(def.name);
96
96
 
97
97
  expect(emitMock).not.toHaveBeenCalled();
@@ -103,7 +103,7 @@ describe('[journey] resetJourney', () => {
103
103
  let stepPromise: Promise<unknown>;
104
104
 
105
105
  beforeEach(() => {
106
- startJourney(def);
106
+ moduleRuntime.startJourney(def);
107
107
  const hang = new Promise<void>(resolve => {
108
108
  resolveWork = resolve;
109
109
  });
@@ -131,7 +131,7 @@ describe('[journey] resetJourney', () => {
131
131
  describe('when a journey countdown is armed', () => {
132
132
  beforeEach(() => {
133
133
  jest.useFakeTimers();
134
- startJourney({ ...def, timeoutMs: 50 });
134
+ moduleRuntime.startJourney({ ...def, timeoutMs: 50 });
135
135
  });
136
136
 
137
137
  afterEach(() => {
@@ -168,7 +168,7 @@ describe('[journey] createJourneyRuntime', () => {
168
168
 
169
169
  afterEach(() => {
170
170
  resetJourney({
171
- sinks: [sendToDatadog],
171
+ sinks: [sendToDatadog, sendToConsole],
172
172
  requestTimeouts: { defaultMs: 4_000, endpoints: [] },
173
173
  });
174
174
  });
@@ -176,7 +176,7 @@ describe('[journey] createJourneyRuntime', () => {
176
176
  describe('when both runtimes open a journey', () => {
177
177
  beforeEach(() => {
178
178
  runtime.startJourney(def);
179
- startJourney({ ...def, name: 'default-side' });
179
+ moduleRuntime.startJourney({ ...def, name: 'default-side' });
180
180
  });
181
181
 
182
182
  const subject = () => {
@@ -201,7 +201,7 @@ describe('[journey] createJourneyRuntime', () => {
201
201
  configureJourney({ requestTimeouts: { defaultMs: 9_000, endpoints: [] } });
202
202
  });
203
203
 
204
- test('keeps timeouts separate from the default engine', () => {
204
+ test('keeps timeouts separate from the module engine', () => {
205
205
  expect(runtime.getRequestTimeouts().defaultMs).toBe(10);
206
206
  expect(getRequestTimeouts().defaultMs).toBe(9_000);
207
207
  });
@@ -210,12 +210,12 @@ describe('[journey] createJourneyRuntime', () => {
210
210
  describe('when the isolated runtime is reset', () => {
211
211
  beforeEach(() => {
212
212
  runtime.startJourney(def);
213
- startJourney({ ...def, name: 'default-side' });
213
+ moduleRuntime.startJourney({ ...def, name: 'default-side' });
214
214
  });
215
215
 
216
216
  const subject = () => runtime.reset({ sinks: [isolatedEmit] });
217
217
 
218
- test('does not clear the default engine', () => {
218
+ test('does not clear the module engine', () => {
219
219
  subject();
220
220
  completeJourney('default-side');
221
221
 
@@ -230,7 +230,7 @@ describe('[journey] createJourneyRuntime', () => {
230
230
  step.complete();
231
231
  });
232
232
 
233
- test('binds to the default runtime', async () => {
233
+ test('binds to the module runtime', async () => {
234
234
  await subject();
235
235
 
236
236
  expect(defaultEmit).toHaveBeenCalledTimes(1);
@@ -1,13 +1,11 @@
1
1
  /** @jest-environment jsdom */
2
- import { baseJourneyDef, lastJourney, useJourneyTestRuntime } from '../__mocks__/test-runtime';
3
2
  import {
4
- completeJourney,
5
- failJourney,
6
- journeyMountStep,
7
- journeyStep,
8
- resolveJourneyStep,
9
- startJourney,
10
- } from '../core';
3
+ baseJourneyDef,
4
+ lastJourney,
5
+ useJourneyTestRuntime,
6
+ } from '../../__test-utils__/test-runtime';
7
+ import { completeJourney, failJourney, journeyStep, resolveJourneyStep } from '../../core';
8
+ import { moduleRuntime } from '../../core/runtime';
11
9
 
12
10
  const emitMock = jest.fn();
13
11
  const baseDef = { ...baseJourneyDef, name: 'step-api' };
@@ -18,7 +16,7 @@ describe('[journey] journeyStep', () => {
18
16
 
19
17
  describe('with a candidate list', () => {
20
18
  beforeEach(() => {
21
- startJourney(baseDef);
19
+ moduleRuntime.startJourney(baseDef);
22
20
  });
23
21
 
24
22
  const subject = () => journeyStep(['missing', baseDef.name], 'do-work', step => step.name);
@@ -30,8 +28,8 @@ describe('[journey] journeyStep', () => {
30
28
 
31
29
  describe('with multiple open candidates', () => {
32
30
  beforeEach(() => {
33
- startJourney(otherDef);
34
- startJourney(baseDef);
31
+ moduleRuntime.startJourney(otherDef);
32
+ moduleRuntime.startJourney(baseDef);
35
33
  });
36
34
 
37
35
  const subject = () =>
@@ -67,7 +65,7 @@ describe('[journey] journeyStep', () => {
67
65
 
68
66
  describe('with an open journey', () => {
69
67
  beforeEach(() => {
70
- startJourney(baseDef);
68
+ moduleRuntime.startJourney(baseDef);
71
69
  });
72
70
 
73
71
  describe('when the step completes with tags', () => {
@@ -186,7 +184,7 @@ describe('[journey] journeyStep', () => {
186
184
  beforeEach(() => {
187
185
  // Parent already opened baseDef; restart with expected steps.
188
186
  emitMock.mockClear();
189
- startJourney({ ...baseDef, steps: ['load', 'save'] });
187
+ moduleRuntime.startJourney({ ...baseDef, steps: ['load', 'save'] });
190
188
  emitMock.mockClear();
191
189
  });
192
190
 
@@ -238,11 +236,11 @@ describe('[journey] journeyStep', () => {
238
236
  describe('journeyMountStep', () => {
239
237
  describe('with candidates', () => {
240
238
  beforeEach(() => {
241
- startJourney(baseDef);
239
+ moduleRuntime.startJourney(baseDef);
242
240
  });
243
241
 
244
242
  const subject = () =>
245
- journeyMountStep(['missing', baseDef.name], 'mount-load', step => {
243
+ moduleRuntime.journeyMountStep(['missing', baseDef.name], 'mount-load', step => {
246
244
  step.complete();
247
245
  return step.name;
248
246
  });
@@ -260,10 +258,10 @@ describe('[journey] journeyStep', () => {
260
258
  });
261
259
 
262
260
  test('defers until after a sync startJourney', async () => {
263
- const mount = journeyMountStep(baseDef.name, 'boot', () => {
261
+ const mount = moduleRuntime.journeyMountStep(baseDef.name, 'boot', () => {
264
262
  order.push('mount-step');
265
263
  });
266
- startJourney(baseDef);
264
+ moduleRuntime.startJourney(baseDef);
267
265
  order.push('started');
268
266
  await mount;
269
267
  completeJourney(baseDef.name);
@@ -276,7 +274,7 @@ describe('[journey] journeyStep', () => {
276
274
  describe('when fail-fast happens mid-step', () => {
277
275
  beforeEach(() => {
278
276
  jest.spyOn(globalThis.performance, 'now').mockReturnValue(1_000);
279
- startJourney(baseDef);
277
+ moduleRuntime.startJourney(baseDef);
280
278
  });
281
279
 
282
280
  afterEach(() => {
@@ -1,6 +1,11 @@
1
1
  /** @jest-environment jsdom */
2
- import { baseJourneyDef, lastJourney, useJourneyTestRuntime } from '../__mocks__/test-runtime';
3
- import { completeJourney, journeyStep, resolveJourneyStep, startJourney } from '../core';
2
+ import {
3
+ baseJourneyDef,
4
+ lastJourney,
5
+ useJourneyTestRuntime,
6
+ } from '../../__test-utils__/test-runtime';
7
+ import { completeJourney, journeyStep, resolveJourneyStep } from '../../core';
8
+ import { moduleRuntime } from '../../core/runtime';
4
9
 
5
10
  const emitMock = jest.fn();
6
11
  const baseDef = { ...baseJourneyDef, name: 'timeout-api' };
@@ -18,7 +23,7 @@ describe('[journey] timeouts', () => {
18
23
 
19
24
  describe('with a journey timeoutMs', () => {
20
25
  beforeEach(() => {
21
- startJourney({ ...baseDef, timeoutMs: 50 });
26
+ moduleRuntime.startJourney({ ...baseDef, timeoutMs: 50 });
22
27
  });
23
28
 
24
29
  const subject = () => jest.advanceTimersByTimeAsync(50);
@@ -33,7 +38,7 @@ describe('[journey] timeouts', () => {
33
38
 
34
39
  describe('with timeoutMs 0', () => {
35
40
  beforeEach(() => {
36
- startJourney({ ...baseDef, timeoutMs: 0 });
41
+ moduleRuntime.startJourney({ ...baseDef, timeoutMs: 0 });
37
42
  });
38
43
 
39
44
  const subject = () => jest.advanceTimersByTimeAsync(60_000);
@@ -52,7 +57,7 @@ describe('[journey] timeouts', () => {
52
57
  let stepPromise: Promise<unknown>;
53
58
 
54
59
  beforeEach(() => {
55
- startJourney({ ...baseDef, stepTimeoutMs: 40 });
60
+ moduleRuntime.startJourney({ ...baseDef, stepTimeoutMs: 40 });
56
61
  const hang = new Promise<void>(resolve => {
57
62
  resolveWork = resolve;
58
63
  });
@@ -81,7 +86,7 @@ describe('[journey] timeouts', () => {
81
86
  let stepPromise: Promise<unknown>;
82
87
 
83
88
  beforeEach(() => {
84
- startJourney({ ...baseDef, stepTimeoutMs: 5_000 });
89
+ moduleRuntime.startJourney({ ...baseDef, stepTimeoutMs: 5_000 });
85
90
  const hang = new Promise<void>(resolve => {
86
91
  resolveWork = resolve;
87
92
  });
@@ -114,7 +119,7 @@ describe('[journey] timeouts', () => {
114
119
  let stepPromise: Promise<unknown>;
115
120
 
116
121
  beforeEach(() => {
117
- startJourney({ ...baseDef, stepTimeoutMs: 40 });
122
+ moduleRuntime.startJourney({ ...baseDef, stepTimeoutMs: 40 });
118
123
  const hang = new Promise<void>(resolve => {
119
124
  resolveWork = resolve;
120
125
  });
@@ -144,7 +149,7 @@ describe('[journey] timeouts', () => {
144
149
  let stepPromise: Promise<unknown>;
145
150
 
146
151
  beforeEach(() => {
147
- startJourney({ ...baseDef, stepTimeoutMs: 40 });
152
+ moduleRuntime.startJourney({ ...baseDef, stepTimeoutMs: 40 });
148
153
  const hang = new Promise<void>(resolve => {
149
154
  resolveWork = resolve;
150
155
  });
@@ -176,7 +181,7 @@ describe('[journey] timeouts', () => {
176
181
  describe('when clearTimeout is bypassed after complete', () => {
177
182
  beforeEach(() => {
178
183
  jest.spyOn(globalThis, 'clearTimeout').mockImplementation(() => {});
179
- startJourney({ ...baseDef, timeoutMs: 50 });
184
+ moduleRuntime.startJourney({ ...baseDef, timeoutMs: 50 });
180
185
  completeJourney(baseDef.name);
181
186
  emitMock.mockClear();
182
187
  });
@@ -199,7 +204,7 @@ describe('[journey] timeouts', () => {
199
204
  let stepPromise: Promise<unknown>;
200
205
 
201
206
  beforeEach(() => {
202
- startJourney({ ...baseDef, stepTimeoutMs: 0 });
207
+ moduleRuntime.startJourney({ ...baseDef, stepTimeoutMs: 0 });
203
208
  const hang = new Promise<void>(resolve => {
204
209
  resolveWork = resolve;
205
210
  });
@@ -0,0 +1,113 @@
1
+ /** @jest-environment jsdom */
2
+ import '../config';
3
+ import { defineJourney, resetJourney } from '../core';
4
+ import {
5
+ JOURNEY_DEBUG_STORAGE_KEY,
6
+ JOURNEY_ENABLED_STORAGE_KEY,
7
+ ST_JOURNEY_GLOBAL_KEY,
8
+ disableJourney,
9
+ enableJourney,
10
+ isJourneyEnabled,
11
+ type StJourneyGlobal,
12
+ } from '../global';
13
+ import { setJourneyRum } from '../sinks/datadog';
14
+
15
+ function getStJourney(): StJourneyGlobal {
16
+ return (globalThis as Record<string, unknown>)[ST_JOURNEY_GLOBAL_KEY] as StJourneyGlobal;
17
+ }
18
+
19
+ describe('[journey] __stJourney global', () => {
20
+ beforeEach(() => {
21
+ localStorage.clear();
22
+ delete getStJourney().rum;
23
+ delete (globalThis as Record<string, unknown>).DD_RUM;
24
+ resetJourney({ sinks: [] });
25
+ });
26
+
27
+ afterEach(() => {
28
+ localStorage.clear();
29
+ delete getStJourney().rum;
30
+ delete (globalThis as Record<string, unknown>).DD_RUM;
31
+ resetJourney({ sinks: [] });
32
+ });
33
+
34
+ test('installs __stJourney on package load', () => {
35
+ expect(getStJourney()).toEqual(
36
+ expect.objectContaining({
37
+ enable: expect.any(Function),
38
+ disable: expect.any(Function),
39
+ isEnabled: expect.any(Function),
40
+ enableDebugging: expect.any(Function),
41
+ disableDebugging: expect.any(Function),
42
+ isDebugEnabled: expect.any(Function),
43
+ getDebugState: expect.any(Function),
44
+ })
45
+ );
46
+ });
47
+
48
+ test('enable / disable toggle force-enable localStorage', () => {
49
+ expect(isJourneyEnabled()).toBe(false);
50
+
51
+ enableJourney();
52
+ expect(localStorage.getItem(JOURNEY_ENABLED_STORAGE_KEY)).toBe('true');
53
+ expect(isJourneyEnabled()).toBe(true);
54
+ expect(getStJourney().isEnabled()).toBe(true);
55
+
56
+ disableJourney();
57
+ expect(localStorage.getItem(JOURNEY_ENABLED_STORAGE_KEY)).toBeNull();
58
+ expect(isJourneyEnabled()).toBe(false);
59
+ });
60
+
61
+ test('enableDebugging / disableDebugging toggle console-debug localStorage', () => {
62
+ const st = getStJourney();
63
+
64
+ st.enableDebugging();
65
+ expect(localStorage.getItem(JOURNEY_DEBUG_STORAGE_KEY)).toBe('true');
66
+ expect(st.isDebugEnabled()).toBe(true);
67
+
68
+ st.disableDebugging();
69
+ expect(localStorage.getItem(JOURNEY_DEBUG_STORAGE_KEY)).toBeNull();
70
+ expect(st.isDebugEnabled()).toBe(false);
71
+ });
72
+
73
+ test('getDebugState omits runtimes until debugging is enabled', () => {
74
+ enableJourney();
75
+ const journey = defineJourney({
76
+ name: 'Book Job',
77
+ team: 'jbce',
78
+ group: 'call-booking',
79
+ service: 'call-screen',
80
+ });
81
+ journey.start();
82
+
83
+ expect(getStJourney().getDebugState()).toEqual(
84
+ expect.objectContaining({
85
+ enabled: true,
86
+ debug: false,
87
+ runtimes: [],
88
+ })
89
+ );
90
+
91
+ getStJourney().enableDebugging();
92
+ const on = getStJourney().getDebugState();
93
+ expect(on.debug).toBe(true);
94
+ expect(on.runtimes.some(r => r.openJourneys.some(j => j.name === 'Book Job'))).toBe(true);
95
+
96
+ getStJourney().disableDebugging();
97
+ expect(getStJourney().getDebugState().runtimes).toEqual([]);
98
+ });
99
+
100
+ test('getDebugState.hasRum reflects DD_RUM or the fallback bridge', () => {
101
+ getStJourney().enableDebugging();
102
+
103
+ expect(getStJourney().getDebugState().hasRum).toBe(false);
104
+
105
+ (globalThis as Record<string, unknown>).DD_RUM = { addAction: jest.fn() };
106
+ expect(getStJourney().getDebugState().hasRum).toBe(true);
107
+
108
+ delete (globalThis as Record<string, unknown>).DD_RUM;
109
+ jest.spyOn(console, 'warn').mockImplementation(() => undefined);
110
+ setJourneyRum({ addAction: jest.fn() });
111
+ expect(getStJourney().getDebugState().hasRum).toBe(true);
112
+ });
113
+ });