@servicetitan/journey 2.0.0 → 3.0.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.
- package/README.md +1 -1
- package/dist/__test-utils__/test-runtime.d.ts.map +1 -0
- package/dist/__test-utils__/test-runtime.js +54 -0
- package/dist/__test-utils__/test-runtime.js.map +1 -0
- package/dist/__tests__/core/ambient.test.d.ts.map +1 -0
- package/dist/__tests__/core/api.test.d.ts +2 -0
- package/dist/__tests__/core/api.test.d.ts.map +1 -0
- package/dist/__tests__/core/define-journey.test.d.ts.map +1 -0
- package/dist/__tests__/core/requests.test.d.ts.map +1 -0
- package/dist/__tests__/core/runtime.test.d.ts.map +1 -0
- package/dist/__tests__/core/sanitize.test.d.ts +2 -0
- package/dist/__tests__/core/sanitize.test.d.ts.map +1 -0
- package/dist/__tests__/core/step.test.d.ts.map +1 -0
- package/dist/__tests__/core/timeouts.test.d.ts.map +1 -0
- package/dist/__tests__/global.test.d.ts +3 -0
- package/dist/__tests__/global.test.d.ts.map +1 -0
- package/dist/__tests__/hardening.test.d.ts +2 -0
- package/dist/__tests__/hardening.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/axios.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/context.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/expose-app-journey.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/fetch.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/host-mfe.test.d.ts +2 -0
- package/dist/__tests__/integrations/host-mfe.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/jquery.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/request-key.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/scope.test.d.ts.map +1 -0
- package/dist/__tests__/integrations/use-journey-step.test.d.ts +2 -0
- package/dist/__tests__/integrations/use-journey-step.test.d.ts.map +1 -0
- package/dist/__tests__/sinks/console.test.d.ts +2 -0
- package/dist/__tests__/sinks/console.test.d.ts.map +1 -0
- package/dist/__tests__/sinks/datadog.test.d.ts.map +1 -0
- package/dist/__tests__/sinks/sinks.test.d.ts.map +1 -0
- package/dist/config.d.ts +61 -13
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +98 -29
- package/dist/config.js.map +1 -1
- package/dist/core/abort-error.d.ts +4 -0
- package/dist/core/abort-error.d.ts.map +1 -0
- package/dist/core/abort-error.js +22 -0
- package/dist/core/abort-error.js.map +1 -0
- package/dist/core/api.d.ts +43 -0
- package/dist/core/api.d.ts.map +1 -0
- package/dist/core/api.js +122 -0
- package/dist/core/api.js.map +1 -0
- package/dist/{endpoint-policy.d.ts → core/endpoint-policy.d.ts} +14 -4
- package/dist/core/endpoint-policy.d.ts.map +1 -0
- package/dist/core/endpoint-policy.js +70 -0
- package/dist/core/endpoint-policy.js.map +1 -0
- package/dist/core/index.d.ts +6 -5
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +4 -3
- package/dist/core/index.js.map +1 -1
- package/dist/core/limits.d.ts +29 -0
- package/dist/core/limits.d.ts.map +1 -0
- package/dist/core/limits.js +63 -0
- package/dist/core/limits.js.map +1 -0
- package/dist/core/request-policy.d.ts +39 -0
- package/dist/core/request-policy.d.ts.map +1 -0
- package/dist/core/request-policy.js +18 -0
- package/dist/core/request-policy.js.map +1 -0
- package/dist/core/runtime.d.ts +95 -17
- package/dist/core/runtime.d.ts.map +1 -1
- package/dist/core/runtime.js +435 -124
- package/dist/core/runtime.js.map +1 -1
- package/dist/core/sanitize.d.ts +23 -0
- package/dist/core/sanitize.d.ts.map +1 -0
- package/dist/core/sanitize.js +143 -0
- package/dist/core/sanitize.js.map +1 -0
- package/dist/core/step-tag.d.ts +31 -6
- package/dist/core/step-tag.d.ts.map +1 -1
- package/dist/core/step-tag.js +62 -9
- package/dist/core/step-tag.js.map +1 -1
- package/dist/core/types.d.ts +76 -26
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +4 -1
- package/dist/core/types.js.map +1 -1
- package/dist/global.d.ts +145 -0
- package/dist/global.d.ts.map +1 -0
- package/dist/global.js +326 -0
- package/dist/global.js.map +1 -0
- package/dist/index.d.ts +9 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -5
- package/dist/index.js.map +1 -1
- package/dist/integrations/axios.d.ts +5 -1
- package/dist/integrations/axios.d.ts.map +1 -1
- package/dist/integrations/axios.js +146 -34
- package/dist/integrations/axios.js.map +1 -1
- package/dist/integrations/fetch.d.ts +6 -4
- package/dist/integrations/fetch.d.ts.map +1 -1
- package/dist/integrations/fetch.js +105 -26
- package/dist/integrations/fetch.js.map +1 -1
- package/dist/integrations/http-report.d.ts +27 -0
- package/dist/integrations/http-report.d.ts.map +1 -0
- package/dist/integrations/http-report.js +123 -0
- package/dist/integrations/http-report.js.map +1 -0
- package/dist/integrations/jquery/index.d.ts +10 -6
- package/dist/integrations/jquery/index.d.ts.map +1 -1
- package/dist/integrations/jquery/index.js +28 -9
- package/dist/integrations/jquery/index.js.map +1 -1
- package/dist/integrations/jquery/instrument.d.ts +14 -5
- package/dist/integrations/jquery/instrument.d.ts.map +1 -1
- package/dist/integrations/jquery/instrument.js +111 -29
- package/dist/integrations/jquery/instrument.js.map +1 -1
- package/dist/integrations/react/context.d.ts +4 -1
- package/dist/integrations/react/context.d.ts.map +1 -1
- package/dist/integrations/react/context.js +4 -1
- package/dist/integrations/react/context.js.map +1 -1
- package/dist/integrations/react/scope.d.ts +14 -3
- package/dist/integrations/react/scope.d.ts.map +1 -1
- package/dist/integrations/react/scope.js +48 -9
- package/dist/integrations/react/scope.js.map +1 -1
- package/dist/integrations/react/stable-config-keys.d.ts +6 -0
- package/dist/integrations/react/stable-config-keys.d.ts.map +1 -0
- package/dist/integrations/react/stable-config-keys.js +21 -0
- package/dist/integrations/react/stable-config-keys.js.map +1 -0
- package/dist/integrations/react/use-journey-step.d.ts +11 -0
- package/dist/integrations/react/use-journey-step.d.ts.map +1 -0
- package/dist/integrations/react/use-journey-step.js +19 -0
- package/dist/integrations/react/use-journey-step.js.map +1 -0
- package/dist/integrations/request-key.d.ts +8 -3
- package/dist/integrations/request-key.d.ts.map +1 -1
- package/dist/integrations/request-key.js +10 -9
- package/dist/integrations/request-key.js.map +1 -1
- package/dist/integrations/soft-skip.d.ts +7 -0
- package/dist/integrations/soft-skip.d.ts.map +1 -0
- package/dist/integrations/soft-skip.js +30 -0
- package/dist/integrations/soft-skip.js.map +1 -0
- package/dist/sinks/console.d.ts +11 -0
- package/dist/sinks/console.d.ts.map +1 -0
- package/dist/sinks/console.js +19 -0
- package/dist/sinks/console.js.map +1 -0
- package/dist/sinks/datadog.d.ts +12 -1
- package/dist/sinks/datadog.d.ts.map +1 -1
- package/dist/sinks/datadog.js +42 -25
- package/dist/sinks/datadog.js.map +1 -1
- package/package.json +2 -2
- package/src/{__mocks__ → __test-utils__}/test-runtime.ts +15 -4
- package/src/__tests__/config.test.ts +158 -42
- package/src/__tests__/{ambient.test.ts → core/ambient.test.ts} +23 -16
- package/src/__tests__/{registry.test.ts → core/api.test.ts} +35 -30
- package/src/__tests__/{define-journey.test.ts → core/define-journey.test.ts} +33 -15
- package/src/__tests__/{requests.test.ts → core/requests.test.ts} +61 -39
- package/src/__tests__/{runtime.test.ts → core/runtime.test.ts} +84 -37
- package/src/__tests__/core/sanitize.test.ts +69 -0
- package/src/__tests__/{step.test.ts → core/step.test.ts} +146 -52
- package/src/__tests__/{timeouts.test.ts → core/timeouts.test.ts} +83 -13
- package/src/__tests__/global.test.ts +154 -0
- package/src/__tests__/hardening.test.ts +613 -0
- package/src/__tests__/integrations/axios.test.ts +616 -0
- package/src/__tests__/{context.test.tsx → integrations/context.test.tsx} +1 -1
- package/src/__tests__/{expose-app-journey.test.ts → integrations/expose-app-journey.test.ts} +11 -5
- package/src/__tests__/{fetch.test.ts → integrations/fetch.test.ts} +159 -25
- package/src/__tests__/integrations/host-mfe.test.ts +548 -0
- package/src/__tests__/integrations/jquery.test.ts +539 -0
- package/src/__tests__/{request-key.test.ts → integrations/request-key.test.ts} +1 -1
- package/src/__tests__/integrations/scope.test.tsx +286 -0
- package/src/__tests__/integrations/use-journey-step.test.tsx +43 -0
- package/src/__tests__/sinks/console.test.ts +103 -0
- package/src/__tests__/{datadog.test.ts → sinks/datadog.test.ts} +109 -5
- package/src/__tests__/{sinks.test.ts → sinks/sinks.test.ts} +13 -12
- package/src/config.ts +173 -41
- package/src/core/abort-error.ts +24 -0
- package/src/core/api.ts +159 -0
- package/src/{endpoint-policy.ts → core/endpoint-policy.ts} +46 -4
- package/src/core/index.ts +16 -17
- package/src/core/limits.ts +73 -0
- package/src/core/request-policy.ts +66 -0
- package/src/core/runtime.ts +560 -137
- package/src/core/sanitize.ts +177 -0
- package/src/core/step-tag.ts +90 -11
- package/src/core/types.ts +82 -26
- package/src/global.ts +466 -0
- package/src/index.ts +31 -18
- package/src/integrations/axios.ts +183 -45
- package/src/integrations/fetch.ts +127 -41
- package/src/integrations/http-report.ts +184 -0
- package/src/integrations/jquery/index.ts +37 -18
- package/src/integrations/jquery/instrument.ts +126 -42
- package/src/integrations/react/context.ts +4 -1
- package/src/integrations/react/scope.tsx +61 -10
- package/src/integrations/react/stable-config-keys.ts +28 -0
- package/src/integrations/react/use-journey-step.ts +31 -0
- package/src/integrations/request-key.ts +18 -10
- package/src/integrations/soft-skip.ts +35 -0
- package/src/sinks/console.ts +21 -0
- package/src/sinks/datadog.ts +52 -33
- package/dist/__mocks__/test-runtime.d.ts.map +0 -1
- package/dist/__tests__/ambient.test.d.ts.map +0 -1
- package/dist/__tests__/axios.test.d.ts.map +0 -1
- package/dist/__tests__/context.test.d.ts.map +0 -1
- package/dist/__tests__/datadog.test.d.ts.map +0 -1
- package/dist/__tests__/define-journey.test.d.ts.map +0 -1
- package/dist/__tests__/expose-app-journey.test.d.ts.map +0 -1
- package/dist/__tests__/fetch.test.d.ts.map +0 -1
- package/dist/__tests__/jquery.test.d.ts.map +0 -1
- package/dist/__tests__/registry.test.d.ts +0 -2
- package/dist/__tests__/registry.test.d.ts.map +0 -1
- package/dist/__tests__/request-key.test.d.ts.map +0 -1
- package/dist/__tests__/requests.test.d.ts.map +0 -1
- package/dist/__tests__/runtime.test.d.ts.map +0 -1
- package/dist/__tests__/scope.test.d.ts.map +0 -1
- package/dist/__tests__/sinks.test.d.ts.map +0 -1
- package/dist/__tests__/step.test.d.ts.map +0 -1
- package/dist/__tests__/timeouts.test.d.ts.map +0 -1
- package/dist/core/registry.d.ts +0 -50
- package/dist/core/registry.d.ts.map +0 -1
- package/dist/core/registry.js +0 -102
- package/dist/core/registry.js.map +0 -1
- package/dist/endpoint-policy.d.ts.map +0 -1
- package/dist/endpoint-policy.js +0 -39
- package/dist/endpoint-policy.js.map +0 -1
- package/src/__tests__/axios.test.ts +0 -365
- package/src/__tests__/jquery.test.ts +0 -327
- package/src/__tests__/scope.test.tsx +0 -128
- package/src/core/registry.ts +0 -162
- /package/dist/{__mocks__ → __test-utils__}/test-runtime.d.ts +0 -0
- /package/dist/__tests__/{ambient.test.d.ts → core/ambient.test.d.ts} +0 -0
- /package/dist/__tests__/{define-journey.test.d.ts → core/define-journey.test.d.ts} +0 -0
- /package/dist/__tests__/{requests.test.d.ts → core/requests.test.d.ts} +0 -0
- /package/dist/__tests__/{runtime.test.d.ts → core/runtime.test.d.ts} +0 -0
- /package/dist/__tests__/{step.test.d.ts → core/step.test.d.ts} +0 -0
- /package/dist/__tests__/{timeouts.test.d.ts → core/timeouts.test.d.ts} +0 -0
- /package/dist/__tests__/{axios.test.d.ts → integrations/axios.test.d.ts} +0 -0
- /package/dist/__tests__/{context.test.d.ts → integrations/context.test.d.ts} +0 -0
- /package/dist/__tests__/{expose-app-journey.test.d.ts → integrations/expose-app-journey.test.d.ts} +0 -0
- /package/dist/__tests__/{fetch.test.d.ts → integrations/fetch.test.d.ts} +0 -0
- /package/dist/__tests__/{jquery.test.d.ts → integrations/jquery.test.d.ts} +0 -0
- /package/dist/__tests__/{request-key.test.d.ts → integrations/request-key.test.d.ts} +0 -0
- /package/dist/__tests__/{scope.test.d.ts → integrations/scope.test.d.ts} +0 -0
- /package/dist/__tests__/{datadog.test.d.ts → sinks/datadog.test.d.ts} +0 -0
- /package/dist/__tests__/{sinks.test.d.ts → sinks/sinks.test.d.ts} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** @jest-environment jsdom */
|
|
2
|
-
import { lastJourney, useJourneyTestRuntime } from '
|
|
3
|
-
import { defineJourney, journeyStep
|
|
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
|
|
|
@@ -25,7 +26,7 @@ describe('[journey] defineJourney', () => {
|
|
|
25
26
|
const subject = () =>
|
|
26
27
|
journey.step('do-work', step => {
|
|
27
28
|
step.setAttribute('x', 1);
|
|
28
|
-
step.
|
|
29
|
+
step.completeJourney({ 'payment.id': 42 });
|
|
29
30
|
});
|
|
30
31
|
|
|
31
32
|
test('opens, records the step, and completes good', async () => {
|
|
@@ -40,7 +41,7 @@ describe('[journey] defineJourney', () => {
|
|
|
40
41
|
|
|
41
42
|
test('mountStep also opens the journey', async () => {
|
|
42
43
|
await journey.mountStep('boot', step => {
|
|
43
|
-
step.
|
|
44
|
+
step.completeJourney();
|
|
44
45
|
});
|
|
45
46
|
|
|
46
47
|
expect(emitMock).toHaveBeenCalledTimes(1);
|
|
@@ -51,12 +52,12 @@ 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 = () =>
|
|
58
59
|
journey.step('do-work', step => {
|
|
59
|
-
step.
|
|
60
|
+
step.completeJourney();
|
|
60
61
|
});
|
|
61
62
|
|
|
62
63
|
test('does not restart the open journey', async () => {
|
|
@@ -75,7 +76,7 @@ describe('[journey] defineJourney', () => {
|
|
|
75
76
|
|
|
76
77
|
const subject = () =>
|
|
77
78
|
journeyStep(journey, 'via-free', step => {
|
|
78
|
-
step.
|
|
79
|
+
step.completeJourney();
|
|
79
80
|
});
|
|
80
81
|
|
|
81
82
|
test('resolves the handle name', async () => {
|
|
@@ -147,12 +148,12 @@ 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 = () =>
|
|
154
155
|
journey.mountStep('boot', step => {
|
|
155
|
-
step.
|
|
156
|
+
step.completeJourney();
|
|
156
157
|
});
|
|
157
158
|
|
|
158
159
|
test('does not restart the open journey', async () => {
|
|
@@ -165,12 +166,29 @@ describe('[journey] defineJourney', () => {
|
|
|
165
166
|
});
|
|
166
167
|
});
|
|
167
168
|
|
|
168
|
-
describe('when
|
|
169
|
-
test('exposes the
|
|
170
|
-
expect(journey.
|
|
171
|
-
expect(journey.
|
|
172
|
-
expect(journey.
|
|
173
|
-
expect(journey.
|
|
169
|
+
describe('when keeping the original config on the handle', () => {
|
|
170
|
+
test('exposes the same config object', () => {
|
|
171
|
+
expect(journey.config).toBe(base);
|
|
172
|
+
expect(journey.config.name).toBe(base.name);
|
|
173
|
+
expect(journey.config.name).toBe(base.name);
|
|
174
|
+
expect(journey.config.team).toBe(base.team);
|
|
175
|
+
expect(journey.config.group).toBe(base.group);
|
|
176
|
+
expect(journey.config.service).toBe(base.service);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe('when stepping via free journeyStep with a handle and nothing open', () => {
|
|
181
|
+
const subject = () =>
|
|
182
|
+
journeyStep(journey, 'via-handle', step => {
|
|
183
|
+
step.completeJourney();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test('auto-starts from the handle config', async () => {
|
|
187
|
+
await subject();
|
|
188
|
+
|
|
189
|
+
expect(emitMock).toHaveBeenCalledTimes(1);
|
|
190
|
+
expect(lastJourney(emitMock).outcome).toBe('good');
|
|
191
|
+
expect(lastJourney(emitMock).steps[0].name).toBe('via-handle');
|
|
174
192
|
});
|
|
175
193
|
});
|
|
176
194
|
});
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
/** @jest-environment jsdom */
|
|
2
|
-
import {
|
|
3
|
-
|
|
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
|
-
resolveJourneyStep,
|
|
9
|
-
startJourney,
|
|
10
12
|
type JourneyDef,
|
|
11
|
-
type
|
|
12
|
-
} from '
|
|
13
|
+
type JourneyStepStamp,
|
|
14
|
+
} from '../../core';
|
|
15
|
+
import type { StepRecord } from '../../core/types';
|
|
16
|
+
import { moduleRuntime } from '../../core/runtime';
|
|
13
17
|
|
|
14
18
|
const emitMock = jest.fn();
|
|
15
19
|
const baseDef = { ...baseJourneyDef, name: 'requests-journey' };
|
|
16
20
|
|
|
17
21
|
describe('[journey] reportBackendRequest', () => {
|
|
18
|
-
let stepRef:
|
|
22
|
+
let stepRef: StepRecord | undefined;
|
|
19
23
|
let status: number | undefined;
|
|
20
24
|
let durationMs: number;
|
|
21
25
|
let key: string;
|
|
@@ -32,12 +36,12 @@ describe('[journey] reportBackendRequest', () => {
|
|
|
32
36
|
const subject = () => reportBackendRequest(stepRef, status, durationMs, key);
|
|
33
37
|
|
|
34
38
|
async function withOpenStep(
|
|
35
|
-
run: (ref:
|
|
36
|
-
def: JourneyDef = { ...baseDef,
|
|
39
|
+
run: (ref: StepRecord) => void | Promise<void>,
|
|
40
|
+
def: JourneyDef = { ...baseDef, slowRequestMs: 5_000 }
|
|
37
41
|
) {
|
|
38
|
-
startJourney(def);
|
|
42
|
+
moduleRuntime.startJourney(def);
|
|
39
43
|
await journeyStep(baseDef.name, 'do-work', async step => {
|
|
40
|
-
stepRef =
|
|
44
|
+
stepRef = (step.stamp() as JourneyStepStamp).step;
|
|
41
45
|
await run(stepRef!);
|
|
42
46
|
});
|
|
43
47
|
}
|
|
@@ -96,7 +100,7 @@ describe('[journey] reportBackendRequest', () => {
|
|
|
96
100
|
() => {
|
|
97
101
|
subject();
|
|
98
102
|
},
|
|
99
|
-
{ ...baseDef,
|
|
103
|
+
{ ...baseDef, slowRequestMs: 50 }
|
|
100
104
|
);
|
|
101
105
|
|
|
102
106
|
expect(lastJourney(emitMock).reason).toBe('request-latency');
|
|
@@ -156,7 +160,7 @@ describe('[journey] reportBackendRequest', () => {
|
|
|
156
160
|
},
|
|
157
161
|
{
|
|
158
162
|
...baseDef,
|
|
159
|
-
|
|
163
|
+
slowRequestMs: 5_000,
|
|
160
164
|
endpoints: [{ match: '/flaky', ignore: true }],
|
|
161
165
|
}
|
|
162
166
|
);
|
|
@@ -166,28 +170,29 @@ describe('[journey] reportBackendRequest', () => {
|
|
|
166
170
|
});
|
|
167
171
|
});
|
|
168
172
|
|
|
169
|
-
describe('when a journey endpoint
|
|
170
|
-
test('
|
|
171
|
-
startJourney({
|
|
173
|
+
describe('when a journey endpoint slowRequestMs is 0', () => {
|
|
174
|
+
test('never scores latency for that prefix', async () => {
|
|
175
|
+
moduleRuntime.startJourney({
|
|
172
176
|
...baseDef,
|
|
173
|
-
|
|
174
|
-
endpoints: [{ match: '/special',
|
|
177
|
+
slowRequestMs: 30,
|
|
178
|
+
endpoints: [{ match: '/special', slowRequestMs: 0 }],
|
|
175
179
|
});
|
|
176
180
|
|
|
177
181
|
await journeyStep(baseDef.name, 'do-work', step => {
|
|
178
|
-
stepRef =
|
|
182
|
+
stepRef = (step.stamp() as JourneyStepStamp).step;
|
|
179
183
|
status = 200;
|
|
180
184
|
durationMs = 100;
|
|
181
185
|
key = '/special/path';
|
|
182
186
|
subject();
|
|
183
187
|
});
|
|
188
|
+
completeJourney(baseDef.name);
|
|
184
189
|
|
|
185
|
-
expect(lastJourney(emitMock).
|
|
190
|
+
expect(lastJourney(emitMock).outcome).toBe('good');
|
|
186
191
|
});
|
|
187
192
|
});
|
|
188
193
|
|
|
189
194
|
describe('when journey endpoint timeoutMs is set', () => {
|
|
190
|
-
test('wins over the journey
|
|
195
|
+
test('wins over the journey slowRequestMs', async () => {
|
|
191
196
|
await withOpenStep(
|
|
192
197
|
() => {
|
|
193
198
|
status = 200;
|
|
@@ -197,8 +202,8 @@ describe('[journey] reportBackendRequest', () => {
|
|
|
197
202
|
},
|
|
198
203
|
{
|
|
199
204
|
...baseDef,
|
|
200
|
-
|
|
201
|
-
endpoints: [{ match: '/special',
|
|
205
|
+
slowRequestMs: 5_000,
|
|
206
|
+
endpoints: [{ match: '/special', slowRequestMs: 20 }],
|
|
202
207
|
}
|
|
203
208
|
);
|
|
204
209
|
|
|
@@ -209,7 +214,7 @@ describe('[journey] reportBackendRequest', () => {
|
|
|
209
214
|
describe('when the journey endpoint ignore overrides the app-wide policy', () => {
|
|
210
215
|
beforeEach(() => {
|
|
211
216
|
configureJourney({
|
|
212
|
-
|
|
217
|
+
slowRequests: {
|
|
213
218
|
defaultMs: 4_000,
|
|
214
219
|
endpoints: [{ match: '/flaky', ignore: true }],
|
|
215
220
|
},
|
|
@@ -225,7 +230,7 @@ describe('[journey] reportBackendRequest', () => {
|
|
|
225
230
|
},
|
|
226
231
|
{
|
|
227
232
|
...baseDef,
|
|
228
|
-
|
|
233
|
+
slowRequestMs: 5_000,
|
|
229
234
|
endpoints: [{ match: '/flaky', ignore: false }],
|
|
230
235
|
}
|
|
231
236
|
);
|
|
@@ -237,11 +242,11 @@ describe('[journey] reportBackendRequest', () => {
|
|
|
237
242
|
describe('when only app-wide endpoint policies apply', () => {
|
|
238
243
|
beforeEach(() => {
|
|
239
244
|
configureJourney({
|
|
240
|
-
|
|
245
|
+
slowRequests: {
|
|
241
246
|
defaultMs: 4_000,
|
|
242
247
|
endpoints: [
|
|
243
248
|
{ match: '/ignored', ignore: true },
|
|
244
|
-
{ match: '/tight',
|
|
249
|
+
{ match: '/tight', slowRequestMs: 30 },
|
|
245
250
|
],
|
|
246
251
|
},
|
|
247
252
|
});
|
|
@@ -270,41 +275,42 @@ describe('[journey] reportBackendRequest', () => {
|
|
|
270
275
|
});
|
|
271
276
|
});
|
|
272
277
|
|
|
273
|
-
describe('when app endpoint
|
|
278
|
+
describe('when app endpoint slowRequestMs is 0', () => {
|
|
274
279
|
beforeEach(() => {
|
|
275
280
|
configureJourney({
|
|
276
|
-
|
|
281
|
+
slowRequests: {
|
|
277
282
|
defaultMs: 40,
|
|
278
|
-
endpoints: [{ match: '/loose',
|
|
283
|
+
endpoints: [{ match: '/loose', slowRequestMs: 0 }],
|
|
279
284
|
},
|
|
280
285
|
});
|
|
281
286
|
});
|
|
282
287
|
|
|
283
|
-
test('
|
|
284
|
-
startJourney(baseDef);
|
|
288
|
+
test('never scores latency for that prefix', async () => {
|
|
289
|
+
moduleRuntime.startJourney(baseDef);
|
|
285
290
|
|
|
286
291
|
await journeyStep(baseDef.name, 'do-work', step => {
|
|
287
|
-
stepRef =
|
|
292
|
+
stepRef = (step.stamp() as JourneyStepStamp).step;
|
|
288
293
|
status = 200;
|
|
289
294
|
durationMs = 100;
|
|
290
295
|
key = '/loose/x';
|
|
291
296
|
subject();
|
|
292
297
|
});
|
|
298
|
+
completeJourney(baseDef.name);
|
|
293
299
|
|
|
294
|
-
expect(lastJourney(emitMock).
|
|
300
|
+
expect(lastJourney(emitMock).outcome).toBe('good');
|
|
295
301
|
});
|
|
296
302
|
});
|
|
297
303
|
|
|
298
|
-
describe('when the journey has no
|
|
304
|
+
describe('when the journey has no slowRequestMs', () => {
|
|
299
305
|
beforeEach(() => {
|
|
300
|
-
configureJourney({
|
|
306
|
+
configureJourney({ slowRequests: { defaultMs: 40, endpoints: [] } });
|
|
301
307
|
});
|
|
302
308
|
|
|
303
309
|
test('uses the app defaultMs', async () => {
|
|
304
|
-
startJourney(baseDef);
|
|
310
|
+
moduleRuntime.startJourney(baseDef);
|
|
305
311
|
|
|
306
312
|
await journeyStep(baseDef.name, 'do-work', step => {
|
|
307
|
-
stepRef =
|
|
313
|
+
stepRef = (step.stamp() as JourneyStepStamp).step;
|
|
308
314
|
status = 200;
|
|
309
315
|
durationMs = 100;
|
|
310
316
|
key = '/any';
|
|
@@ -316,7 +322,7 @@ describe('[journey] reportBackendRequest', () => {
|
|
|
316
322
|
});
|
|
317
323
|
|
|
318
324
|
describe('against a closed journey step ref', () => {
|
|
319
|
-
let closedRef:
|
|
325
|
+
let closedRef: StepRecord | undefined;
|
|
320
326
|
|
|
321
327
|
beforeEach(async () => {
|
|
322
328
|
await withOpenStep(ref => {
|
|
@@ -334,4 +340,20 @@ describe('[journey] reportBackendRequest', () => {
|
|
|
334
340
|
expect(emitMock).not.toHaveBeenCalled();
|
|
335
341
|
});
|
|
336
342
|
});
|
|
343
|
+
|
|
344
|
+
describe('when many distinct keys are attributed', () => {
|
|
345
|
+
test('stops recording after 50 keys', async () => {
|
|
346
|
+
await withOpenStep(ref => {
|
|
347
|
+
for (let i = 0; i < 55; i += 1) {
|
|
348
|
+
reportBackendRequest(ref, 200, 1, `/api/${i}`);
|
|
349
|
+
}
|
|
350
|
+
expect(ref.attributedRequestKeys).toHaveLength(50);
|
|
351
|
+
expect(
|
|
352
|
+
moduleRuntime.getDebugSnapshot().openJourneys[0].steps[0].attributedRequestKeys
|
|
353
|
+
).toHaveLength(50);
|
|
354
|
+
});
|
|
355
|
+
completeJourney(baseDef.name);
|
|
356
|
+
expect(lastJourney(emitMock).outcome).toBe('good');
|
|
357
|
+
});
|
|
358
|
+
});
|
|
337
359
|
});
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
/** @jest-environment jsdom */
|
|
2
|
-
import { lastJourney, useJourneyTestRuntime } from '
|
|
3
|
-
import { configureJourney,
|
|
2
|
+
import { lastJourney, useJourneyTestRuntime } from '../../__test-utils__/test-runtime';
|
|
3
|
+
import { configureJourney, getSlowRequests } from '../../config';
|
|
4
4
|
import {
|
|
5
|
-
activeJourneyStep,
|
|
6
5
|
completeJourney,
|
|
7
|
-
createJourneyRuntime,
|
|
8
6
|
defineJourney,
|
|
9
7
|
journeyStep,
|
|
10
8
|
resetJourney,
|
|
11
|
-
|
|
12
|
-
} from '
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
singleInFlightJourneyStep,
|
|
10
|
+
} from '../../core';
|
|
11
|
+
import { createJourneyRuntime, moduleRuntime } from '../../core/runtime';
|
|
12
|
+
import { registerJourneyRuntime, setJourneyDefaultEnabled } from '../../global';
|
|
13
|
+
import { sendToConsole } from '../../sinks/console';
|
|
14
|
+
import { sendToDatadog } from '../../sinks/datadog';
|
|
15
|
+
|
|
16
|
+
jest.mock('../../sinks/datadog', () => ({
|
|
16
17
|
sendToDatadog: jest.fn(),
|
|
17
18
|
}));
|
|
18
19
|
|
|
@@ -31,7 +32,7 @@ describe('[journey] resetJourney', () => {
|
|
|
31
32
|
|
|
32
33
|
describe('when a journey is open', () => {
|
|
33
34
|
beforeEach(() => {
|
|
34
|
-
startJourney(def);
|
|
35
|
+
moduleRuntime.startJourney(def);
|
|
35
36
|
});
|
|
36
37
|
|
|
37
38
|
const subject = () => resetJourney({ sinks: [emitMock] });
|
|
@@ -52,29 +53,31 @@ describe('[journey] resetJourney', () => {
|
|
|
52
53
|
|
|
53
54
|
describe('when request timeouts were customized', () => {
|
|
54
55
|
beforeEach(() => {
|
|
55
|
-
configureJourney({
|
|
56
|
+
configureJourney({ slowRequests: { defaultMs: 100, endpoints: [] } });
|
|
56
57
|
});
|
|
57
58
|
|
|
58
59
|
const subject = () => resetJourney({ sinks: [emitMock] });
|
|
59
60
|
|
|
60
|
-
test('
|
|
61
|
+
test('clears this runtime overlay and inherits shared policies', () => {
|
|
61
62
|
subject();
|
|
62
63
|
|
|
63
|
-
expect(
|
|
64
|
+
expect(moduleRuntime.getDebugSnapshot().policyOverlay.slowRequests).toBe(false);
|
|
65
|
+
expect(moduleRuntime.getSlowRequests().defaultMs).toBe(100);
|
|
66
|
+
expect(getSlowRequests().defaultMs).toBe(100);
|
|
64
67
|
});
|
|
65
68
|
});
|
|
66
69
|
|
|
67
|
-
describe('when reset passes custom
|
|
70
|
+
describe('when reset passes custom slowRequests', () => {
|
|
68
71
|
const subject = () =>
|
|
69
72
|
resetJourney({
|
|
70
73
|
sinks: [emitMock],
|
|
71
|
-
|
|
74
|
+
slowRequests: { defaultMs: 250, endpoints: [] },
|
|
72
75
|
});
|
|
73
76
|
|
|
74
|
-
test('applies those timeouts', () => {
|
|
77
|
+
test('applies those timeouts on the module engine', () => {
|
|
75
78
|
subject();
|
|
76
79
|
|
|
77
|
-
expect(
|
|
80
|
+
expect(moduleRuntime.getSlowRequests().defaultMs).toBe(250);
|
|
78
81
|
});
|
|
79
82
|
});
|
|
80
83
|
|
|
@@ -82,7 +85,7 @@ describe('[journey] resetJourney', () => {
|
|
|
82
85
|
beforeEach(() => {
|
|
83
86
|
resetJourney({ sinks: [emitMock] });
|
|
84
87
|
emitMock.mockClear();
|
|
85
|
-
startJourney(def);
|
|
88
|
+
moduleRuntime.startJourney(def);
|
|
86
89
|
completeJourney(def.name);
|
|
87
90
|
emitMock.mockClear();
|
|
88
91
|
});
|
|
@@ -91,7 +94,7 @@ describe('[journey] resetJourney', () => {
|
|
|
91
94
|
|
|
92
95
|
test('leaves sinks empty', () => {
|
|
93
96
|
subject();
|
|
94
|
-
startJourney(def);
|
|
97
|
+
moduleRuntime.startJourney(def);
|
|
95
98
|
completeJourney(def.name);
|
|
96
99
|
|
|
97
100
|
expect(emitMock).not.toHaveBeenCalled();
|
|
@@ -103,7 +106,7 @@ describe('[journey] resetJourney', () => {
|
|
|
103
106
|
let stepPromise: Promise<unknown>;
|
|
104
107
|
|
|
105
108
|
beforeEach(() => {
|
|
106
|
-
startJourney(def);
|
|
109
|
+
moduleRuntime.startJourney(def);
|
|
107
110
|
const hang = new Promise<void>(resolve => {
|
|
108
111
|
resolveWork = resolve;
|
|
109
112
|
});
|
|
@@ -120,18 +123,31 @@ describe('[journey] resetJourney', () => {
|
|
|
120
123
|
const subject = () => resetJourney({ sinks: [emitMock] });
|
|
121
124
|
|
|
122
125
|
test('clears the ambient step', () => {
|
|
123
|
-
expect(
|
|
126
|
+
expect(singleInFlightJourneyStep()).not.toBeNull();
|
|
124
127
|
|
|
125
128
|
subject();
|
|
126
129
|
|
|
127
|
-
expect(
|
|
130
|
+
expect(singleInFlightJourneyStep()).toBeNull();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('reuses the same ambient stamp while the active step is unchanged', async () => {
|
|
134
|
+
const first = singleInFlightJourneyStep();
|
|
135
|
+
const second = singleInFlightJourneyStep();
|
|
136
|
+
|
|
137
|
+
expect(first).not.toBeNull();
|
|
138
|
+
expect(second).toBe(first);
|
|
139
|
+
|
|
140
|
+
resolveWork();
|
|
141
|
+
await stepPromise;
|
|
142
|
+
|
|
143
|
+
expect(singleInFlightJourneyStep()).toBeNull();
|
|
128
144
|
});
|
|
129
145
|
});
|
|
130
146
|
|
|
131
147
|
describe('when a journey countdown is armed', () => {
|
|
132
148
|
beforeEach(() => {
|
|
133
149
|
jest.useFakeTimers();
|
|
134
|
-
startJourney({ ...def, timeoutMs: 50 });
|
|
150
|
+
moduleRuntime.startJourney({ ...def, timeoutMs: 50 });
|
|
135
151
|
});
|
|
136
152
|
|
|
137
153
|
afterEach(() => {
|
|
@@ -157,26 +173,42 @@ describe('[journey] createJourneyRuntime', () => {
|
|
|
157
173
|
let runtime: ReturnType<typeof createJourneyRuntime>;
|
|
158
174
|
|
|
159
175
|
beforeEach(() => {
|
|
176
|
+
setJourneyDefaultEnabled(true);
|
|
160
177
|
resetJourney({
|
|
161
178
|
sinks: [defaultEmit],
|
|
162
|
-
|
|
179
|
+
slowRequests: { defaultMs: 4_000, endpoints: [] },
|
|
163
180
|
});
|
|
164
181
|
defaultEmit.mockClear();
|
|
165
182
|
isolatedEmit.mockClear();
|
|
166
183
|
runtime = createJourneyRuntime({ sinks: [isolatedEmit] });
|
|
167
184
|
});
|
|
168
185
|
|
|
186
|
+
test('auto-assigns a unique runtime-N id when registered without an id', () => {
|
|
187
|
+
registerJourneyRuntime(runtime);
|
|
188
|
+
|
|
189
|
+
expect(moduleRuntime.id).toMatch(/^runtime-\d+$/);
|
|
190
|
+
expect(runtime.id).toMatch(/^runtime-\d+$/);
|
|
191
|
+
expect(runtime.id).not.toBe(moduleRuntime.id);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test('uses the provided id', () => {
|
|
195
|
+
const named = createJourneyRuntime({ id: 'job-booking', sinks: [isolatedEmit] });
|
|
196
|
+
expect(named.id).toBe('job-booking');
|
|
197
|
+
});
|
|
198
|
+
|
|
169
199
|
afterEach(() => {
|
|
200
|
+
setJourneyDefaultEnabled(false);
|
|
201
|
+
localStorage.clear();
|
|
170
202
|
resetJourney({
|
|
171
|
-
sinks: [sendToDatadog],
|
|
172
|
-
|
|
203
|
+
sinks: [sendToDatadog, sendToConsole],
|
|
204
|
+
slowRequests: { defaultMs: 4_000, endpoints: [] },
|
|
173
205
|
});
|
|
174
206
|
});
|
|
175
207
|
|
|
176
208
|
describe('when both runtimes open a journey', () => {
|
|
177
209
|
beforeEach(() => {
|
|
178
210
|
runtime.startJourney(def);
|
|
179
|
-
startJourney({ ...def, name: 'default-side' });
|
|
211
|
+
moduleRuntime.startJourney({ ...def, name: 'default-side' });
|
|
180
212
|
});
|
|
181
213
|
|
|
182
214
|
const subject = () => {
|
|
@@ -192,30 +224,32 @@ describe('[journey] createJourneyRuntime', () => {
|
|
|
192
224
|
});
|
|
193
225
|
});
|
|
194
226
|
|
|
195
|
-
describe('when the isolated runtime has its own
|
|
227
|
+
describe('when the isolated runtime has its own slowRequests', () => {
|
|
196
228
|
beforeEach(() => {
|
|
197
229
|
runtime = createJourneyRuntime({
|
|
198
230
|
sinks: [isolatedEmit],
|
|
199
|
-
|
|
231
|
+
slowRequests: { defaultMs: 10, endpoints: [] },
|
|
200
232
|
});
|
|
201
|
-
configureJourney({
|
|
233
|
+
configureJourney({ slowRequests: { defaultMs: 9_000, endpoints: [] } });
|
|
202
234
|
});
|
|
203
235
|
|
|
204
|
-
test('
|
|
205
|
-
expect(runtime.
|
|
206
|
-
|
|
236
|
+
test('inherits shared host thresholds unless this runtime overlays', () => {
|
|
237
|
+
expect(runtime.getSlowRequests().defaultMs).toBe(9_000);
|
|
238
|
+
runtime.setPolicyOverlay({ slowRequests: { defaultMs: 10, endpoints: [] } });
|
|
239
|
+
expect(runtime.getSlowRequests().defaultMs).toBe(10);
|
|
240
|
+
expect(getSlowRequests().defaultMs).toBe(9_000);
|
|
207
241
|
});
|
|
208
242
|
});
|
|
209
243
|
|
|
210
244
|
describe('when the isolated runtime is reset', () => {
|
|
211
245
|
beforeEach(() => {
|
|
212
246
|
runtime.startJourney(def);
|
|
213
|
-
startJourney({ ...def, name: 'default-side' });
|
|
247
|
+
moduleRuntime.startJourney({ ...def, name: 'default-side' });
|
|
214
248
|
});
|
|
215
249
|
|
|
216
250
|
const subject = () => runtime.reset({ sinks: [isolatedEmit] });
|
|
217
251
|
|
|
218
|
-
test('does not clear the
|
|
252
|
+
test('does not clear the module engine', () => {
|
|
219
253
|
subject();
|
|
220
254
|
completeJourney('default-side');
|
|
221
255
|
|
|
@@ -227,10 +261,10 @@ describe('[journey] createJourneyRuntime', () => {
|
|
|
227
261
|
describe('when defineJourney is used', () => {
|
|
228
262
|
const subject = () =>
|
|
229
263
|
defineJourney(def).step('load', step => {
|
|
230
|
-
step.
|
|
264
|
+
step.completeJourney();
|
|
231
265
|
});
|
|
232
266
|
|
|
233
|
-
test('binds to the
|
|
267
|
+
test('binds to the module runtime', async () => {
|
|
234
268
|
await subject();
|
|
235
269
|
|
|
236
270
|
expect(defaultEmit).toHaveBeenCalledTimes(1);
|
|
@@ -270,4 +304,17 @@ describe('[journey] createJourneyRuntime', () => {
|
|
|
270
304
|
expect(isolatedEmit).toHaveBeenCalledTimes(1);
|
|
271
305
|
});
|
|
272
306
|
});
|
|
307
|
+
|
|
308
|
+
describe('maxSteps bounds', () => {
|
|
309
|
+
test('clamps invalid and oversized values', () => {
|
|
310
|
+
moduleRuntime.setMaxSteps(Infinity);
|
|
311
|
+
expect(moduleRuntime.getMaxSteps()).toBe(50);
|
|
312
|
+
moduleRuntime.setMaxSteps(0);
|
|
313
|
+
expect(moduleRuntime.getMaxSteps()).toBe(50);
|
|
314
|
+
moduleRuntime.setMaxSteps(500);
|
|
315
|
+
expect(moduleRuntime.getMaxSteps()).toBe(200);
|
|
316
|
+
moduleRuntime.setMaxSteps(Number.NaN);
|
|
317
|
+
expect(moduleRuntime.getMaxSteps()).toBe(50);
|
|
318
|
+
});
|
|
319
|
+
});
|
|
273
320
|
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/** @jest-environment jsdom */
|
|
2
|
+
import { MAX_ATTRIBUTE_KEYS, MAX_TAG_KEYS, isUnsafeKey } from '../../core/limits';
|
|
3
|
+
import { assignRecord, copyRecord, emptyRecord, sanitizeJourneyEvent } from '../../core/sanitize';
|
|
4
|
+
import type { JourneyEvent } from '../../core/types';
|
|
5
|
+
|
|
6
|
+
describe('[journey] sanitize / copyRecord', () => {
|
|
7
|
+
test('isUnsafeKey blocks proto and secret names', () => {
|
|
8
|
+
expect(isUnsafeKey('__proto__')).toBe(true);
|
|
9
|
+
expect(isUnsafeKey('constructor')).toBe(true);
|
|
10
|
+
expect(isUnsafeKey('Authorization')).toBe(true);
|
|
11
|
+
expect(isUnsafeKey('token')).toBe(true);
|
|
12
|
+
expect(isUnsafeKey('customer.id')).toBe(false);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('JSON __proto__ assign does not pollute Object.prototype', () => {
|
|
16
|
+
const target = emptyRecord();
|
|
17
|
+
const polluted = JSON.parse('{"__proto__":{"polluted":true},"ok":1}') as Record<
|
|
18
|
+
string,
|
|
19
|
+
unknown
|
|
20
|
+
>;
|
|
21
|
+
assignRecord(target, polluted, MAX_TAG_KEYS);
|
|
22
|
+
expect(target.ok).toBe(1);
|
|
23
|
+
expect(({} as { polluted?: boolean }).polluted).toBeUndefined();
|
|
24
|
+
expect(Object.prototype.hasOwnProperty.call(target, '__proto__')).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('copyRecord caps keys and drops secrets', () => {
|
|
28
|
+
const src: Record<string, string | number> = { token: 'secret', keep: 1 };
|
|
29
|
+
for (let i = 0; i < MAX_ATTRIBUTE_KEYS + 5; i += 1) {
|
|
30
|
+
src[`k${i}`] = i;
|
|
31
|
+
}
|
|
32
|
+
const out = copyRecord(src, { maxKeys: MAX_ATTRIBUTE_KEYS, normalizeKeys: true });
|
|
33
|
+
expect(out.token).toBeUndefined();
|
|
34
|
+
expect(Object.keys(out).length).toBe(MAX_ATTRIBUTE_KEYS);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('sanitizeJourneyEvent normalizes step names and tag keys', () => {
|
|
38
|
+
const event: JourneyEvent = {
|
|
39
|
+
journey: {
|
|
40
|
+
name: 'Make A Sale!!',
|
|
41
|
+
team: 'Sales',
|
|
42
|
+
group: 'retail',
|
|
43
|
+
service: 'checkout',
|
|
44
|
+
outcome: 'good',
|
|
45
|
+
durationMs: 10,
|
|
46
|
+
expected: ['Create Sale'],
|
|
47
|
+
tags: { 'User Email': 'a@b.com', 'token': 'nope', 'plan': 'Pro' },
|
|
48
|
+
steps: [
|
|
49
|
+
{
|
|
50
|
+
name: 'Load Customer 1',
|
|
51
|
+
startMs: 0,
|
|
52
|
+
durationMs: 4,
|
|
53
|
+
outcome: 'good',
|
|
54
|
+
attributes: { 'token': 'x', 'Draft.id': 'abc' },
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
const j = sanitizeJourneyEvent(event).journey;
|
|
60
|
+
expect(j.name).toBe('make_a_sale');
|
|
61
|
+
expect(j.expected).toEqual(['create_sale']);
|
|
62
|
+
expect(j.steps[0].name).toBe('load_customer_1');
|
|
63
|
+
expect(j.tags?.user_email).toBe('a_b.com');
|
|
64
|
+
expect(j.tags?.token).toBeUndefined();
|
|
65
|
+
expect(j.tags?.plan).toBe('pro');
|
|
66
|
+
expect(j.steps[0].attributes?.token).toBeUndefined();
|
|
67
|
+
expect(j.steps[0].attributes?.['draft.id']).toBe('abc');
|
|
68
|
+
});
|
|
69
|
+
});
|