@funnelsgrove/runtime 0.1.55 → 0.1.59
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/dist/components/FunnelContext.d.ts +5 -1
- package/dist/components/FunnelContext.js +2 -1
- package/dist/components/ManageSubscriptionScreen.js +4 -1
- package/dist/components/SubscriptionHandoffScreen.d.ts +2 -1
- package/dist/components/SubscriptionHandoffScreen.js +11 -6
- package/dist/config/funnel.manifest.types.d.ts +4 -2
- package/dist/generated/step-contract-hash.d.ts +1 -0
- package/dist/generated/step-contract-hash.js +2 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +10 -0
- package/dist/migrations/step-contract-v2.d.ts +50 -0
- package/dist/migrations/step-contract-v2.js +310 -0
- package/dist/migrations/step-contract-v3.d.ts +41 -0
- package/dist/migrations/step-contract-v3.js +26 -0
- package/dist/runtime/funnel-attribution.d.ts +1 -0
- package/dist/runtime/funnel-attribution.js +24 -0
- package/dist/runtime/funnel-manifest.validation.d.ts +7 -2
- package/dist/runtime/funnel-manifest.validation.js +24 -68
- package/dist/runtime/funnel-step-lifecycle.d.ts +35 -0
- package/dist/runtime/funnel-step-lifecycle.js +49 -0
- package/dist/runtime/funnel-step-metadata.validation.d.ts +7 -0
- package/dist/runtime/funnel-step-metadata.validation.js +88 -0
- package/dist/runtime/submit-email-capture.d.ts +23 -0
- package/dist/runtime/submit-email-capture.js +54 -0
- package/dist/runtime/use-funnel-flow-controller.d.ts +71 -5
- package/dist/runtime/use-funnel-flow-controller.js +850 -116
- package/dist/runtime/use-step-choices.d.ts +58 -0
- package/dist/runtime/use-step-choices.js +187 -0
- package/dist/sdk/userAnswers.d.ts +1 -0
- package/dist/services/api.service.d.ts +10 -0
- package/dist/services/api.service.js +32 -0
- package/dist/services/funnel-sdk.service.d.ts +17 -0
- package/dist/services/funnel-sdk.service.js +13 -0
- package/dist/steps/choice-contract.d.ts +33 -0
- package/dist/steps/choice-contract.js +302 -0
- package/dist/steps/step-contract.d.ts +498 -0
- package/dist/steps/step-contract.js +506 -0
- package/dist/steps/types.d.ts +7 -5
- package/dist/steps/types.js +1 -18
- package/dist/testing/funnel-contract-journey-driver.d.ts +35 -0
- package/dist/testing/funnel-contract-journey-driver.js +266 -0
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.js +1 -0
- package/dist/validation/fixtures/invalid-reserved-identities.d.ts +249 -0
- package/dist/validation/fixtures/invalid-reserved-identities.js +20 -0
- package/dist/validation/fixtures/valid-v2-project.d.ts +419 -0
- package/dist/validation/fixtures/valid-v2-project.js +228 -0
- package/dist/validation/fixtures/valid-v3-project.d.ts +419 -0
- package/dist/validation/fixtures/valid-v3-project.js +30 -0
- package/dist/validation/funnel-contract-diagnostics.d.ts +49 -0
- package/dist/validation/funnel-contract-diagnostics.js +67 -0
- package/dist/validation/funnel-contract-validator.d.ts +30 -0
- package/dist/validation/funnel-contract-validator.js +1458 -0
- package/dist/validation/funnel-manifest-input.normalization.d.ts +67 -0
- package/dist/validation/funnel-manifest-input.normalization.js +386 -0
- package/package.json +9 -1
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { JSDOM } from 'jsdom';
|
|
3
|
+
import { fireEvent, render, waitFor } from '@testing-library/react';
|
|
4
|
+
import { useEffect } from 'react';
|
|
5
|
+
import { FunnelProvider, useFunnel } from '../components/FunnelContext.js';
|
|
6
|
+
import { useFunnelFlowController } from '../runtime/use-funnel-flow-controller.js';
|
|
7
|
+
import { useStepChoices } from '../runtime/use-step-choices.js';
|
|
8
|
+
const steps = [
|
|
9
|
+
{
|
|
10
|
+
id: 'intro',
|
|
11
|
+
name: 'intro',
|
|
12
|
+
type: 'intro_hero',
|
|
13
|
+
figmaNodeId: '1:1',
|
|
14
|
+
title: 'Intro',
|
|
15
|
+
description: 'Intro',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 'goal-choice',
|
|
19
|
+
name: 'goal-choice',
|
|
20
|
+
type: 'single_step_choice',
|
|
21
|
+
choice: { answerKey: 'goal' },
|
|
22
|
+
figmaNodeId: '1:2',
|
|
23
|
+
title: 'Goal',
|
|
24
|
+
description: 'Goal',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'mood-choice',
|
|
28
|
+
name: 'mood-choice',
|
|
29
|
+
type: 'single_step_choice_emoji',
|
|
30
|
+
choice: { answerKey: 'mood' },
|
|
31
|
+
figmaNodeId: '1:3',
|
|
32
|
+
title: 'Mood',
|
|
33
|
+
description: 'Mood',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'habit-choice',
|
|
37
|
+
name: 'habit-choice',
|
|
38
|
+
type: 'multi_select_choice',
|
|
39
|
+
choice: { answerKey: 'habits', allowEmpty: false },
|
|
40
|
+
figmaNodeId: '1:4',
|
|
41
|
+
title: 'Habits',
|
|
42
|
+
description: 'Habits',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 'email-capture',
|
|
46
|
+
name: 'email-capture',
|
|
47
|
+
type: 'form_input',
|
|
48
|
+
figmaNodeId: '1:5',
|
|
49
|
+
title: 'Email',
|
|
50
|
+
description: 'Email',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'paywall',
|
|
54
|
+
name: 'paywall',
|
|
55
|
+
type: 'paywall_offer',
|
|
56
|
+
kind: 'paywall',
|
|
57
|
+
figmaNodeId: '1:6',
|
|
58
|
+
title: 'Paywall',
|
|
59
|
+
description: 'Paywall',
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'subscription-started',
|
|
63
|
+
name: 'subscription-started',
|
|
64
|
+
type: 'complete_registration',
|
|
65
|
+
kind: 'subscription-handoff',
|
|
66
|
+
figmaNodeId: '1:7',
|
|
67
|
+
title: 'Subscription started',
|
|
68
|
+
description: 'Subscription started',
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
const stepById = Object.fromEntries(steps.map((step) => [step.id, step]));
|
|
72
|
+
const stepIds = steps.map((step) => step.id);
|
|
73
|
+
const standardOptions = [{ id: 'focus', label: 'Focus' }];
|
|
74
|
+
const emojiOptions = [{ id: 'calm', label: 'Calm', emoji: '😌' }];
|
|
75
|
+
const multiOptions = [
|
|
76
|
+
{ id: 'walk', label: 'Walk' },
|
|
77
|
+
{ id: 'sleep', label: 'Sleep' },
|
|
78
|
+
];
|
|
79
|
+
const installDomIfNeeded = () => {
|
|
80
|
+
if (typeof window !== 'undefined' && typeof document !== 'undefined') {
|
|
81
|
+
document.body.replaceChildren();
|
|
82
|
+
window.history.replaceState({}, '', '/intro?mode=test');
|
|
83
|
+
return () => document.body.replaceChildren();
|
|
84
|
+
}
|
|
85
|
+
const dom = new JSDOM('<!doctype html><html><body></body></html>', {
|
|
86
|
+
pretendToBeVisual: true,
|
|
87
|
+
url: 'https://journey.test/intro?mode=test',
|
|
88
|
+
});
|
|
89
|
+
const values = {
|
|
90
|
+
window: dom.window,
|
|
91
|
+
document: dom.window.document,
|
|
92
|
+
navigator: dom.window.navigator,
|
|
93
|
+
HTMLElement: dom.window.HTMLElement,
|
|
94
|
+
Node: dom.window.Node,
|
|
95
|
+
Event: dom.window.Event,
|
|
96
|
+
CustomEvent: dom.window.CustomEvent,
|
|
97
|
+
PageTransitionEvent: dom.window.PageTransitionEvent,
|
|
98
|
+
Blob: dom.window.Blob,
|
|
99
|
+
localStorage: dom.window.localStorage,
|
|
100
|
+
IS_REACT_ACT_ENVIRONMENT: true,
|
|
101
|
+
};
|
|
102
|
+
const previous = new Map();
|
|
103
|
+
for (const [key, value] of Object.entries(values)) {
|
|
104
|
+
previous.set(key, Object.getOwnPropertyDescriptor(globalThis, key));
|
|
105
|
+
Object.defineProperty(globalThis, key, {
|
|
106
|
+
configurable: true,
|
|
107
|
+
writable: true,
|
|
108
|
+
value,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return () => {
|
|
112
|
+
dom.window.close();
|
|
113
|
+
for (const [key, descriptor] of previous) {
|
|
114
|
+
if (descriptor) {
|
|
115
|
+
Object.defineProperty(globalThis, key, descriptor);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
delete globalThis[key];
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
function JourneyProbe({ onVisit }) {
|
|
124
|
+
const funnel = useFunnel();
|
|
125
|
+
const single = useStepChoices(stepById['goal-choice'], standardOptions);
|
|
126
|
+
const emoji = useStepChoices(stepById['mood-choice'], emojiOptions);
|
|
127
|
+
const multi = useStepChoices(stepById['habit-choice'], multiOptions);
|
|
128
|
+
useEffect(() => onVisit(funnel.activeStepId), [funnel.activeStepId, onVisit]);
|
|
129
|
+
const act = () => {
|
|
130
|
+
switch (funnel.activeStepId) {
|
|
131
|
+
case 'intro':
|
|
132
|
+
case 'paywall':
|
|
133
|
+
funnel.goNext();
|
|
134
|
+
break;
|
|
135
|
+
case 'goal-choice':
|
|
136
|
+
single.selectSingle('focus');
|
|
137
|
+
break;
|
|
138
|
+
case 'mood-choice':
|
|
139
|
+
emoji.selectSingle('calm');
|
|
140
|
+
break;
|
|
141
|
+
case 'habit-choice':
|
|
142
|
+
multi.completeMulti(['walk', 'sleep']);
|
|
143
|
+
break;
|
|
144
|
+
case 'email-capture':
|
|
145
|
+
void funnel.submitEmailCapture(' Person@Example.com ');
|
|
146
|
+
break;
|
|
147
|
+
case 'subscription-started':
|
|
148
|
+
funnel.completeFunnel('subscription-started');
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
return (_jsx("button", { "data-journey-action": true, "data-step-id": funnel.activeStepId, onClick: act, children: "Continue" }));
|
|
153
|
+
}
|
|
154
|
+
function JourneyHarness({ api, analytics, onVisit, }) {
|
|
155
|
+
const controller = useFunnelFlowController({
|
|
156
|
+
api,
|
|
157
|
+
analytics,
|
|
158
|
+
stepContractVersion: 2,
|
|
159
|
+
defaultStepId: 'intro',
|
|
160
|
+
stepSequence: stepIds,
|
|
161
|
+
stepById,
|
|
162
|
+
stepComponentById: Object.fromEntries(stepIds.map((id) => [id, true])),
|
|
163
|
+
funnelExperiments: [],
|
|
164
|
+
getPathForStep: (stepId) => `/${stepId}`,
|
|
165
|
+
getStepIdFromPath: (pathname) => {
|
|
166
|
+
const candidate = pathname.slice(1);
|
|
167
|
+
return stepIds.includes(candidate) ? candidate : null;
|
|
168
|
+
},
|
|
169
|
+
getSequentialNextStepId: (stepId) => { var _a; return (_a = stepIds[stepIds.indexOf(stepId) + 1]) !== null && _a !== void 0 ? _a : null; },
|
|
170
|
+
getChoiceTargetsForStep: () => null,
|
|
171
|
+
isFunnelStepId: (value) => stepIds.includes(value),
|
|
172
|
+
resolveConfiguredNextStep: (stepId) => { var _a; return (_a = stepIds[stepIds.indexOf(stepId) + 1]) !== null && _a !== void 0 ? _a : null; },
|
|
173
|
+
resolveRuntimeInitialStepId: () => 'intro',
|
|
174
|
+
});
|
|
175
|
+
return (_jsx(FunnelProvider, { value: controller.contextValue, children: _jsx(JourneyProbe, { onVisit: onVisit }) }));
|
|
176
|
+
}
|
|
177
|
+
export const runFunnelContractJourney = async (options) => {
|
|
178
|
+
const restoreDom = installDomIfNeeded();
|
|
179
|
+
const envelopes = [];
|
|
180
|
+
const emailCaptureCalls = [];
|
|
181
|
+
const browserEmailEvents = [];
|
|
182
|
+
const visitedStepIds = [];
|
|
183
|
+
let eventSequence = 0;
|
|
184
|
+
const journeyUser = {
|
|
185
|
+
id: 'journey-user',
|
|
186
|
+
name: '',
|
|
187
|
+
email: '',
|
|
188
|
+
attributes: {},
|
|
189
|
+
document: {},
|
|
190
|
+
};
|
|
191
|
+
const api = {
|
|
192
|
+
getOrCreateClientUserId: () => journeyUser.id,
|
|
193
|
+
getBootstrapCandidateUserId: () => journeyUser.id,
|
|
194
|
+
bootstrapSession: async () => journeyUser,
|
|
195
|
+
pingUserContext: async () => null,
|
|
196
|
+
updateUser: async (user) => user,
|
|
197
|
+
captureEmail: async (captureInput) => {
|
|
198
|
+
emailCaptureCalls.push(captureInput);
|
|
199
|
+
return options.captureEmail(captureInput);
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
const capture = (eventType, input) => {
|
|
203
|
+
eventSequence += 1;
|
|
204
|
+
const eventId = `journey-event-${eventSequence}`;
|
|
205
|
+
envelopes.push(Object.assign({ eventType, eventId }, input));
|
|
206
|
+
return eventId;
|
|
207
|
+
};
|
|
208
|
+
const analytics = {
|
|
209
|
+
flush: async () => envelopes.length,
|
|
210
|
+
trackEmailCapturedBrowserDestinations: (input) => {
|
|
211
|
+
browserEmailEvents.push(input);
|
|
212
|
+
},
|
|
213
|
+
trackStepStarted: (input) => capture('step_start', input),
|
|
214
|
+
trackStepCompleted: (input) => capture('step_end', input),
|
|
215
|
+
trackStepExited: (input) => capture('step_exit', input),
|
|
216
|
+
trackFunnelStarted: (input) => capture('funnel_start', input),
|
|
217
|
+
trackFunnelCompleted: (input) => capture('funnel_completed', input),
|
|
218
|
+
trackFirstStepViewed: (input) => capture('first_step_viewed', input),
|
|
219
|
+
trackFirstStepClicked: (input) => capture('first_step_clicked', input),
|
|
220
|
+
};
|
|
221
|
+
const rendered = render(_jsx(JourneyHarness, { api: api, analytics: analytics, onVisit: (stepId) => {
|
|
222
|
+
if (visitedStepIds.at(-1) !== stepId) {
|
|
223
|
+
visitedStepIds.push(stepId);
|
|
224
|
+
}
|
|
225
|
+
} }));
|
|
226
|
+
try {
|
|
227
|
+
await waitFor(() => {
|
|
228
|
+
if (envelopes.filter((event) => event.eventType === 'step_start').length !== 1) {
|
|
229
|
+
throw new Error('Initial lifecycle did not start.');
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
for (const stepId of stepIds) {
|
|
233
|
+
const button = await waitFor(() => {
|
|
234
|
+
const candidate = rendered.container.querySelector('[data-journey-action]');
|
|
235
|
+
if (!candidate || candidate.dataset.stepId !== stepId) {
|
|
236
|
+
throw new Error(`Journey did not reach ${stepId}.`);
|
|
237
|
+
}
|
|
238
|
+
return candidate;
|
|
239
|
+
});
|
|
240
|
+
fireEvent.click(button);
|
|
241
|
+
if (stepId === 'subscription-started') {
|
|
242
|
+
await waitFor(() => {
|
|
243
|
+
if (!envelopes.some((event) => event.eventType === 'funnel_completed')) {
|
|
244
|
+
throw new Error('Funnel completion did not fire.');
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
const nextId = stepIds[stepIds.indexOf(stepId) + 1];
|
|
250
|
+
await waitFor(() => {
|
|
251
|
+
var _a;
|
|
252
|
+
const activeId = (_a = rendered.container
|
|
253
|
+
.querySelector('[data-journey-action]')) === null || _a === void 0 ? void 0 : _a.dataset.stepId;
|
|
254
|
+
if (activeId !== nextId) {
|
|
255
|
+
throw new Error(`Journey did not advance from ${stepId}.`);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return { envelopes, emailCaptureCalls, browserEmailEvents, visitedStepIds };
|
|
261
|
+
}
|
|
262
|
+
finally {
|
|
263
|
+
rendered.unmount();
|
|
264
|
+
restoreDom();
|
|
265
|
+
}
|
|
266
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './funnel-contract-journey-driver.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './funnel-contract-journey-driver.js';
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
export declare const scratchcardMisclassifiedManifest: {
|
|
2
|
+
readonly steps: readonly [{
|
|
3
|
+
readonly id: "step-1";
|
|
4
|
+
readonly name: "step-one";
|
|
5
|
+
readonly path: "/step-1";
|
|
6
|
+
readonly filePath: "src/steps/step-01.tsx";
|
|
7
|
+
readonly componentKey: "stepOne";
|
|
8
|
+
readonly type: "intro_hero";
|
|
9
|
+
readonly title: "Introduction";
|
|
10
|
+
}, {
|
|
11
|
+
readonly id: "step-2";
|
|
12
|
+
readonly name: "device-choice";
|
|
13
|
+
readonly path: "/step-2";
|
|
14
|
+
readonly filePath: "src/steps/step-02.tsx";
|
|
15
|
+
readonly componentKey: "stepTwo";
|
|
16
|
+
readonly type: "multi_select_choice";
|
|
17
|
+
readonly choice: {
|
|
18
|
+
readonly answerKey: "selectedDevices";
|
|
19
|
+
readonly allowEmpty: true;
|
|
20
|
+
};
|
|
21
|
+
readonly title: "Choose devices";
|
|
22
|
+
}, {
|
|
23
|
+
readonly id: "email-capture";
|
|
24
|
+
readonly name: "email-capture";
|
|
25
|
+
readonly path: "/email-capture";
|
|
26
|
+
readonly filePath: "src/steps/step-31-email-capture.tsx";
|
|
27
|
+
readonly componentKey: "stepEmailCapture";
|
|
28
|
+
readonly type: "form_input";
|
|
29
|
+
readonly title: "Email capture";
|
|
30
|
+
}, {
|
|
31
|
+
readonly id: "paywall";
|
|
32
|
+
readonly name: "paywall";
|
|
33
|
+
readonly path: "/paywall";
|
|
34
|
+
readonly filePath: "src/steps/step-32-paywall.tsx";
|
|
35
|
+
readonly componentKey: "stepPaywall";
|
|
36
|
+
readonly type: "paywall_offer";
|
|
37
|
+
readonly kind: "paywall";
|
|
38
|
+
readonly title: "Paywall";
|
|
39
|
+
}, {
|
|
40
|
+
readonly id: "paywall-b";
|
|
41
|
+
readonly name: "paywall-b";
|
|
42
|
+
readonly path: "/paywall-b";
|
|
43
|
+
readonly filePath: "src/steps/step-32b-paywall-b.tsx";
|
|
44
|
+
readonly componentKey: "stepPaywallB";
|
|
45
|
+
readonly type: "paywall_offer";
|
|
46
|
+
readonly kind: "paywall";
|
|
47
|
+
readonly title: "Paywall B";
|
|
48
|
+
}, {
|
|
49
|
+
readonly id: "subscription-started";
|
|
50
|
+
readonly name: "subscription-started";
|
|
51
|
+
readonly path: "/subscription-started";
|
|
52
|
+
readonly filePath: "src/steps/step-33-subscription-started.tsx";
|
|
53
|
+
readonly componentKey: "stepSubscriptionStarted";
|
|
54
|
+
readonly type: "complete_registration";
|
|
55
|
+
readonly kind: "subscription-handoff";
|
|
56
|
+
readonly title: "Subscription started";
|
|
57
|
+
}, {
|
|
58
|
+
readonly id: "manage-subscription";
|
|
59
|
+
readonly name: "manage-subscription";
|
|
60
|
+
readonly path: "/manage-subscription";
|
|
61
|
+
readonly filePath: "src/steps/step-35-manage-subscription.tsx";
|
|
62
|
+
readonly componentKey: "stepManageSubscription";
|
|
63
|
+
readonly type: "subscription_management";
|
|
64
|
+
readonly kind: "manage-subscription";
|
|
65
|
+
readonly title: "Manage subscription";
|
|
66
|
+
}, {
|
|
67
|
+
readonly id: "step-4";
|
|
68
|
+
readonly name: "scratchcard";
|
|
69
|
+
readonly path: "/step-4";
|
|
70
|
+
readonly filePath: "src/steps/step-04.tsx";
|
|
71
|
+
readonly componentKey: "stepFour";
|
|
72
|
+
readonly type: "paywall_offer";
|
|
73
|
+
readonly kind: "paywall";
|
|
74
|
+
readonly title: "Scratchcard";
|
|
75
|
+
}];
|
|
76
|
+
readonly templateArchitectureVersion: 1;
|
|
77
|
+
readonly stepContractVersion: 2;
|
|
78
|
+
readonly meta: {
|
|
79
|
+
readonly title: "Contract fixture";
|
|
80
|
+
readonly description: "A valid version 2 funnel contract fixture.";
|
|
81
|
+
};
|
|
82
|
+
readonly viewport: {
|
|
83
|
+
readonly width: 390;
|
|
84
|
+
readonly height: 844;
|
|
85
|
+
};
|
|
86
|
+
readonly assets: {};
|
|
87
|
+
readonly entryPoints: readonly [{
|
|
88
|
+
readonly id: "default";
|
|
89
|
+
readonly stepId: "step-1";
|
|
90
|
+
readonly isDefault: true;
|
|
91
|
+
}, {
|
|
92
|
+
readonly id: "manage-subscription";
|
|
93
|
+
readonly stepId: "manage-subscription";
|
|
94
|
+
}];
|
|
95
|
+
readonly edgesByStepId: {
|
|
96
|
+
readonly 'step-1': readonly [{
|
|
97
|
+
readonly toStepId: "step-2";
|
|
98
|
+
}];
|
|
99
|
+
readonly 'step-2': readonly [{
|
|
100
|
+
readonly toStepId: "email-capture";
|
|
101
|
+
}];
|
|
102
|
+
readonly 'email-capture': readonly [{
|
|
103
|
+
readonly toStepId: "paywall";
|
|
104
|
+
}];
|
|
105
|
+
readonly paywall: readonly [{
|
|
106
|
+
readonly toStepId: "subscription-started";
|
|
107
|
+
}];
|
|
108
|
+
readonly 'paywall-b': readonly [{
|
|
109
|
+
readonly toStepId: "subscription-started";
|
|
110
|
+
}];
|
|
111
|
+
};
|
|
112
|
+
readonly branches: readonly [{
|
|
113
|
+
readonly id: "device-owners";
|
|
114
|
+
readonly name: "Device owners";
|
|
115
|
+
readonly sourceStepId: "step-2";
|
|
116
|
+
readonly stepIds: readonly ["email-capture", "paywall"];
|
|
117
|
+
}];
|
|
118
|
+
readonly experiments: readonly [{
|
|
119
|
+
readonly experimentId: "paywall-offer";
|
|
120
|
+
readonly stepId: "paywall";
|
|
121
|
+
readonly variants: readonly [{
|
|
122
|
+
readonly variantKey: "control";
|
|
123
|
+
readonly routeToStepId: "paywall";
|
|
124
|
+
}, {
|
|
125
|
+
readonly variantKey: "variant-b";
|
|
126
|
+
readonly routeToStepId: "paywall-b";
|
|
127
|
+
}];
|
|
128
|
+
}];
|
|
129
|
+
};
|
|
130
|
+
export declare const wrongSubscriptionTerminalManifest: {
|
|
131
|
+
steps: ({
|
|
132
|
+
readonly id: "step-1";
|
|
133
|
+
readonly name: "step-one";
|
|
134
|
+
readonly path: "/step-1";
|
|
135
|
+
readonly filePath: "src/steps/step-01.tsx";
|
|
136
|
+
readonly componentKey: "stepOne";
|
|
137
|
+
readonly type: "intro_hero";
|
|
138
|
+
readonly title: "Introduction";
|
|
139
|
+
} | {
|
|
140
|
+
readonly id: "step-2";
|
|
141
|
+
readonly name: "device-choice";
|
|
142
|
+
readonly path: "/step-2";
|
|
143
|
+
readonly filePath: "src/steps/step-02.tsx";
|
|
144
|
+
readonly componentKey: "stepTwo";
|
|
145
|
+
readonly type: "multi_select_choice";
|
|
146
|
+
readonly choice: {
|
|
147
|
+
readonly answerKey: "selectedDevices";
|
|
148
|
+
readonly allowEmpty: true;
|
|
149
|
+
};
|
|
150
|
+
readonly title: "Choose devices";
|
|
151
|
+
} | {
|
|
152
|
+
readonly id: "email-capture";
|
|
153
|
+
readonly name: "email-capture";
|
|
154
|
+
readonly path: "/email-capture";
|
|
155
|
+
readonly filePath: "src/steps/step-31-email-capture.tsx";
|
|
156
|
+
readonly componentKey: "stepEmailCapture";
|
|
157
|
+
readonly type: "form_input";
|
|
158
|
+
readonly title: "Email capture";
|
|
159
|
+
} | {
|
|
160
|
+
readonly id: "paywall";
|
|
161
|
+
readonly name: "paywall";
|
|
162
|
+
readonly path: "/paywall";
|
|
163
|
+
readonly filePath: "src/steps/step-32-paywall.tsx";
|
|
164
|
+
readonly componentKey: "stepPaywall";
|
|
165
|
+
readonly type: "paywall_offer";
|
|
166
|
+
readonly kind: "paywall";
|
|
167
|
+
readonly title: "Paywall";
|
|
168
|
+
} | {
|
|
169
|
+
readonly id: "paywall-b";
|
|
170
|
+
readonly name: "paywall-b";
|
|
171
|
+
readonly path: "/paywall-b";
|
|
172
|
+
readonly filePath: "src/steps/step-32b-paywall-b.tsx";
|
|
173
|
+
readonly componentKey: "stepPaywallB";
|
|
174
|
+
readonly type: "paywall_offer";
|
|
175
|
+
readonly kind: "paywall";
|
|
176
|
+
readonly title: "Paywall B";
|
|
177
|
+
} | {
|
|
178
|
+
readonly id: "manage-subscription";
|
|
179
|
+
readonly name: "manage-subscription";
|
|
180
|
+
readonly path: "/manage-subscription";
|
|
181
|
+
readonly filePath: "src/steps/step-35-manage-subscription.tsx";
|
|
182
|
+
readonly componentKey: "stepManageSubscription";
|
|
183
|
+
readonly type: "subscription_management";
|
|
184
|
+
readonly kind: "manage-subscription";
|
|
185
|
+
readonly title: "Manage subscription";
|
|
186
|
+
} | {
|
|
187
|
+
type: "subscription_handoff";
|
|
188
|
+
id: "subscription-started";
|
|
189
|
+
name: "subscription-started";
|
|
190
|
+
path: "/subscription-started";
|
|
191
|
+
filePath: "src/steps/step-33-subscription-started.tsx";
|
|
192
|
+
componentKey: "stepSubscriptionStarted";
|
|
193
|
+
kind: "subscription-handoff";
|
|
194
|
+
title: "Subscription started";
|
|
195
|
+
})[];
|
|
196
|
+
templateArchitectureVersion: 1;
|
|
197
|
+
stepContractVersion: 2;
|
|
198
|
+
meta: {
|
|
199
|
+
readonly title: "Contract fixture";
|
|
200
|
+
readonly description: "A valid version 2 funnel contract fixture.";
|
|
201
|
+
};
|
|
202
|
+
viewport: {
|
|
203
|
+
readonly width: 390;
|
|
204
|
+
readonly height: 844;
|
|
205
|
+
};
|
|
206
|
+
assets: {};
|
|
207
|
+
entryPoints: readonly [{
|
|
208
|
+
readonly id: "default";
|
|
209
|
+
readonly stepId: "step-1";
|
|
210
|
+
readonly isDefault: true;
|
|
211
|
+
}, {
|
|
212
|
+
readonly id: "manage-subscription";
|
|
213
|
+
readonly stepId: "manage-subscription";
|
|
214
|
+
}];
|
|
215
|
+
edgesByStepId: {
|
|
216
|
+
readonly 'step-1': readonly [{
|
|
217
|
+
readonly toStepId: "step-2";
|
|
218
|
+
}];
|
|
219
|
+
readonly 'step-2': readonly [{
|
|
220
|
+
readonly toStepId: "email-capture";
|
|
221
|
+
}];
|
|
222
|
+
readonly 'email-capture': readonly [{
|
|
223
|
+
readonly toStepId: "paywall";
|
|
224
|
+
}];
|
|
225
|
+
readonly paywall: readonly [{
|
|
226
|
+
readonly toStepId: "subscription-started";
|
|
227
|
+
}];
|
|
228
|
+
readonly 'paywall-b': readonly [{
|
|
229
|
+
readonly toStepId: "subscription-started";
|
|
230
|
+
}];
|
|
231
|
+
};
|
|
232
|
+
branches: readonly [{
|
|
233
|
+
readonly id: "device-owners";
|
|
234
|
+
readonly name: "Device owners";
|
|
235
|
+
readonly sourceStepId: "step-2";
|
|
236
|
+
readonly stepIds: readonly ["email-capture", "paywall"];
|
|
237
|
+
}];
|
|
238
|
+
experiments: readonly [{
|
|
239
|
+
readonly experimentId: "paywall-offer";
|
|
240
|
+
readonly stepId: "paywall";
|
|
241
|
+
readonly variants: readonly [{
|
|
242
|
+
readonly variantKey: "control";
|
|
243
|
+
readonly routeToStepId: "paywall";
|
|
244
|
+
}, {
|
|
245
|
+
readonly variantKey: "variant-b";
|
|
246
|
+
readonly routeToStepId: "paywall-b";
|
|
247
|
+
}];
|
|
248
|
+
}];
|
|
249
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { validV2ProjectManifest } from './valid-v2-project.js';
|
|
2
|
+
export const scratchcardMisclassifiedManifest = Object.assign(Object.assign({}, validV2ProjectManifest), { steps: [
|
|
3
|
+
...validV2ProjectManifest.steps,
|
|
4
|
+
{
|
|
5
|
+
id: 'step-4',
|
|
6
|
+
name: 'scratchcard',
|
|
7
|
+
path: '/step-4',
|
|
8
|
+
filePath: 'src/steps/step-04.tsx',
|
|
9
|
+
componentKey: 'stepFour',
|
|
10
|
+
type: 'paywall_offer',
|
|
11
|
+
kind: 'paywall',
|
|
12
|
+
title: 'Scratchcard',
|
|
13
|
+
},
|
|
14
|
+
] });
|
|
15
|
+
export const wrongSubscriptionTerminalManifest = Object.assign(Object.assign({}, validV2ProjectManifest), { steps: validV2ProjectManifest.steps.map((step) => {
|
|
16
|
+
if (step.id !== 'subscription-started') {
|
|
17
|
+
return step;
|
|
18
|
+
}
|
|
19
|
+
return Object.assign(Object.assign({}, step), { type: 'subscription_handoff' });
|
|
20
|
+
}) });
|