@ordergroove/offers 2.44.0 → 2.44.1-alpha-PR-1167-2.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/bundle-report.html +42 -39
  2. package/dist/offers.js +69 -69
  3. package/dist/offers.js.map +4 -4
  4. package/package.json +2 -2
  5. package/src/components/FrequencyStatus.js +14 -10
  6. package/src/components/Offer.js +33 -14
  7. package/src/components/OptinButton.js +2 -2
  8. package/src/components/OptinSelect.js +6 -5
  9. package/src/components/OptinStatus.js +16 -9
  10. package/src/components/Price.js +3 -3
  11. package/src/components/SelectFrequency.js +11 -6
  12. package/src/components/TestWizard.js +45 -41
  13. package/src/components/UpsellModal.js +9 -3
  14. package/src/components/__tests__/Offer.spec.js +0 -19
  15. package/src/components/__tests__/OptinStatus.spec.js +17 -4
  16. package/src/core/__tests__/actions.spec.js +47 -1
  17. package/src/core/__tests__/base.spec.js +0 -77
  18. package/src/core/__tests__/experiments.spec.js +0 -3
  19. package/src/core/__tests__/offerRequest.spec.js +2 -1
  20. package/src/core/__tests__/selectors.spec.js +7 -7
  21. package/src/core/actions-preview.js +6 -3
  22. package/src/core/actions.js +22 -13
  23. package/src/core/base.js +0 -23
  24. package/src/core/offerRequest.js +1 -1
  25. package/src/core/{reducer.js → reducer.ts} +30 -10
  26. package/src/core/selectors.ts +215 -0
  27. package/src/core/types/api.ts +71 -0
  28. package/src/core/types/reducer.ts +94 -0
  29. package/src/core/types/utility.ts +1 -0
  30. package/src/core/utils.ts +32 -15
  31. package/src/make-api.js +1 -1
  32. package/src/shopify/__tests__/reducers/config.spec.js +603 -0
  33. package/src/shopify/__tests__/shopifyReducer.spec.js +69 -744
  34. package/src/shopify/__tests__/utils.spec.js +24 -1
  35. package/src/shopify/reducers/config.ts +185 -0
  36. package/src/shopify/shopifyMiddleware.ts +2 -9
  37. package/src/shopify/{shopifyReducer.js → shopifyReducer.ts} +50 -195
  38. package/src/shopify/utils.ts +25 -0
  39. package/src/core/selectors.js +0 -192
  40. package/src/types.ts +0 -16
package/src/core/utils.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import platform from '../platform';
2
2
  import { ENV_PROD, ENV_STAGING, STAGING_STATIC_HOST, STATIC_HOST } from './constants';
3
3
  import { isSameProduct } from './selectors';
4
+ import { ConfigState } from './types/reducer';
4
5
 
5
6
  export function onReady(fn) {
6
7
  if (document.readyState === 'loading') {
@@ -47,33 +48,45 @@ export const safeProductId = product => {
47
48
  return productId;
48
49
  };
49
50
 
51
+ type Frequencies = ConfigState['frequencies'];
52
+ type FrequenciesEveryPeriod = ConfigState['frequenciesEveryPeriod'];
53
+
50
54
  /**
51
55
  * Returns the OG frequency if platform is running on selling plans
52
- * @param initialFrequency
53
- * @param config
54
56
  */
55
- export const safeOgFrequency = (initialFrequency, config) => {
57
+ export const safeOgFrequency = (
58
+ initialFrequency: string | null,
59
+ frequencies: Frequencies,
60
+ frequenciesEveryPeriod: FrequenciesEveryPeriod
61
+ ) => {
56
62
  if (platform.shopify_selling_plans) {
57
- const ix = config.frequencies?.indexOf(initialFrequency);
58
- if (ix >= 0 && config.frequenciesEveryPeriod[ix]) {
59
- return config.frequenciesEveryPeriod[ix];
63
+ const ix = frequencies?.indexOf(initialFrequency);
64
+ if (ix >= 0 && frequenciesEveryPeriod[ix]) {
65
+ return frequenciesEveryPeriod[ix];
60
66
  }
61
67
  }
62
68
  return initialFrequency;
63
69
  };
64
70
 
65
- export const frequencyToSellingPlan = (ogFrequency, config) => {
71
+ export const frequencyToSellingPlan = (
72
+ ogFrequency: string,
73
+ frequencyConfig: {
74
+ frequencies: Frequencies;
75
+ frequenciesEveryPeriod: FrequenciesEveryPeriod;
76
+ }
77
+ ) => {
66
78
  // og frequency contains underscore
67
79
  if (!`${ogFrequency}`.includes('_')) return ogFrequency;
68
- const ix = config.frequenciesEveryPeriod?.indexOf(ogFrequency);
69
- if (ix >= 0 && config.frequenciesEveryPeriod[ix]) {
70
- return config.frequencies[ix];
80
+ const { frequencies, frequenciesEveryPeriod } = frequencyConfig;
81
+ const ix = frequenciesEveryPeriod?.indexOf(ogFrequency);
82
+ if (ix >= 0 && frequenciesEveryPeriod[ix]) {
83
+ return frequencies[ix];
71
84
  }
72
85
 
73
86
  // if we can't find the OG frequency, but we have selling plans, return the first selling plan
74
- if (config.frequencies?.length > 0 && config.frequenciesEveryPeriod?.length > 0) {
87
+ if (frequencies?.length > 0 && frequenciesEveryPeriod?.length > 0) {
75
88
  console.warn(`Unable to find selling plan match for frequency ${ogFrequency}; falling back to first selling plan`);
76
- return config.frequencies[0];
89
+ return frequencies[0];
77
90
  }
78
91
 
79
92
  return ogFrequency;
@@ -110,14 +123,18 @@ export function getCookieValue(cookieId) {
110
123
  const cookie = document.cookie.match(`(^|;) ?${cookieId}=([^;]*)(;|$)`);
111
124
  return cookie ? cookie[2] : null;
112
125
  }
113
- export const isOgFrequency = frequency => !!(frequency && frequency?.includes('_'));
126
+ export const isOgFrequency = (frequency: string) => !!(frequency && frequency?.includes('_'));
114
127
 
115
- export const getFirstSellingPlan = (frequencies = []) => frequencies?.[0] || null;
128
+ export const getFirstSellingPlan = (frequencies: string[] = []) => frequencies?.[0] || null;
116
129
 
117
130
  export const hasShopifySellingPlans = (sellingPlans = [], frequenciesEveryPeriod = []) =>
118
131
  !!(platform?.shopify_selling_plans && sellingPlans.length && frequenciesEveryPeriod.length);
119
132
 
120
- export const mapFrequencyToSellingPlan = (sellingPlans, frequenciesEveryPeriod, frequency) => {
133
+ export const mapFrequencyToSellingPlan = (
134
+ sellingPlans: string[],
135
+ frequenciesEveryPeriod: string[],
136
+ frequency: string
137
+ ) => {
121
138
  if (sellingPlans.length !== frequenciesEveryPeriod.length) {
122
139
  return null;
123
140
  }
package/src/make-api.js CHANGED
@@ -164,7 +164,7 @@ export default function makeApi(store) {
164
164
  * @param {*} merchantId
165
165
  * @param {*} env
166
166
  * @param {*} authUrl
167
- * @param {import('./types').MerchantSettings} merchantSettings
167
+ * @param {import('./core/types/api').MerchantSettings} merchantSettings
168
168
  */
169
169
  initialize(merchantId, env, authUrl, merchantSettings = {}) {
170
170
  // settings resolves once, before anything.
@@ -0,0 +1,603 @@
1
+ import config from '../../reducers/config';
2
+ import * as constants from '../../../core/constants';
3
+ import { DEFAULT_PAY_AS_YOU_GO_GROUP_NAME } from '../../utils';
4
+
5
+ describe('config', () => {
6
+ it('should set defaultFrequency to selling plan given selling plan exists for frequency given action SETUP_PRODUCT', () => {
7
+ const actual = config(
8
+ {
9
+ productFrequencies: {
10
+ 'yum product id': {
11
+ defaultFrequency: '1_2'
12
+ }
13
+ }
14
+ },
15
+ {
16
+ type: constants.SETUP_PRODUCT,
17
+ payload: {
18
+ offer: {
19
+ config: {}
20
+ },
21
+ product: {
22
+ variants: [
23
+ {
24
+ id: 'yum product id',
25
+ selling_plan_allocations: [
26
+ {
27
+ selling_plan_id: 'yum selling plan id 1',
28
+ selling_plan_group_id: 'yum selling plan group id'
29
+ },
30
+ {
31
+ selling_plan_id: 'yum selling plan id 2',
32
+ selling_plan_group_id: 'yum selling plan group id'
33
+ }
34
+ ]
35
+ }
36
+ ],
37
+ selling_plan_groups: [
38
+ {
39
+ name: DEFAULT_PAY_AS_YOU_GO_GROUP_NAME,
40
+ id: 'yum selling plan group id',
41
+ selling_plans: [
42
+ {
43
+ id: 'yum selling plan id 1',
44
+ options: [
45
+ {
46
+ value: '1 day'
47
+ }
48
+ ]
49
+ },
50
+ {
51
+ id: 'yum selling plan id 2',
52
+ options: [
53
+ {
54
+ value: '1 week'
55
+ }
56
+ ]
57
+ }
58
+ ]
59
+ }
60
+ ]
61
+ }
62
+ }
63
+ }
64
+ );
65
+
66
+ expect(actual).toEqual(
67
+ jasmine.objectContaining({
68
+ productFrequencies: {
69
+ 'yum product id': jasmine.objectContaining({
70
+ defaultFrequency: 'yum selling plan id 2'
71
+ })
72
+ }
73
+ })
74
+ );
75
+ });
76
+
77
+ it('should set defaultFrequency to first selling plan given selling plan does not exist for frequency given action SETUP_PRODUCT', () => {
78
+ const actual = config(
79
+ {
80
+ productFrequencies: {
81
+ 'yum product id': {
82
+ defaultFrequency: '1_3'
83
+ }
84
+ }
85
+ },
86
+ {
87
+ type: constants.SETUP_PRODUCT,
88
+ payload: {
89
+ offer: {
90
+ config: {}
91
+ },
92
+ product: {
93
+ variants: [
94
+ {
95
+ id: 'yum product id',
96
+ selling_plan_allocations: [
97
+ {
98
+ selling_plan_id: 'yum selling plan id 1',
99
+ selling_plan_group_id: 'yum selling plan group id'
100
+ },
101
+ {
102
+ selling_plan_id: 'yum selling plan id 2',
103
+ selling_plan_group_id: 'yum selling plan group id'
104
+ }
105
+ ]
106
+ }
107
+ ],
108
+ selling_plan_groups: [
109
+ {
110
+ name: DEFAULT_PAY_AS_YOU_GO_GROUP_NAME,
111
+ id: 'yum selling plan group id',
112
+ selling_plans: [
113
+ {
114
+ id: 'yum selling plan id 1',
115
+ options: [
116
+ {
117
+ value: '1 day'
118
+ }
119
+ ]
120
+ },
121
+ {
122
+ id: 'yum selling plan id 2',
123
+ options: [
124
+ {
125
+ value: '1 week'
126
+ }
127
+ ]
128
+ }
129
+ ]
130
+ }
131
+ ]
132
+ }
133
+ }
134
+ }
135
+ );
136
+
137
+ expect(actual).toEqual(
138
+ jasmine.objectContaining({
139
+ productFrequencies: {
140
+ 'yum product id': jasmine.objectContaining({
141
+ defaultFrequency: 'yum selling plan id 1'
142
+ })
143
+ }
144
+ })
145
+ );
146
+ });
147
+
148
+ it('should not change default frequency if is already a selling plan given action SETUP_PRODUCT', () => {
149
+ const actual = config(
150
+ {
151
+ productFrequencies: {
152
+ 'yum product id': {
153
+ defaultFrequency: 'yum selling plan id 5'
154
+ }
155
+ }
156
+ },
157
+ {
158
+ type: constants.SETUP_PRODUCT,
159
+ payload: {
160
+ offer: {
161
+ config: {}
162
+ },
163
+ product: {
164
+ variants: [
165
+ {
166
+ id: 'yum product id',
167
+ selling_plan_allocations: [
168
+ {
169
+ selling_plan_id: 'yum selling plan id 1',
170
+ selling_plan_group_id: 'yum selling plan group id'
171
+ },
172
+ {
173
+ selling_plan_id: 'yum selling plan id 2',
174
+ selling_plan_group_id: 'yum selling plan group id'
175
+ }
176
+ ]
177
+ }
178
+ ],
179
+ selling_plan_groups: [
180
+ {
181
+ name: DEFAULT_PAY_AS_YOU_GO_GROUP_NAME,
182
+ id: 'yum selling plan group id',
183
+ selling_plans: [
184
+ {
185
+ id: 'yum selling plan id 1',
186
+ options: [
187
+ {
188
+ value: '1 day'
189
+ }
190
+ ]
191
+ },
192
+ {
193
+ id: 'yum selling plan id 2',
194
+ options: [
195
+ {
196
+ value: '1 week'
197
+ }
198
+ ]
199
+ }
200
+ ]
201
+ }
202
+ ]
203
+ }
204
+ }
205
+ }
206
+ );
207
+
208
+ expect(actual).toEqual(
209
+ jasmine.objectContaining({
210
+ productFrequencies: {
211
+ 'yum product id': jasmine.objectContaining({
212
+ defaultFrequency: 'yum selling plan id 5'
213
+ })
214
+ }
215
+ })
216
+ );
217
+ });
218
+
219
+ const defaultSellingPlanPayload = {
220
+ product: {
221
+ variants: [
222
+ {
223
+ id: 'yum product id',
224
+ selling_plan_allocations: [
225
+ {
226
+ selling_plan_id: 'yum selling plan id 1',
227
+ selling_plan_group_id: 'yum selling plan group id'
228
+ },
229
+ {
230
+ selling_plan_id: 'yum selling plan id 2',
231
+ selling_plan_group_id: 'yum selling plan group id'
232
+ }
233
+ ]
234
+ }
235
+ ],
236
+ selling_plan_groups: [
237
+ {
238
+ id: 'yum selling plan group id',
239
+ name: DEFAULT_PAY_AS_YOU_GO_GROUP_NAME,
240
+ selling_plans: [
241
+ {
242
+ id: 'yum selling plan id 1'
243
+ },
244
+ {
245
+ id: 'yum selling plan id 2'
246
+ }
247
+ ]
248
+ }
249
+ ]
250
+ }
251
+ };
252
+
253
+ it('should return selling plan ids as frequencies given action SETUP_PRODUCT', () => {
254
+ const actual = config(
255
+ {
256
+ productFrequencies: {}
257
+ },
258
+ {
259
+ type: constants.SETUP_PRODUCT,
260
+ payload: defaultSellingPlanPayload
261
+ }
262
+ );
263
+
264
+ expect(actual).toEqual(
265
+ jasmine.objectContaining({
266
+ productFrequencies: {
267
+ 'yum product id': jasmine.objectContaining({
268
+ frequencies: ['yum selling plan id 1', 'yum selling plan id 2']
269
+ })
270
+ }
271
+ })
272
+ );
273
+ });
274
+
275
+ it('should return values of first OG selling plan group as frequencies text given action SETUP_PRODUCT', () => {
276
+ const actual = config(
277
+ {
278
+ productFrequencies: {}
279
+ },
280
+ {
281
+ type: constants.SETUP_PRODUCT,
282
+ payload: {
283
+ product: {
284
+ variants: [
285
+ {
286
+ id: 'yum product id',
287
+ selling_plan_allocations: [
288
+ {
289
+ selling_plan_id: 'old yum selling plan id',
290
+ selling_plan_group_id: 'yum old selling plan group id'
291
+ },
292
+ {
293
+ selling_plan_id: 'yum selling plan id',
294
+ selling_plan_group_id: 'yum default selling plan group id'
295
+ }
296
+ ]
297
+ }
298
+ ],
299
+ selling_plan_groups: [
300
+ {
301
+ name: 'Old Selling Plan Group',
302
+ id: 'yum old selling plan group id',
303
+ options: [{ values: 'old yum values' }],
304
+ selling_plans: [
305
+ {
306
+ id: 'old yum selling plan id'
307
+ }
308
+ ]
309
+ },
310
+ {
311
+ name: DEFAULT_PAY_AS_YOU_GO_GROUP_NAME,
312
+ id: 'yum default selling plan group id',
313
+ options: [{ values: 'yum values' }],
314
+ selling_plans: [
315
+ {
316
+ id: 'yum selling plan id'
317
+ }
318
+ ]
319
+ }
320
+ ]
321
+ }
322
+ }
323
+ }
324
+ );
325
+
326
+ expect(actual).toEqual(
327
+ jasmine.objectContaining({
328
+ productFrequencies: {
329
+ 'yum product id': jasmine.objectContaining({
330
+ frequenciesText: 'yum values'
331
+ })
332
+ }
333
+ })
334
+ );
335
+ });
336
+
337
+ it('should use product-specific frequency selling plan groups if present', () => {
338
+ const actual = config(
339
+ {
340
+ productFrequencies: {}
341
+ },
342
+ {
343
+ type: constants.SETUP_PRODUCT,
344
+ payload: {
345
+ product: {
346
+ variants: [
347
+ {
348
+ id: 'yum product id',
349
+ selling_plan_allocations: [
350
+ {
351
+ selling_plan_group_id: 'og psfl group id',
352
+ selling_plan_id: 'psfl-id-1'
353
+ },
354
+ {
355
+ selling_plan_group_id: 'og psfl group id',
356
+ selling_plan_id: 'psfl-id-2'
357
+ },
358
+ {
359
+ selling_plan_group_id: 'og psfl group id',
360
+ selling_plan_id: 'psfl-id-3'
361
+ },
362
+ {
363
+ selling_plan_group_id: 'og regular group id',
364
+ selling_plan_id: 'regular-id-1'
365
+ },
366
+ {
367
+ selling_plan_group_id: 'og regular group id',
368
+ selling_plan_id: 'regular-id-2'
369
+ },
370
+ {
371
+ selling_plan_group_id: 'og regular group id',
372
+ selling_plan_id: 'regular-id-3'
373
+ }
374
+ ]
375
+ }
376
+ ],
377
+ selling_plan_groups: [
378
+ {
379
+ name: 'og_psfl_2m4m6m',
380
+ id: 'og psfl group id',
381
+ options: [
382
+ {
383
+ values: ['2 months', '4 months', '6 months']
384
+ }
385
+ ],
386
+ selling_plans: [
387
+ {
388
+ id: 'psfl-id-1'
389
+ },
390
+ {
391
+ id: 'psfl-id-2'
392
+ },
393
+ {
394
+ id: 'psfl-id-3'
395
+ }
396
+ ]
397
+ },
398
+ {
399
+ name: 'Subscribe and Save',
400
+ id: 'og regular group id',
401
+ options: [
402
+ {
403
+ values: ['month', '2 months', '3 months']
404
+ }
405
+ ],
406
+ selling_plans: [
407
+ {
408
+ id: 'regular-id-1'
409
+ },
410
+ {
411
+ id: 'regular-id-2'
412
+ },
413
+ {
414
+ id: 'regular-id-3'
415
+ }
416
+ ]
417
+ }
418
+ ]
419
+ }
420
+ }
421
+ }
422
+ );
423
+
424
+ expect(actual).toEqual(
425
+ jasmine.objectContaining({
426
+ productFrequencies: {
427
+ 'yum product id': jasmine.objectContaining({
428
+ frequencies: ['psfl-id-1', 'psfl-id-2', 'psfl-id-3']
429
+ })
430
+ }
431
+ })
432
+ );
433
+ });
434
+
435
+ it('should set prepaidSellingPlans', () => {
436
+ const sellingPlanGroups = [
437
+ {
438
+ name: 'Prepaid-43017264201944',
439
+ selling_plans: [
440
+ {
441
+ id: 2146042072,
442
+ name: 'Delivered every 2 months, prepaid for 4 shipments',
443
+ options: [
444
+ { name: 'Delivery every', position: 1, value: 'PREPAID-2 months' },
445
+ { name: 'Shipment amount', position: 2, value: '4 shipments' }
446
+ ]
447
+ }
448
+ ]
449
+ }
450
+ ];
451
+ const actual = config(
452
+ {},
453
+ {
454
+ type: constants.SETUP_PRODUCT,
455
+ payload: {
456
+ product: {
457
+ selling_plan_groups: sellingPlanGroups
458
+ }
459
+ }
460
+ }
461
+ );
462
+ expect(actual).toEqual(
463
+ jasmine.objectContaining({
464
+ prepaidSellingPlans: {
465
+ 43017264201944: [
466
+ {
467
+ numberShipments: 4,
468
+ sellingPlan: '2146042072'
469
+ }
470
+ ]
471
+ }
472
+ })
473
+ );
474
+ });
475
+
476
+ it('should add prepaidSellingPlans on top of old ones', () => {
477
+ const sellingPlanGroups = [
478
+ {
479
+ name: 'Prepaid-43017264201946',
480
+ selling_plans: [
481
+ {
482
+ id: 2146042072,
483
+ name: 'Delivered every 1 months, prepaid for 3 shipments',
484
+ options: [
485
+ { name: 'Delivery every', position: 1, value: 'PREPAID-1 months' },
486
+ { name: 'Shipment amount', position: 2, value: '3 shipments' }
487
+ ]
488
+ }
489
+ ]
490
+ },
491
+ {
492
+ name: 'Prepaid-43017264201945',
493
+ selling_plans: [
494
+ {
495
+ id: 2146042073,
496
+ name: 'Delivered every 3 months, prepaid for 5 shipments',
497
+ options: [
498
+ { name: 'Delivery every', position: 1, value: 'PREPAID-3 months' },
499
+ { name: 'Shipment amount', position: 2, value: '5 shipments' }
500
+ ]
501
+ }
502
+ ]
503
+ }
504
+ ];
505
+ const actual = config(
506
+ {
507
+ prepaidSellingPlans: {
508
+ 43017264201946: [
509
+ {
510
+ numberShipments: 1,
511
+ sellingPlan: '2146042072'
512
+ }
513
+ ],
514
+ 43017264201944: [
515
+ {
516
+ numberShipments: 4,
517
+ sellingPlan: '2146042071'
518
+ }
519
+ ]
520
+ }
521
+ },
522
+ {
523
+ type: constants.SETUP_PRODUCT,
524
+ payload: {
525
+ product: {
526
+ selling_plan_groups: sellingPlanGroups
527
+ }
528
+ }
529
+ }
530
+ );
531
+ expect(actual).toEqual(
532
+ jasmine.objectContaining({
533
+ prepaidSellingPlans: {
534
+ 43017264201944: [
535
+ {
536
+ numberShipments: 4,
537
+ sellingPlan: '2146042071'
538
+ }
539
+ ],
540
+ 43017264201946: [
541
+ {
542
+ numberShipments: 3,
543
+ sellingPlan: '2146042072'
544
+ }
545
+ ],
546
+ 43017264201945: [
547
+ {
548
+ numberShipments: 5,
549
+ sellingPlan: '2146042073'
550
+ }
551
+ ]
552
+ }
553
+ })
554
+ );
555
+ });
556
+
557
+ it('should return unmodified state given unsupported action', () => {
558
+ const actual = config(
559
+ { 'yum existing key': 'yum existing value' },
560
+ {
561
+ type: 'yum unsupported action',
562
+ payload: {}
563
+ }
564
+ );
565
+
566
+ expect(actual).toEqual({ 'yum existing key': 'yum existing value' });
567
+ });
568
+
569
+ it('should not set prepaid selling plan as a defaultFrequency', () => {
570
+ const initial = {
571
+ prepaidSellingPlans: {
572
+ 123: [
573
+ {
574
+ sellingPlan: 'prepaid selling plan'
575
+ }
576
+ ]
577
+ },
578
+ productFrequencies: {
579
+ 123: {
580
+ frequencies: ['yum selling plan id 1', 'yum selling plan id 4'],
581
+ frequenciesEveryPeriod: ['1_1', '1_4']
582
+ }
583
+ }
584
+ };
585
+ const actual = config(initial, {
586
+ type: constants.RECEIVE_OFFER,
587
+ payload: {
588
+ offer: {
589
+ product: { id: 123 },
590
+ defaultFrequency: 'prepaid selling plan'
591
+ }
592
+ }
593
+ });
594
+ expect(actual).toEqual({
595
+ ...initial,
596
+ productFrequencies: {
597
+ 123: jasmine.objectContaining({
598
+ defaultFrequency: 'yum selling plan id 1'
599
+ })
600
+ }
601
+ });
602
+ });
603
+ });