@ordergroove/offers 2.44.0 → 2.44.1-alpha-PR-1166-3.30

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 (38) hide show
  1. package/dist/bundle-report.html +52 -49
  2. package/dist/offers.js +38 -38
  3. package/dist/offers.js.map +4 -4
  4. package/package.json +2 -2
  5. package/src/components/FrequencyStatus.js +13 -10
  6. package/src/components/Offer.js +33 -14
  7. package/src/components/OptinButton.js +2 -2
  8. package/src/components/OptinSelect.js +5 -5
  9. package/src/components/OptinStatus.js +15 -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__/offerRequest.spec.js +2 -1
  19. package/src/core/__tests__/selectors.spec.js +7 -7
  20. package/src/core/actions-preview.js +6 -3
  21. package/src/core/actions.js +22 -13
  22. package/src/core/base.js +0 -23
  23. package/src/core/offerRequest.js +1 -1
  24. package/src/core/{reducer.js → reducer.ts} +30 -10
  25. package/src/core/{selectors.js → selectors.ts} +73 -57
  26. package/src/core/types/api.ts +71 -0
  27. package/src/core/types/reducer.ts +95 -0
  28. package/src/core/types/utility.ts +1 -0
  29. package/src/core/utils.ts +32 -15
  30. package/src/make-api.js +1 -1
  31. package/src/shopify/__tests__/reducers/config.spec.js +497 -0
  32. package/src/shopify/__tests__/shopifyReducer.spec.js +65 -610
  33. package/src/shopify/__tests__/utils.spec.js +24 -1
  34. package/src/shopify/reducers/config.ts +223 -0
  35. package/src/shopify/shopifyMiddleware.ts +2 -9
  36. package/src/shopify/{shopifyReducer.js → shopifyReducer.ts} +45 -177
  37. package/src/shopify/utils.ts +25 -0
  38. package/src/types.ts +0 -16
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ordergroove/offers",
3
- "version": "2.44.0",
3
+ "version": "2.44.1-alpha-PR-1166-3.30+930d1b8c",
4
4
  "description": "offer state component",
5
5
  "author": "Eugenio Lattanzio <eugenio63@gmail.com>",
6
6
  "homepage": "https://github.com/ordergroove/plush-toys#readme",
@@ -49,5 +49,5 @@
49
49
  "@ordergroove/offers-templates": "^0.9.7",
50
50
  "@types/lodash.memoize": "^4.1.9"
51
51
  },
52
- "gitHead": "6920106790e66edb68d2f452825f9dc4be5911d4"
52
+ "gitHead": "930d1b8c0307645ab97fbbc3a59ed98fae26bd6d"
53
53
  }
@@ -1,9 +1,12 @@
1
1
  import { html, css } from 'lit-element';
2
2
  import {
3
- makeProductFrequencySelector,
3
+ makeProductFrequencyOptedInSelector,
4
4
  makeOptedinSelector,
5
- configSelector,
5
+ getFallbackValue,
6
6
  templatesSelector,
7
+ makeProductSpecificDefaultFrequencySelector,
8
+ makeProductFrequenciesSelector,
9
+ makeProductFrequencyOptionsSelector,
7
10
  makeProductDefaultFrequencySelector
8
11
  } from '../core/selectors';
9
12
  import { connect } from '../core/connect';
@@ -89,14 +92,14 @@ export class FrequencyStatus extends withProduct(TemplateElement) {
89
92
 
90
93
  export const mapStateToProps = (state, ownProps) => ({
91
94
  subscribed: makeOptedinSelector(ownProps.product)(state),
92
- frequency: makeProductFrequencySelector(ownProps.product)(state),
93
- productDefaultFrequency: makeProductDefaultFrequencySelector((ownProps.product || {}).id)(state),
94
- configDefaultFrequency: state.config?.defaultFrequency,
95
- frequenciesText: state.config?.frequenciesText,
96
- config: state.config,
97
- ...configSelector(state, ownProps, 'frequencies'),
98
- ...configSelector(state, ownProps, 'defaultFrequency'),
99
- ...templatesSelector(state, ownProps)
95
+ frequency: makeProductFrequencyOptedInSelector(ownProps.product)(state),
96
+ productDefaultFrequency: makeProductSpecificDefaultFrequencySelector((ownProps.product || {}).id)(state),
97
+ frequencies:
98
+ makeProductFrequencyOptionsSelector(ownProps.product?.id)(state) || getFallbackValue(ownProps, 'frequencies'),
99
+ defaultFrequency:
100
+ makeProductDefaultFrequencySelector(ownProps.product?.id)(state) || getFallbackValue(ownProps, 'defaultFrequency'),
101
+ ...templatesSelector(state, ownProps),
102
+ productFrequencies: makeProductFrequenciesSelector(ownProps.product)(state)
100
103
  });
101
104
 
102
105
  export const ConnectedFrequencyStatus = connect(mapStateToProps)(FrequencyStatus);
@@ -12,14 +12,16 @@ import {
12
12
  } from '../core/actions';
13
13
  import {
14
14
  isSameProduct,
15
- configSelector,
15
+ getFallbackValue,
16
16
  templatesSelector,
17
17
  makeOptedoutSelector,
18
- makeProductFrequencySelector,
19
- makeProductDefaultFrequencySelector,
18
+ makeProductFrequencyOptedInSelector,
19
+ makeProductSpecificDefaultFrequencySelector,
20
20
  optedinSelector,
21
21
  autoshipSelector,
22
- makeOptedinSelector
22
+ makeOptedinSelector,
23
+ kebabCase,
24
+ makeProductDefaultFrequencySelector
23
25
  } from '../core/selectors';
24
26
  import { product as productProp, auth as authProp } from '../core/props';
25
27
  import { TemplateElement } from '../core/base';
@@ -339,12 +341,6 @@ export class Offer extends TemplateElement {
339
341
  }
340
342
  }
341
343
 
342
- // product-specific frequency lists are not supported in cart offers
343
- if (this.isCart && this.config?.hasProductSpecificFrequencies) {
344
- logProductSpecificFrequencyListWarning();
345
- return false;
346
- }
347
-
348
344
  return true;
349
345
  }
350
346
 
@@ -368,7 +364,27 @@ export class Offer extends TemplateElement {
368
364
  return freq.currentFrequency;
369
365
  }
370
366
 
371
- return this.getOption('defaultFrequency');
367
+ // not certain if this logic was used or not -- the following code was inlined from a shared function that was only used here
368
+ const attributeValue = this.getValueFromAttribute('defaultFrequency');
369
+ if (attributeValue) {
370
+ return attributeValue;
371
+ }
372
+
373
+ if (this.template && this.template.config && typeof this.template.config.defaultFrequency !== 'undefined') {
374
+ return this.template.config.defaultFrequency;
375
+ }
376
+
377
+ return this.configDefaultFrequency;
378
+ }
379
+
380
+ getValueFromAttribute(key) {
381
+ const attrName = kebabCase(key);
382
+ if (this.hasAttribute(attrName)) {
383
+ const attr = this.getAttribute(attrName);
384
+ if (attr.toString().toLowerCase() === 'true') return true;
385
+ if (attr.toString().toLowerCase() === 'false') return false;
386
+ return attr;
387
+ }
372
388
  }
373
389
  }
374
390
 
@@ -376,9 +392,12 @@ export const mapStateToProps = (state, ownProps) => ({
376
392
  config: state.config,
377
393
  auth: state.auth,
378
394
  offerId: ((state.productOffer || {})[(ownProps.product || {}).id] || [])[0],
379
- productFrequency: makeProductFrequencySelector(ownProps.product)(state),
380
- productDefaultFrequency: makeProductDefaultFrequencySelector((ownProps.product || {}).id)(state),
381
- ...configSelector(state, ownProps, 'autoshipByDefault', autoshipSelector(state)[(ownProps.product || {}).id]),
395
+ configDefaultFrequency: makeProductDefaultFrequencySelector(ownProps.product?.id)(state),
396
+ productFrequency: makeProductFrequencyOptedInSelector(ownProps.product)(state),
397
+ productDefaultFrequency: makeProductSpecificDefaultFrequencySelector((ownProps.product || {}).id)(state),
398
+ autoshipByDefault:
399
+ (state.config && state.config.autoshipByDefault) ||
400
+ getFallbackValue(ownProps, 'autoshipByDefault', autoshipSelector(state)[(ownProps.product || {}).id]),
382
401
  ...(makeOptedoutSelector(ownProps.product)(state) && { autoshipByDefault: false }),
383
402
  optedin: optedinSelector(state),
384
403
  subscribed: makeOptedinSelector(ownProps.product)(state),
@@ -21,7 +21,7 @@ export class OptinButton extends OptinStatus {
21
21
  if (changed.has('subscribed') || changed.has('frequencies')) {
22
22
  if (platform.shopify_selling_plans && this.store) {
23
23
  let buttonFreq = this.getAttribute('default-frequency');
24
- buttonFreq = frequencyToSellingPlan(buttonFreq, this.store.getState().config);
24
+ buttonFreq = frequencyToSellingPlan(buttonFreq, this.productFrequencies);
25
25
  this.sellingPlanFreq = buttonFreq;
26
26
  }
27
27
  this.frequencyMatch = this.frequency === this.optinFrequency;
@@ -40,7 +40,7 @@ export class OptinButton extends OptinStatus {
40
40
  freq = this.offer ? this.offer.defaultFrequency : this.defaultFrequency;
41
41
  }
42
42
  if (platform.shopify_selling_plans && this.store) {
43
- freq = frequencyToSellingPlan(freq, this.store.getState().config);
43
+ freq = frequencyToSellingPlan(freq, this.productFrequencies);
44
44
  }
45
45
 
46
46
  return freq;
@@ -5,7 +5,7 @@ import { OptinStatus, mapStateToProps as mapStateToPropsOptinStatus } from './Op
5
5
  import { mapStateToProps, frequencyText } from './FrequencyStatus';
6
6
  import { connect } from '../core/connect';
7
7
  import { defaultFrequency } from '../core/props';
8
- import { configSelector } from '../core/selectors';
8
+ import { getFallbackValue, makeProductFrequencyOptionsSelector } from '../core/selectors';
9
9
 
10
10
  export class OptinSelect extends withChildOptions(OptinStatus) {
11
11
  static get properties() {
@@ -51,14 +51,13 @@ export class OptinSelect extends withChildOptions(OptinStatus) {
51
51
  const { options: childOptions } = this.childOptions;
52
52
  let options;
53
53
  if (this.frequencies?.length) {
54
+ const { frequenciesText } = this.productFrequencies;
54
55
  options = [
55
56
  ...([childOptions.find(option => option.value === 'optedOut')] || []),
56
57
  ...this.frequencies.map((value, ix) => ({
57
58
  value,
58
59
  text:
59
- this.frequenciesText && ix in this.frequenciesText
60
- ? this.frequenciesText[ix]
61
- : frequencyText(value, this.defaultFrequency)
60
+ frequenciesText && ix in frequenciesText ? frequenciesText[ix] : frequencyText(value, this.defaultFrequency)
62
61
  }))
63
62
  ];
64
63
  } else {
@@ -79,7 +78,8 @@ export const ConnectedOptinSelect = connect(
79
78
  (state, ownProps) => ({
80
79
  ...mapStateToPropsOptinStatus(state, ownProps),
81
80
  ...mapStateToProps(state, ownProps),
82
- ...configSelector(state, ownProps, 'frequencies')
81
+ frequencies:
82
+ makeProductFrequencyOptionsSelector(ownProps.product?.id)(state) || getFallbackValue(ownProps, 'frequencies')
83
83
  }),
84
84
  { productChangeFrequency, optoutProduct }
85
85
  )(OptinSelect);
@@ -1,11 +1,14 @@
1
1
  import { html, css } from 'lit-element';
2
2
  import {
3
3
  makeOptedinSelector,
4
- makeProductDefaultFrequencySelector,
4
+ makeProductSpecificDefaultFrequencySelector,
5
5
  makeProductPrepaidShipmentsOptedInSelector,
6
- makeProductFrequencySelector,
7
- configSelector,
8
- templatesSelector
6
+ makeProductFrequencyOptedInSelector,
7
+ getFallbackValue,
8
+ templatesSelector,
9
+ makeProductFrequenciesSelector,
10
+ makeProductDefaultFrequencySelector,
11
+ makeProductFrequencyOptionsSelector
9
12
  } from '../core/selectors';
10
13
  import { connect } from '../core/connect';
11
14
  import { subscribed } from '../core/props';
@@ -130,12 +133,15 @@ export class OptinStatus extends withProduct(TemplateElement) {
130
133
 
131
134
  export const mapStateToProps = (state, ownProps = {}) => ({
132
135
  subscribed: makeOptedinSelector(ownProps.product)(state),
133
- frequency: makeProductFrequencySelector(ownProps.product)(state),
134
- productDefaultFrequency: makeProductDefaultFrequencySelector((ownProps.product || {}).id)(state),
136
+ frequency: makeProductFrequencyOptedInSelector(ownProps.product)(state),
137
+ productDefaultFrequency: makeProductSpecificDefaultFrequencySelector((ownProps.product || {}).id)(state),
135
138
  prepaidShipmentsOptedIn: makeProductPrepaidShipmentsOptedInSelector(ownProps.product)(state),
136
- ...configSelector(state, ownProps, 'defaultFrequency'),
137
- ...configSelector(state, ownProps, 'frequencies'),
138
- ...templatesSelector(state, ownProps)
139
+ defaultFrequency:
140
+ makeProductDefaultFrequencySelector(ownProps.product?.id)(state) || getFallbackValue(ownProps, 'defaultFrequency'),
141
+ frequencies:
142
+ makeProductFrequencyOptionsSelector(ownProps.product?.id)(state) || getFallbackValue(ownProps, 'frequencies'),
143
+ ...templatesSelector(state, ownProps),
144
+ productFrequencies: makeProductFrequenciesSelector(ownProps.product)(state)
139
145
  });
140
146
 
141
147
  export const ConnectedOptinStatus = connect(mapStateToProps)(OptinStatus);
@@ -3,7 +3,7 @@ import { connect } from '../core/connect';
3
3
 
4
4
  import { withProduct } from '../core/resolveProperties';
5
5
  import { TemplateElement } from '../core/base';
6
- import { makeProductFrequencySelector } from '../core/selectors';
6
+ import { makeProductDefaultFrequencySelector, makeProductFrequencyOptedInSelector } from '../core/selectors';
7
7
  import { safeProductId } from '../core/utils';
8
8
 
9
9
  export class Price extends withProduct(TemplateElement) {
@@ -81,8 +81,8 @@ export class Price extends withProduct(TemplateElement) {
81
81
  }
82
82
  const mapStateToProps = (state, ownProps) => ({
83
83
  productPlans: state.productPlans,
84
- configDefaultFrequency: state.config?.defaultFrequency,
85
- frequency: makeProductFrequencySelector(ownProps.product)(state)
84
+ configDefaultFrequency: makeProductDefaultFrequencySelector(ownProps.product?.id)(state),
85
+ frequency: makeProductFrequencyOptedInSelector(ownProps.product)(state)
86
86
  });
87
87
 
88
88
  export default connect(mapStateToProps)(Price);
@@ -65,8 +65,12 @@ export class SelectFrequency extends withChildOptions(FrequencyStatus) {
65
65
  }
66
66
 
67
67
  // this runs for shopify selling plans translated to freq
68
- if (this.config?.frequencies?.length && result && this.config?.frequenciesEveryPeriod?.length) {
69
- return frequencyToSellingPlan(result, this.config);
68
+ if (
69
+ this.productFrequencies?.frequencies?.length &&
70
+ result &&
71
+ this.productFrequencies?.frequenciesEveryPeriod?.length
72
+ ) {
73
+ return frequencyToSellingPlan(result, this.productFrequencies);
70
74
  }
71
75
 
72
76
  return result;
@@ -90,10 +94,11 @@ export class SelectFrequency extends withChildOptions(FrequencyStatus) {
90
94
  if (this.frequencies?.length) {
91
95
  options = this.frequencies.map((value, ix) => {
92
96
  let text;
93
- if (this.config.frequenciesEveryPeriod && ix in this.config.frequenciesEveryPeriod) {
94
- text = frequencyText(this.config.frequenciesEveryPeriod[ix], defaultFrequency);
95
- } else if (this.frequenciesText && ix in this.frequenciesText) {
96
- text = this.frequenciesText[ix];
97
+ const { frequenciesEveryPeriod, frequenciesText } = this.productFrequencies;
98
+ if (frequenciesEveryPeriod && ix in frequenciesEveryPeriod) {
99
+ text = frequencyText(frequenciesEveryPeriod[ix], defaultFrequency);
100
+ } else if (frequenciesText && ix in frequenciesText) {
101
+ text = frequenciesText[ix];
97
102
  } else {
98
103
  text = frequencyText(value, this.defaultFrequency);
99
104
  }
@@ -194,50 +194,54 @@ export class TestWizard extends LitElement {
194
194
  const offer = document.querySelectorAll('og-offer')[ix];
195
195
  const productId = offer.product.id;
196
196
  offer.store.dispatch(
197
- actions.receiveOffer({
198
- in_stock: { [productId]: inStock },
199
- eligibility_groups: { [productId]: groups },
200
- result: 'success',
201
- autoship: { [productId]: autoship },
202
- module_view: { regular: '58a01e9aacbe40389b5c7325d79091bb' },
203
- modifiers: {},
204
- incentives_display: {
205
- '47c01e9aacbe40389b5c7325d79091aa': {
206
- field: 'sub_total',
207
- object: 'order',
208
- type: 'Discount Percent',
209
- value: 5
197
+ actions.receiveOffer(
198
+ {
199
+ in_stock: { [productId]: inStock },
200
+ eligibility_groups: { [productId]: groups },
201
+ result: 'success',
202
+ autoship: { [productId]: autoship },
203
+ module_view: { regular: '58a01e9aacbe40389b5c7325d79091bb' },
204
+ modifiers: {},
205
+ incentives_display: {
206
+ '47c01e9aacbe40389b5c7325d79091aa': {
207
+ field: 'sub_total',
208
+ object: 'order',
209
+ type: 'Discount Percent',
210
+ value: 5
211
+ },
212
+ e6534b9d877f41e586c37b7d8abc3a58: {
213
+ field: 'total_price',
214
+ object: 'item',
215
+ type: 'Discount Percent',
216
+ value: 5
217
+ },
218
+ f35e842710b24929922db4a529eecd40: {
219
+ field: 'total_price',
220
+ object: 'item',
221
+ type: 'Discount Percent',
222
+ value: 10
223
+ },
224
+ '5be321d7c17f4e18a757212b9a20bfcc': {
225
+ field: 'total_price',
226
+ object: 'item',
227
+ type: 'Discount Percent',
228
+ value: 1
229
+ }
210
230
  },
211
- e6534b9d877f41e586c37b7d8abc3a58: {
212
- field: 'total_price',
213
- object: 'item',
214
- type: 'Discount Percent',
215
- value: 5
216
- },
217
- f35e842710b24929922db4a529eecd40: {
218
- field: 'total_price',
219
- object: 'item',
220
- type: 'Discount Percent',
221
- value: 10
222
- },
223
- '5be321d7c17f4e18a757212b9a20bfcc': {
224
- field: 'total_price',
225
- object: 'item',
226
- type: 'Discount Percent',
227
- value: 1
231
+ incentives: {
232
+ [productId]: {
233
+ initial: ['5be321d7c17f4e18a757212b9a20bfcc'],
234
+ ongoing: [
235
+ 'e6534b9d877f41e586c37b7d8abc3a58',
236
+ '47c01e9aacbe40389b5c7325d79091aa',
237
+ 'f35e842710b24929922db4a529eecd40'
238
+ ]
239
+ }
228
240
  }
229
241
  },
230
- incentives: {
231
- [productId]: {
232
- initial: ['5be321d7c17f4e18a757212b9a20bfcc'],
233
- ongoing: [
234
- 'e6534b9d877f41e586c37b7d8abc3a58',
235
- '47c01e9aacbe40389b5c7325d79091aa',
236
- 'f35e842710b24929922db4a529eecd40'
237
- ]
238
- }
239
- }
240
- })
242
+ {},
243
+ productId
244
+ )
241
245
  );
242
246
  this.runTests();
243
247
  };
@@ -2,7 +2,12 @@ import { html } from 'lit-element';
2
2
  import { concludeUpsell, createIu } from '../core/actions';
3
3
  import { connect } from '../core/connect';
4
4
  import { auth, defaultFrequency } from '../core/props';
5
- import { makeOptedinSelector, makeProductFrequencySelector, configSelector } from '../core/selectors';
5
+ import {
6
+ makeOptedinSelector,
7
+ makeProductFrequencyOptedInSelector,
8
+ getFallbackValue,
9
+ makeProductDefaultFrequencySelector
10
+ } from '../core/selectors';
6
11
  import { withProduct } from '../core/resolveProperties';
7
12
  import { TemplateElement } from '../core/base';
8
13
 
@@ -102,8 +107,9 @@ export const mapStateToProps = (state, ownProps) => ({
102
107
  auth: state.auth,
103
108
  offerId: state.offerId,
104
109
  subscribed: makeOptedinSelector(ownProps.product)(state),
105
- frequency: makeProductFrequencySelector(ownProps.product)(state),
106
- ...configSelector(state, ownProps, 'defaultFrequency'),
110
+ frequency: makeProductFrequencyOptedInSelector(ownProps.product)(state),
111
+ defaultFrequency:
112
+ makeProductDefaultFrequencySelector(ownProps.product?.id)(state) || getFallbackValue(ownProps, 'defaultFrequency'),
107
113
  nextUpcomingOrder: state.previewUpsellOffer ? { public_id: 'preview-order-id' } : state.nextUpcomingOrder,
108
114
  isPreview: state.previewUpsellOffer
109
115
  });
@@ -369,22 +369,3 @@ describe('multi-currency', () => {
369
369
  assertOfferShown(element);
370
370
  });
371
371
  });
372
-
373
- describe('product specific frequencies', () => {
374
- it('should hide the offer when on cart', async () => {
375
- const element = await getOfferElement({
376
- hasProductSpecificFrequencies: true
377
- });
378
- element.isCart = true;
379
- await element.updateComplete;
380
-
381
- assertOfferHidden(element);
382
- });
383
-
384
- it('should show the offer when not on cart', async () => {
385
- const element = await getOfferElement({
386
- hasProductSpecificFrequencies: true
387
- });
388
- assertOfferShown(element);
389
- });
390
- });
@@ -29,10 +29,23 @@ describe('OptinStatus', () => {
29
29
  describe('mapStateToProps', () => {
30
30
  it('should pick defaultFrequency from config', () => {
31
31
  expect(
32
- mapStateToProps({
33
- config: { defaultFrequency: 'foo from config' },
34
- locale: { defaultFrequency: 'foo from locale' }
35
- })
32
+ mapStateToProps(
33
+ {
34
+ config: {
35
+ productFrequencies: {
36
+ 123: {
37
+ defaultFrequency: 'foo from config'
38
+ }
39
+ }
40
+ },
41
+ locale: { defaultFrequency: 'foo from locale' }
42
+ },
43
+ {
44
+ product: {
45
+ id: '123'
46
+ }
47
+ }
48
+ )
36
49
  ).toEqual(jasmine.objectContaining({ defaultFrequency: 'foo from config' }));
37
50
  });
38
51
 
@@ -19,7 +19,8 @@ import {
19
19
  receiveCreateOneTime,
20
20
  createIu,
21
21
  requestConvertOneTimeToSubscription,
22
- receiveConvertOneTime
22
+ receiveConvertOneTime,
23
+ productChangePrepaidShipments
23
24
  } from '../actions';
24
25
  import * as constants from '../constants';
25
26
  import { api } from '../api';
@@ -246,6 +247,51 @@ describe('redux actions', function () {
246
247
  expect(dispatch.calls.argsFor(1)[0]).toEqual(unauthorized(new Error('some reason')));
247
248
  });
248
249
  });
250
+
251
+ describe('actions.productChangePrepaidShipments', () => {
252
+ function changePrepaidShipments(prepaidShipments) {
253
+ const getState = jasmine.createSpy('getState').and.returnValue({
254
+ config: {
255
+ prepaidSellingPlans: {
256
+ 'yum prepaid id': [
257
+ { numberShipments: 3, sellingPlan: 'yum prepaid selling plan id 1' },
258
+ {
259
+ numberShipments: 4,
260
+ sellingPlan: 'yum prepaid selling plan id 2'
261
+ }
262
+ ]
263
+ },
264
+ productFrequencies: {
265
+ 'yum prepaid id': {
266
+ frequencies: ['yum selling plan id 1']
267
+ }
268
+ }
269
+ }
270
+ });
271
+ const dispatch = jasmine.createSpy('dispatch');
272
+ productChangePrepaidShipments(
273
+ {
274
+ id: 'yum prepaid id'
275
+ },
276
+ prepaidShipments,
277
+ {}
278
+ )(dispatch, getState);
279
+
280
+ return {
281
+ dispatchedFrequency: dispatch.calls.argsFor(0)[0]?.payload.frequency
282
+ };
283
+ }
284
+
285
+ it('sets frequency to the corresponding selling plan', () => {
286
+ const { dispatchedFrequency } = changePrepaidShipments(4);
287
+ expect(dispatchedFrequency).toEqual('yum prepaid selling plan id 2');
288
+ });
289
+
290
+ it('sets frequency to the first frequency when prepaidShipments is null', () => {
291
+ const { dispatchedFrequency } = changePrepaidShipments(null);
292
+ expect(dispatchedFrequency).toEqual('yum selling plan id 1');
293
+ });
294
+ });
249
295
  });
250
296
 
251
297
  describe('actions.iu', () => {
@@ -10,84 +10,7 @@ class MockElement extends TemplateElement {
10
10
 
11
11
  customElements.define('og-test-element', MockElement);
12
12
  describe('TemplateElement', () => {
13
- describe('getOption', () => {
14
- it('should get option from attribute', () => {
15
- const el = new MockElement();
16
- el.setAttribute('foo', 'bar');
17
- expect(el.getOption('foo')).toEqual('bar');
18
- });
19
-
20
- it('should return undefined given element does not have attribute', () => {
21
- const el = new MockElement();
22
- el.setAttribute('yum', 'egg');
23
- expect(el.getOption('clam')).toBeUndefined();
24
- });
25
-
26
- it('should return boolean given element has attribute with true string', () => {
27
- [
28
- ['one', 'true'],
29
- ['two', 'True']
30
- ].forEach(([attrName, attrValue]) => {
31
- const el = new MockElement();
32
- el.setAttribute(attrName, attrValue);
33
- expect(el.getOption(attrName)).toBe(true);
34
- });
35
- });
36
-
37
- it('should return boolean given element has attribute with false string', () => {
38
- [
39
- ['three', 'false'],
40
- ['four', 'False']
41
- ].forEach(([attrName, attrValue]) => {
42
- const el = new MockElement();
43
- el.setAttribute(attrName, attrValue);
44
- expect(el.getOption(attrName)).toBe(false);
45
- });
46
- });
47
-
48
- it('should get option from attribute in kebabCase', () => {
49
- const el = new MockElement();
50
- el.setAttribute('foo-bar', 'bar');
51
- expect(el.getOption('fooBar')).toEqual('bar');
52
- });
53
-
54
- it('should get option from config', () => {
55
- const el = new MockElement();
56
- el.config = { fooBar: 'bar' };
57
- expect(el.getOption('fooBar')).toEqual('bar');
58
- });
59
-
60
- it('should get option from template.config', () => {
61
- const el = new MockElement();
62
- el.template = { config: { fooBar: 'bar' } };
63
- expect(el.getOption('fooBar')).toEqual('bar');
64
- });
65
-
66
- it('should template.config take precedence over config', () => {
67
- const el = new MockElement();
68
- el.config = { fooBar: 'baz' };
69
- el.template = { config: { fooBar: 'bar' } };
70
- expect(el.getOption('fooBar')).toEqual('bar');
71
- });
72
-
73
- it('should attribute take precedence over template.config', () => {
74
- const el = new MockElement();
75
- el.setAttribute('foo-bar', 'foo');
76
- el.config = { fooBar: 'baz' };
77
- el.template = { config: { fooBar: 'bar' } };
78
- expect(el.getOption('fooBar')).toEqual('foo');
79
- });
80
- });
81
-
82
13
  describe('applyTemplate', () => {
83
- it('should attribute take precedence over template.config', () => {
84
- const el = new MockElement();
85
- el.setAttribute('foo-bar', 'foo');
86
- el.config = { fooBar: 'baz' };
87
- el.template = { config: { fooBar: 'bar' } };
88
- expect(el.getOption('fooBar')).toEqual('foo');
89
- });
90
-
91
14
  it('should only set innerHTML if change from prev', () => {
92
15
  const el = new MockElement();
93
16
  /* eslint no-underscore-dangle: ["error", { "allow": ["_templateMarkup"] }] */
@@ -4,6 +4,7 @@ import { environment, merchantId, offer, sessionId } from '../reducer';
4
4
  import { requestOffer } from '../actions';
5
5
  import { createStore, combineReducers, applyMiddleware } from 'redux';
6
6
  import { FETCH_RESPONSE_ERROR } from '../constants';
7
+ import thunk from 'redux-thunk';
7
8
 
8
9
  const mockOfferResponse = (productId, inStock = true, autoship = true, defaultFrequency) => {
9
10
  return Promise.resolve({
@@ -44,7 +45,7 @@ describe('offerRequest', () => {
44
45
  sessionId: 'x.y.z',
45
46
  lastError: null
46
47
  },
47
- applyMiddleware(offerRequestMiddleware)
48
+ applyMiddleware(offerRequestMiddleware, thunk)
48
49
  );
49
50
  });
50
51
 
@@ -3,7 +3,7 @@ import {
3
3
  makeOptedinSelector,
4
4
  isSameProduct,
5
5
  templatesSelector,
6
- makeProductDefaultFrequencySelector,
6
+ makeProductSpecificDefaultFrequencySelector,
7
7
  makeProductPrepaidShipmentOptionsSelector
8
8
  } from '../selectors';
9
9
  import { stringifyFrequency } from '../api';
@@ -61,25 +61,25 @@ describe('templatesSelector', () => {
61
61
  });
62
62
  });
63
63
 
64
- describe('makeProductDefaultFrequencySelector', () => {
64
+ describe('makeProductSpecificDefaultFrequencySelector', () => {
65
65
  it('should return memoized function', () => {
66
- const firsCall = makeProductDefaultFrequencySelector(123);
66
+ const firsCall = makeProductSpecificDefaultFrequencySelector(123);
67
67
  expect(firsCall).toEqual(jasmine.any(Function));
68
- expect(firsCall).toBe(makeProductDefaultFrequencySelector(123));
68
+ expect(firsCall).toBe(makeProductSpecificDefaultFrequencySelector(123));
69
69
  });
70
70
 
71
71
  it('should return null by default', () => {
72
- const selectProductDefaultFrequency = makeProductDefaultFrequencySelector(123);
72
+ const selectProductDefaultFrequency = makeProductSpecificDefaultFrequencySelector(123);
73
73
  expect(selectProductDefaultFrequency({})).toEqual(null);
74
74
  });
75
75
 
76
76
  it('should select the product default frequency if there is one', () => {
77
- const selectProductDefaultFrequency = makeProductDefaultFrequencySelector(123);
77
+ const selectProductDefaultFrequency = makeProductSpecificDefaultFrequencySelector(123);
78
78
  expect(selectProductDefaultFrequency({ defaultFrequencies: { 123: '2_w' } })).toEqual('2_w');
79
79
  });
80
80
 
81
81
  it('should select the product default frequency if there is one (object frequency)', () => {
82
- const selectProductDefaultFrequency = makeProductDefaultFrequencySelector(123);
82
+ const selectProductDefaultFrequency = makeProductSpecificDefaultFrequencySelector(123);
83
83
  const frequency = { every: '2', period: 'w' };
84
84
  expect(selectProductDefaultFrequency({ defaultFrequencies: { 123: frequency } })).toEqual(
85
85
  stringifyFrequency(frequency)