@ordergroove/offers 2.47.2-alpha-PR-1356-3.33 → 2.47.2
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/CHANGELOG.md +11 -0
- package/dist/bundle-report.html +23 -23
- package/dist/offers.js +73 -73
- package/dist/offers.js.map +3 -3
- package/package.json +2 -2
- package/src/components/IncentiveText.js +3 -0
- package/src/components/PrepaidStatus.js +1 -2
- package/src/components/__tests__/IncentiveText.spec.js +36 -0
- package/src/core/__tests__/reducer.spec.js +43 -0
- package/src/core/actions.js +3 -9
- package/src/core/constants.js +0 -4
- package/src/core/descriptors.js +1 -2
- package/src/core/reducer.ts +7 -1
- package/src/core/selectors.ts +0 -6
- package/src/core/types/reducer.ts +4 -5
- package/src/shopify/__tests__/reducers/optedin.spec.js +0 -347
- package/src/shopify/reducers/config.ts +5 -12
- package/src/shopify/shopifyReducer.ts +44 -99
- package/src/shopify/utils.ts +0 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ordergroove/offers",
|
|
3
|
-
"version": "2.47.2
|
|
3
|
+
"version": "2.47.2",
|
|
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.10.0",
|
|
50
50
|
"@types/lodash.memoize": "^4.1.9"
|
|
51
51
|
},
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "b039a0b5286ac8736eb4501b4763512f27e58953"
|
|
53
53
|
}
|
|
@@ -109,6 +109,9 @@ export class IncentiveText extends withProduct(LitElement) {
|
|
|
109
109
|
// we only have criteria if the incentive is standardized
|
|
110
110
|
incentive.criteria &&
|
|
111
111
|
incentive.criteria.node_type === 'PREMISE' &&
|
|
112
|
+
// prefer to show non-threshold discounts
|
|
113
|
+
// we don't currently evaluate thresholds, so we don't know if they apply or not (especially if it's order-level)
|
|
114
|
+
!incentive.threshold_field &&
|
|
112
115
|
preferredIncentiveStandards.includes(incentive.criteria.standard)
|
|
113
116
|
);
|
|
114
117
|
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
} from '../core/selectors';
|
|
8
8
|
import { productChangePrepaidShipments } from '../core/actions';
|
|
9
9
|
import { withProduct } from '../core/resolveProperties';
|
|
10
|
-
import { getDefaultPrepaidOption } from '../shopify/utils';
|
|
11
10
|
|
|
12
11
|
export class PrepaidStatus extends withProduct(LitElement) {
|
|
13
12
|
static get properties() {
|
|
@@ -30,7 +29,7 @@ export class PrepaidStatus extends withProduct(LitElement) {
|
|
|
30
29
|
getDefaultPrepaidShipments() {
|
|
31
30
|
return this.options.includes(this.defaultPrepaidShipments)
|
|
32
31
|
? this.defaultPrepaidShipments
|
|
33
|
-
:
|
|
32
|
+
: this.options[1] || this.options[0];
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
handleSelect({ target: { value } }) {
|
|
@@ -208,6 +208,42 @@ describe('IncentiveText', () => {
|
|
|
208
208
|
expect(el.innerText.trim()).toEqual('33%');
|
|
209
209
|
});
|
|
210
210
|
|
|
211
|
+
it('prefers non-threshold incentives', async () => {
|
|
212
|
+
const el = new IncentiveText();
|
|
213
|
+
el.from = 'DiscountPercent';
|
|
214
|
+
el.incentives = {
|
|
215
|
+
initial: [],
|
|
216
|
+
ongoing: [
|
|
217
|
+
{
|
|
218
|
+
field: 'total_price',
|
|
219
|
+
object: 'item',
|
|
220
|
+
type: 'Discount Percent',
|
|
221
|
+
value: 25,
|
|
222
|
+
threshold_field: 'order',
|
|
223
|
+
threshold_value: '10.00',
|
|
224
|
+
criteria: {
|
|
225
|
+
node_type: 'PREMISE',
|
|
226
|
+
standard: 'PROGRAM_WIDE'
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
field: 'total_price',
|
|
231
|
+
object: 'item',
|
|
232
|
+
type: 'Discount Percent',
|
|
233
|
+
value: 10,
|
|
234
|
+
threshold_field: null,
|
|
235
|
+
threshold_value: null,
|
|
236
|
+
criteria: {
|
|
237
|
+
node_type: 'PREMISE',
|
|
238
|
+
standard: 'PROGRAM_WIDE'
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
};
|
|
243
|
+
await appendToBody(el);
|
|
244
|
+
expect(el.innerText.trim()).toEqual('10%');
|
|
245
|
+
});
|
|
246
|
+
|
|
211
247
|
it('returns other matching incentives if no preferred incentives available', async () => {
|
|
212
248
|
const el = new IncentiveText();
|
|
213
249
|
el.from = 'DiscountPercent';
|
|
@@ -1072,6 +1072,21 @@ describe('reducers', () => {
|
|
|
1072
1072
|
it('should consider incentives_display_enhanced if present', () => {
|
|
1073
1073
|
const enhancedOfferResponse = {
|
|
1074
1074
|
...offerResponse,
|
|
1075
|
+
incentives: {
|
|
1076
|
+
44198332104980: {
|
|
1077
|
+
ongoing: ['8285f16f826e4cf29b49f073e45e32ab', 'threshold-id', '10205e185a5c4ccf83de75459241623a'],
|
|
1078
|
+
initial: ['0f98a321d29e47d2a17fe3a8a7522d26']
|
|
1079
|
+
}
|
|
1080
|
+
},
|
|
1081
|
+
incentives_display: {
|
|
1082
|
+
...offerResponse.incentives_display,
|
|
1083
|
+
'threshold-id': {
|
|
1084
|
+
object: 'order',
|
|
1085
|
+
field: 'sub_total',
|
|
1086
|
+
type: 'Discount Percent',
|
|
1087
|
+
value: 20.0
|
|
1088
|
+
}
|
|
1089
|
+
},
|
|
1075
1090
|
incentives_display_enhanced: {
|
|
1076
1091
|
'8285f16f826e4cf29b49f073e45e32ab': {
|
|
1077
1092
|
incentive_target: 'order',
|
|
@@ -1081,6 +1096,14 @@ describe('reducers', () => {
|
|
|
1081
1096
|
threshold_field: null,
|
|
1082
1097
|
threshold_value: null
|
|
1083
1098
|
},
|
|
1099
|
+
'threshold-id': {
|
|
1100
|
+
incentive_target: 'order',
|
|
1101
|
+
incentive_type: 'discount_percent',
|
|
1102
|
+
incentive_value: '20.00',
|
|
1103
|
+
// no criteria, since this is program-wide
|
|
1104
|
+
threshold_field: 'order',
|
|
1105
|
+
threshold_value: '10.00'
|
|
1106
|
+
},
|
|
1084
1107
|
'10205e185a5c4ccf83de75459241623a': {
|
|
1085
1108
|
incentive_target: 'item',
|
|
1086
1109
|
incentive_type: 'discount_percent',
|
|
@@ -1117,6 +1140,22 @@ describe('reducers', () => {
|
|
|
1117
1140
|
type: 'Discount Percent',
|
|
1118
1141
|
value: 10.0,
|
|
1119
1142
|
id: '8285f16f826e4cf29b49f073e45e32ab',
|
|
1143
|
+
threshold_field: null,
|
|
1144
|
+
threshold_value: null,
|
|
1145
|
+
criteria: {
|
|
1146
|
+
node_type: 'PREMISE',
|
|
1147
|
+
standard: 'PROGRAM_WIDE',
|
|
1148
|
+
premise_value: null
|
|
1149
|
+
}
|
|
1150
|
+
},
|
|
1151
|
+
{
|
|
1152
|
+
object: 'order',
|
|
1153
|
+
field: 'sub_total',
|
|
1154
|
+
type: 'Discount Percent',
|
|
1155
|
+
value: 20.0,
|
|
1156
|
+
id: 'threshold-id',
|
|
1157
|
+
threshold_field: 'order',
|
|
1158
|
+
threshold_value: '10.00',
|
|
1120
1159
|
criteria: {
|
|
1121
1160
|
node_type: 'PREMISE',
|
|
1122
1161
|
standard: 'PROGRAM_WIDE',
|
|
@@ -1129,6 +1168,8 @@ describe('reducers', () => {
|
|
|
1129
1168
|
type: 'Discount Amount',
|
|
1130
1169
|
value: 5.0,
|
|
1131
1170
|
id: '10205e185a5c4ccf83de75459241623a',
|
|
1171
|
+
threshold_field: null,
|
|
1172
|
+
threshold_value: null,
|
|
1132
1173
|
criteria: {
|
|
1133
1174
|
node_type: 'PREMISE',
|
|
1134
1175
|
standard: 'PREPAID_ORDERS_PER_BILLING',
|
|
@@ -1143,6 +1184,8 @@ describe('reducers', () => {
|
|
|
1143
1184
|
type: 'Discount Percent',
|
|
1144
1185
|
value: 11.0,
|
|
1145
1186
|
id: '0f98a321d29e47d2a17fe3a8a7522d26',
|
|
1187
|
+
threshold_field: null,
|
|
1188
|
+
threshold_value: null,
|
|
1146
1189
|
criteria: {
|
|
1147
1190
|
node_type: 'PREMISE',
|
|
1148
1191
|
standard: 'PSI',
|
package/src/core/actions.js
CHANGED
|
@@ -2,11 +2,7 @@ import { resolveAuth } from '@ordergroove/auth';
|
|
|
2
2
|
import * as constants from './constants';
|
|
3
3
|
import { api } from './api';
|
|
4
4
|
import { safeOgFrequency } from './utils';
|
|
5
|
-
import {
|
|
6
|
-
makeFrequencyForPrepaidShipmentsSelector,
|
|
7
|
-
makePrepaidSellingPlansSelector,
|
|
8
|
-
makeProductFrequenciesSelector
|
|
9
|
-
} from './selectors';
|
|
5
|
+
import { makeFrequencyForPrepaidShipmentsSelector, makeProductFrequenciesSelector } from './selectors';
|
|
10
6
|
|
|
11
7
|
export const optinProduct = (product, frequency, offer) => ({
|
|
12
8
|
type: constants.OPTIN_PRODUCT,
|
|
@@ -194,12 +190,10 @@ export const requestSessionId = () => (dispatch, getState) => {
|
|
|
194
190
|
|
|
195
191
|
export const receiveOffer = (response, offer, productId) => (dispatch, getState) => {
|
|
196
192
|
// this is a thunk so that we access the state for the selector
|
|
197
|
-
const
|
|
198
|
-
const frequencyConfig = makeProductFrequenciesSelector(productId)(state);
|
|
199
|
-
const prepaidSellingPlans = makePrepaidSellingPlansSelector(productId)(state);
|
|
193
|
+
const frequencyConfig = makeProductFrequenciesSelector(productId)(getState());
|
|
200
194
|
dispatch({
|
|
201
195
|
type: constants.RECEIVE_OFFER,
|
|
202
|
-
payload: { ...response, offer, frequencyConfig
|
|
196
|
+
payload: { ...response, offer, frequencyConfig }
|
|
203
197
|
});
|
|
204
198
|
};
|
|
205
199
|
|
package/src/core/constants.js
CHANGED
package/src/core/descriptors.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ELIGIBILITY_GROUPS } from './constants';
|
|
2
1
|
import {
|
|
3
2
|
makeSubscribedSelector,
|
|
4
3
|
makeOptedoutSelector,
|
|
@@ -26,7 +25,7 @@ export const hasUpsellGroup = (state, ownProps) => {
|
|
|
26
25
|
|
|
27
26
|
export const prepaidEligible = (state, ownProps) => {
|
|
28
27
|
const groups = eligibilityGroups(state, ownProps);
|
|
29
|
-
return groups?.some(it => it ===
|
|
28
|
+
return groups?.some(it => it === 'prepaid') || false;
|
|
30
29
|
};
|
|
31
30
|
|
|
32
31
|
export const subscribed = (state, ownProps) => makeSubscribedSelector(ownProps.product)(state);
|
package/src/core/reducer.ts
CHANGED
|
@@ -180,7 +180,13 @@ const mapIncentive = (
|
|
|
180
180
|
? enhanced.criteria
|
|
181
181
|
: // when there is no criteria in the enhanced incentive, it means it's a program wide incentive
|
|
182
182
|
// for ease-of-use, we set use a "PROGRAM_WIDE" pseudo-standard here
|
|
183
|
-
{
|
|
183
|
+
{
|
|
184
|
+
node_type: 'PREMISE',
|
|
185
|
+
standard: constants.INCENTIVE_STANDARD_TYPES.PROGRAM_WIDE,
|
|
186
|
+
premise_value: null
|
|
187
|
+
},
|
|
188
|
+
threshold_field: enhanced.threshold_field,
|
|
189
|
+
threshold_value: enhanced.threshold_value
|
|
184
190
|
}
|
|
185
191
|
: {}),
|
|
186
192
|
id: [i][0]
|
package/src/core/selectors.ts
CHANGED
|
@@ -232,12 +232,6 @@ export const makeFrequencyForPrepaidShipmentsSelector = (product: BaseProduct, p
|
|
|
232
232
|
}
|
|
233
233
|
);
|
|
234
234
|
|
|
235
|
-
export const makePrepaidSellingPlansSelector = (product: string) =>
|
|
236
|
-
createSelector(prepaidSellingPlansSelector, prepaidSellingPlans => {
|
|
237
|
-
const productId = safeProductId(product);
|
|
238
|
-
return prepaidSellingPlans[productId] || [];
|
|
239
|
-
});
|
|
240
|
-
|
|
241
235
|
/** Determine the discounted price of the product, based on the incentives returned from the Offers endpoint. This assumes a pay-as-you-go subscription. */
|
|
242
236
|
export const makeDiscountedProductPriceSelector = memoize((productId: string) =>
|
|
243
237
|
createSelector(
|
|
@@ -20,8 +20,10 @@ export type IncentivesState = Record<string, IncentiveObject>;
|
|
|
20
20
|
export type Incentive = ApiIncentive & {
|
|
21
21
|
id: string;
|
|
22
22
|
/**
|
|
23
|
-
* undefined when the offer profile is not standardized
|
|
23
|
+
* all these fields are undefined when the offer profile is not standardized
|
|
24
24
|
*/
|
|
25
|
+
threshold_field?: string | null;
|
|
26
|
+
threshold_value?: string | null;
|
|
25
27
|
criteria?: {
|
|
26
28
|
node_type: string;
|
|
27
29
|
premise_value: unknown;
|
|
@@ -53,13 +55,11 @@ export type ConfigState = Partial<{
|
|
|
53
55
|
* @deprecated use productFrequencies instead
|
|
54
56
|
*/
|
|
55
57
|
frequenciesText: string[];
|
|
56
|
-
prepaidSellingPlans: Record<string,
|
|
58
|
+
prepaidSellingPlans: Record<string, { numberShipments: number; sellingPlan: string }[]>;
|
|
57
59
|
storeCurrency: string;
|
|
58
60
|
productFrequencies: Record<string, ProductFrequencyConfig>;
|
|
59
61
|
}>;
|
|
60
62
|
|
|
61
|
-
export type PrepaidSellingPlan = { numberShipments: number; sellingPlan: string };
|
|
62
|
-
|
|
63
63
|
export type ProductFrequencyConfig = {
|
|
64
64
|
frequencies?: string[];
|
|
65
65
|
frequenciesEveryPeriod?: string[];
|
|
@@ -92,7 +92,6 @@ export type ReceiveOfferPayload = OfferResponse & {
|
|
|
92
92
|
frequenciesEveryPeriod?: string[];
|
|
93
93
|
frequenciesText?: string[];
|
|
94
94
|
};
|
|
95
|
-
prepaidSellingPlans: PrepaidSellingPlan[];
|
|
96
95
|
};
|
|
97
96
|
|
|
98
97
|
export type OfferElement = InstanceType<typeof Offer> & { config: ConfigState };
|
|
@@ -2,45 +2,6 @@ import * as constants from '../../../core/constants';
|
|
|
2
2
|
import { optedin } from '../../shopifyReducer';
|
|
3
3
|
import { DEFAULT_PAY_AS_YOU_GO_GROUP_NAME } from '../../utils';
|
|
4
4
|
|
|
5
|
-
const PREPAID_PRODUCT_ID = '43017264201944';
|
|
6
|
-
const BOTH_ELIGIBLE_PRODUCT_ID = '43017264201946';
|
|
7
|
-
|
|
8
|
-
const PREPAID_PLAN_3_SHIPMENTS = 'prepaid-plan-3-shipments';
|
|
9
|
-
const PREPAID_PLAN_6_SHIPMENTS = 'prepaid-plan-6-shipments';
|
|
10
|
-
|
|
11
|
-
const prepaidPlanWith3Shipments = { sellingPlan: PREPAID_PLAN_3_SHIPMENTS, numberShipments: 3 };
|
|
12
|
-
const prepaidPlanWith6Shipments = { sellingPlan: PREPAID_PLAN_6_SHIPMENTS, numberShipments: 6 };
|
|
13
|
-
|
|
14
|
-
function buildReceiveOfferPayload(overrides = {}) {
|
|
15
|
-
return {
|
|
16
|
-
autoship: {},
|
|
17
|
-
autoship_by_default: {},
|
|
18
|
-
default_frequencies: {},
|
|
19
|
-
in_stock: {},
|
|
20
|
-
eligibility_groups: {},
|
|
21
|
-
offer: {},
|
|
22
|
-
frequencyConfig: {
|
|
23
|
-
frequencies: [],
|
|
24
|
-
frequenciesEveryPeriod: []
|
|
25
|
-
},
|
|
26
|
-
prepaidSellingPlans: [],
|
|
27
|
-
...overrides
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function buildPrepaidSellingPlanGroup(variantId, plans) {
|
|
32
|
-
return {
|
|
33
|
-
name: `Prepaid-${variantId}`,
|
|
34
|
-
selling_plans: plans.map((plan, index) => ({
|
|
35
|
-
id: plan.sellingPlan,
|
|
36
|
-
options: [
|
|
37
|
-
{ name: 'Delivery every', value: `PREPAID-${index + 1} months` },
|
|
38
|
-
{ name: 'Shipment amount', value: `${plan.numberShipments} shipments` }
|
|
39
|
-
]
|
|
40
|
-
}))
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
5
|
describe('optedin', () => {
|
|
45
6
|
it('should return optins given action SETUP_CART', () => {
|
|
46
7
|
const actual = optedin([], {
|
|
@@ -622,208 +583,6 @@ describe('optedin', () => {
|
|
|
622
583
|
]);
|
|
623
584
|
});
|
|
624
585
|
});
|
|
625
|
-
|
|
626
|
-
describe('given product is prepaid eligible but NOT autoship eligible', () => {
|
|
627
|
-
describe('given prepaidSellingPlans are populated', () => {
|
|
628
|
-
it('should opt into 2nd prepaid plan and set prepaidShipments when multiple plans available', () => {
|
|
629
|
-
const actual = optedin([], {
|
|
630
|
-
type: constants.RECEIVE_OFFER,
|
|
631
|
-
payload: buildReceiveOfferPayload({
|
|
632
|
-
autoship: {
|
|
633
|
-
[PREPAID_PRODUCT_ID]: false
|
|
634
|
-
},
|
|
635
|
-
autoship_by_default: {
|
|
636
|
-
[PREPAID_PRODUCT_ID]: true
|
|
637
|
-
},
|
|
638
|
-
in_stock: {
|
|
639
|
-
[PREPAID_PRODUCT_ID]: true
|
|
640
|
-
},
|
|
641
|
-
eligibility_groups: {
|
|
642
|
-
[PREPAID_PRODUCT_ID]: [constants.ELIGIBILITY_GROUPS.PREPAID]
|
|
643
|
-
},
|
|
644
|
-
prepaidSellingPlans: [prepaidPlanWith3Shipments, prepaidPlanWith6Shipments]
|
|
645
|
-
})
|
|
646
|
-
});
|
|
647
|
-
|
|
648
|
-
expect(actual).toEqual([
|
|
649
|
-
{
|
|
650
|
-
id: PREPAID_PRODUCT_ID,
|
|
651
|
-
frequency: PREPAID_PLAN_6_SHIPMENTS,
|
|
652
|
-
prepaidShipments: 6
|
|
653
|
-
}
|
|
654
|
-
]);
|
|
655
|
-
});
|
|
656
|
-
|
|
657
|
-
it('should opt into 1st prepaid plan and set prepaidShipments when only one plan available', () => {
|
|
658
|
-
const actual = optedin([], {
|
|
659
|
-
type: constants.RECEIVE_OFFER,
|
|
660
|
-
payload: buildReceiveOfferPayload({
|
|
661
|
-
autoship: {
|
|
662
|
-
[PREPAID_PRODUCT_ID]: false
|
|
663
|
-
},
|
|
664
|
-
autoship_by_default: {
|
|
665
|
-
[PREPAID_PRODUCT_ID]: true
|
|
666
|
-
},
|
|
667
|
-
in_stock: {
|
|
668
|
-
[PREPAID_PRODUCT_ID]: true
|
|
669
|
-
},
|
|
670
|
-
eligibility_groups: {
|
|
671
|
-
[PREPAID_PRODUCT_ID]: [constants.ELIGIBILITY_GROUPS.PREPAID]
|
|
672
|
-
},
|
|
673
|
-
prepaidSellingPlans: [prepaidPlanWith3Shipments]
|
|
674
|
-
})
|
|
675
|
-
});
|
|
676
|
-
|
|
677
|
-
expect(actual).toEqual([
|
|
678
|
-
{
|
|
679
|
-
id: PREPAID_PRODUCT_ID,
|
|
680
|
-
frequency: PREPAID_PLAN_3_SHIPMENTS,
|
|
681
|
-
prepaidShipments: 3
|
|
682
|
-
}
|
|
683
|
-
]);
|
|
684
|
-
});
|
|
685
|
-
});
|
|
686
|
-
|
|
687
|
-
describe('given prepaidSellingPlans are NOT populated', () => {
|
|
688
|
-
it('should opt into prepaid with PREPAID_PLACEHOLDER and prepaidShipments null', () => {
|
|
689
|
-
const actual = optedin([], {
|
|
690
|
-
type: constants.RECEIVE_OFFER,
|
|
691
|
-
payload: buildReceiveOfferPayload({
|
|
692
|
-
autoship: {
|
|
693
|
-
[PREPAID_PRODUCT_ID]: false
|
|
694
|
-
},
|
|
695
|
-
autoship_by_default: {
|
|
696
|
-
[PREPAID_PRODUCT_ID]: true
|
|
697
|
-
},
|
|
698
|
-
in_stock: {
|
|
699
|
-
[PREPAID_PRODUCT_ID]: true
|
|
700
|
-
},
|
|
701
|
-
eligibility_groups: {
|
|
702
|
-
[PREPAID_PRODUCT_ID]: [constants.ELIGIBILITY_GROUPS.PREPAID]
|
|
703
|
-
},
|
|
704
|
-
prepaidSellingPlans: []
|
|
705
|
-
})
|
|
706
|
-
});
|
|
707
|
-
|
|
708
|
-
expect(actual).toEqual([
|
|
709
|
-
{
|
|
710
|
-
id: PREPAID_PRODUCT_ID,
|
|
711
|
-
frequency: 'prepaid-replace-me',
|
|
712
|
-
prepaidShipments: null
|
|
713
|
-
}
|
|
714
|
-
]);
|
|
715
|
-
});
|
|
716
|
-
});
|
|
717
|
-
|
|
718
|
-
describe('edge cases', () => {
|
|
719
|
-
it('should not create optin when product not in stock, not autoship_by_default, or not in eligibility_groups', () => {
|
|
720
|
-
// Product not in stock
|
|
721
|
-
const notInStock = optedin([], {
|
|
722
|
-
type: constants.RECEIVE_OFFER,
|
|
723
|
-
payload: buildReceiveOfferPayload({
|
|
724
|
-
autoship: {
|
|
725
|
-
[PREPAID_PRODUCT_ID]: false
|
|
726
|
-
},
|
|
727
|
-
autoship_by_default: {
|
|
728
|
-
[PREPAID_PRODUCT_ID]: true
|
|
729
|
-
},
|
|
730
|
-
in_stock: {
|
|
731
|
-
[PREPAID_PRODUCT_ID]: false
|
|
732
|
-
},
|
|
733
|
-
eligibility_groups: {
|
|
734
|
-
[PREPAID_PRODUCT_ID]: [constants.ELIGIBILITY_GROUPS.PREPAID]
|
|
735
|
-
},
|
|
736
|
-
prepaidSellingPlans: [prepaidPlanWith3Shipments]
|
|
737
|
-
})
|
|
738
|
-
});
|
|
739
|
-
|
|
740
|
-
// Product not autoship_by_default
|
|
741
|
-
const notAutoshipByDefault = optedin([], {
|
|
742
|
-
type: constants.RECEIVE_OFFER,
|
|
743
|
-
payload: buildReceiveOfferPayload({
|
|
744
|
-
autoship: {
|
|
745
|
-
[PREPAID_PRODUCT_ID]: false
|
|
746
|
-
},
|
|
747
|
-
autoship_by_default: {
|
|
748
|
-
[PREPAID_PRODUCT_ID]: false
|
|
749
|
-
},
|
|
750
|
-
in_stock: {
|
|
751
|
-
[PREPAID_PRODUCT_ID]: true
|
|
752
|
-
},
|
|
753
|
-
eligibility_groups: {
|
|
754
|
-
[PREPAID_PRODUCT_ID]: [constants.ELIGIBILITY_GROUPS.PREPAID]
|
|
755
|
-
},
|
|
756
|
-
prepaidSellingPlans: [prepaidPlanWith3Shipments]
|
|
757
|
-
})
|
|
758
|
-
});
|
|
759
|
-
|
|
760
|
-
// Product not in eligibility_groups
|
|
761
|
-
const notInEligibilityGroups = optedin([], {
|
|
762
|
-
type: constants.RECEIVE_OFFER,
|
|
763
|
-
payload: buildReceiveOfferPayload({
|
|
764
|
-
autoship: {
|
|
765
|
-
[PREPAID_PRODUCT_ID]: false
|
|
766
|
-
},
|
|
767
|
-
autoship_by_default: {
|
|
768
|
-
[PREPAID_PRODUCT_ID]: true
|
|
769
|
-
},
|
|
770
|
-
in_stock: {
|
|
771
|
-
[PREPAID_PRODUCT_ID]: true
|
|
772
|
-
},
|
|
773
|
-
eligibility_groups: {
|
|
774
|
-
[PREPAID_PRODUCT_ID]: []
|
|
775
|
-
},
|
|
776
|
-
prepaidSellingPlans: [prepaidPlanWith3Shipments]
|
|
777
|
-
})
|
|
778
|
-
});
|
|
779
|
-
|
|
780
|
-
expect(notInStock).toEqual([]);
|
|
781
|
-
expect(notAutoshipByDefault).toEqual([]);
|
|
782
|
-
expect(notInEligibilityGroups).toEqual([]);
|
|
783
|
-
});
|
|
784
|
-
});
|
|
785
|
-
});
|
|
786
|
-
|
|
787
|
-
describe('given product is BOTH prepaid eligible AND autoship eligible', () => {
|
|
788
|
-
it('should opt into regular subscription with PSDF frequency, not prepaid', () => {
|
|
789
|
-
const actual = optedin([], {
|
|
790
|
-
type: constants.RECEIVE_OFFER,
|
|
791
|
-
payload: buildReceiveOfferPayload({
|
|
792
|
-
autoship: {
|
|
793
|
-
[BOTH_ELIGIBLE_PRODUCT_ID]: true
|
|
794
|
-
},
|
|
795
|
-
autoship_by_default: {
|
|
796
|
-
[BOTH_ELIGIBLE_PRODUCT_ID]: true
|
|
797
|
-
},
|
|
798
|
-
default_frequencies: {
|
|
799
|
-
[BOTH_ELIGIBLE_PRODUCT_ID]: {
|
|
800
|
-
every: 1,
|
|
801
|
-
every_period: 2
|
|
802
|
-
}
|
|
803
|
-
},
|
|
804
|
-
in_stock: {
|
|
805
|
-
[BOTH_ELIGIBLE_PRODUCT_ID]: true
|
|
806
|
-
},
|
|
807
|
-
eligibility_groups: {
|
|
808
|
-
[BOTH_ELIGIBLE_PRODUCT_ID]: [constants.ELIGIBILITY_GROUPS.PREPAID]
|
|
809
|
-
},
|
|
810
|
-
frequencyConfig: {
|
|
811
|
-
frequencies: ['yum selling plan id 1', 'yum selling plan id 2'],
|
|
812
|
-
frequenciesEveryPeriod: ['1_1', '1_2']
|
|
813
|
-
},
|
|
814
|
-
prepaidSellingPlans: [prepaidPlanWith3Shipments, prepaidPlanWith6Shipments]
|
|
815
|
-
})
|
|
816
|
-
});
|
|
817
|
-
|
|
818
|
-
expect(actual).toEqual([
|
|
819
|
-
{
|
|
820
|
-
id: BOTH_ELIGIBLE_PRODUCT_ID,
|
|
821
|
-
frequency: 'yum selling plan id 2'
|
|
822
|
-
}
|
|
823
|
-
]);
|
|
824
|
-
expect(actual[0].prepaidShipments).toBeUndefined();
|
|
825
|
-
});
|
|
826
|
-
});
|
|
827
586
|
});
|
|
828
587
|
});
|
|
829
588
|
|
|
@@ -997,69 +756,6 @@ describe('optedin', () => {
|
|
|
997
756
|
}
|
|
998
757
|
]);
|
|
999
758
|
});
|
|
1000
|
-
|
|
1001
|
-
describe('given frequency is PREPAID_PLACEHOLDER', () => {
|
|
1002
|
-
describe('given prepaidSellingPlans exist for variant', () => {
|
|
1003
|
-
it('should replace prepaid placeholder optin', () => {
|
|
1004
|
-
const actual = optedin(
|
|
1005
|
-
[
|
|
1006
|
-
{
|
|
1007
|
-
id: PREPAID_PRODUCT_ID,
|
|
1008
|
-
frequency: 'prepaid-replace-me',
|
|
1009
|
-
prepaidShipments: null
|
|
1010
|
-
}
|
|
1011
|
-
],
|
|
1012
|
-
{
|
|
1013
|
-
type: constants.SETUP_PRODUCT,
|
|
1014
|
-
payload: {
|
|
1015
|
-
product: {
|
|
1016
|
-
variants: [{ id: PREPAID_PRODUCT_ID }],
|
|
1017
|
-
selling_plan_groups: [
|
|
1018
|
-
buildPrepaidSellingPlanGroup(PREPAID_PRODUCT_ID, [
|
|
1019
|
-
prepaidPlanWith3Shipments,
|
|
1020
|
-
prepaidPlanWith6Shipments
|
|
1021
|
-
])
|
|
1022
|
-
]
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
);
|
|
1027
|
-
|
|
1028
|
-
expect(actual).toEqual([
|
|
1029
|
-
{
|
|
1030
|
-
id: PREPAID_PRODUCT_ID,
|
|
1031
|
-
frequency: PREPAID_PLAN_6_SHIPMENTS,
|
|
1032
|
-
prepaidShipments: 6
|
|
1033
|
-
}
|
|
1034
|
-
]);
|
|
1035
|
-
});
|
|
1036
|
-
});
|
|
1037
|
-
|
|
1038
|
-
describe('given prepaidSellingPlans do NOT exist for variant', () => {
|
|
1039
|
-
it('should remove placeholder optin', () => {
|
|
1040
|
-
const actual = optedin(
|
|
1041
|
-
[
|
|
1042
|
-
{
|
|
1043
|
-
id: PREPAID_PRODUCT_ID,
|
|
1044
|
-
frequency: 'prepaid-replace-me',
|
|
1045
|
-
prepaidShipments: null
|
|
1046
|
-
}
|
|
1047
|
-
],
|
|
1048
|
-
{
|
|
1049
|
-
type: constants.SETUP_PRODUCT,
|
|
1050
|
-
payload: {
|
|
1051
|
-
product: {
|
|
1052
|
-
variants: [{ id: PREPAID_PRODUCT_ID }],
|
|
1053
|
-
selling_plan_groups: []
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
);
|
|
1058
|
-
|
|
1059
|
-
expect(actual).toEqual([]);
|
|
1060
|
-
});
|
|
1061
|
-
});
|
|
1062
|
-
});
|
|
1063
759
|
});
|
|
1064
760
|
|
|
1065
761
|
describe('given action is PRODUCT_CHANGE_PREPAID_SHIPMENTS', () => {
|
|
@@ -1111,49 +807,6 @@ describe('optedin', () => {
|
|
|
1111
807
|
});
|
|
1112
808
|
});
|
|
1113
809
|
|
|
1114
|
-
it('integration: SETUP_PRODUCT after RECEIVE_OFFER should handle default prepaid optin', () => {
|
|
1115
|
-
// Step 1: RECEIVE_OFFER creates optin with placeholder (no prepaidSellingPlans available yet)
|
|
1116
|
-
const afterReceiveOffer = optedin([], {
|
|
1117
|
-
type: constants.RECEIVE_OFFER,
|
|
1118
|
-
payload: buildReceiveOfferPayload({
|
|
1119
|
-
autoship: {
|
|
1120
|
-
[PREPAID_PRODUCT_ID]: false
|
|
1121
|
-
},
|
|
1122
|
-
autoship_by_default: {
|
|
1123
|
-
[PREPAID_PRODUCT_ID]: true
|
|
1124
|
-
},
|
|
1125
|
-
in_stock: {
|
|
1126
|
-
[PREPAID_PRODUCT_ID]: true
|
|
1127
|
-
},
|
|
1128
|
-
eligibility_groups: {
|
|
1129
|
-
[PREPAID_PRODUCT_ID]: [constants.ELIGIBILITY_GROUPS.PREPAID]
|
|
1130
|
-
},
|
|
1131
|
-
prepaidSellingPlans: []
|
|
1132
|
-
})
|
|
1133
|
-
});
|
|
1134
|
-
|
|
1135
|
-
// Step 2: SETUP_PRODUCT replaces placeholder with actual selling plan
|
|
1136
|
-
const afterSetupProduct = optedin(afterReceiveOffer, {
|
|
1137
|
-
type: constants.SETUP_PRODUCT,
|
|
1138
|
-
payload: {
|
|
1139
|
-
product: {
|
|
1140
|
-
variants: [{ id: PREPAID_PRODUCT_ID }],
|
|
1141
|
-
selling_plan_groups: [
|
|
1142
|
-
buildPrepaidSellingPlanGroup(PREPAID_PRODUCT_ID, [prepaidPlanWith3Shipments, prepaidPlanWith6Shipments])
|
|
1143
|
-
]
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
});
|
|
1147
|
-
|
|
1148
|
-
expect(afterSetupProduct).toEqual([
|
|
1149
|
-
{
|
|
1150
|
-
id: PREPAID_PRODUCT_ID,
|
|
1151
|
-
frequency: PREPAID_PLAN_6_SHIPMENTS,
|
|
1152
|
-
prepaidShipments: 6
|
|
1153
|
-
}
|
|
1154
|
-
]);
|
|
1155
|
-
});
|
|
1156
|
-
|
|
1157
810
|
it('should return unmodified state given unsupported action', () => {
|
|
1158
811
|
const actual = optedin(
|
|
1159
812
|
{ 'yum existing key': 'yum existing value' },
|