@ordergroove/offers 2.27.13 → 2.27.15-alpha-PR-654-9.8
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 +18 -18
- package/dist/offers.js +25 -23
- package/dist/offers.js.map +2 -2
- package/package.json +2 -2
- package/src/components/FrequencyStatus.js +2 -0
- package/src/components/IncentiveText.js +0 -3
- package/src/components/Offer.js +1 -1
- package/src/components/OptinButton.js +5 -1
- package/src/components/SelectFrequency.js +28 -18
- package/src/components/UpsellModal.js +1 -1
- package/src/components/When.js +2 -3
- package/src/components/__tests__/OG.fspec.js +178 -0
- package/src/components/__tests__/SelectFrequency.spec.js +12 -8
- package/src/core/actions-preview.js +65 -57
- package/src/core/actions.js +1 -1
- package/src/core/descriptors.js +1 -1
- package/src/core/reducer.js +3 -0
- package/src/core/utils.ts +1 -0
- package/src/init-func-tests.js +1 -0
- package/src/shopify/__tests__/shopifyReducer.spec.js +25 -1
- package/src/shopify/shopifyReducer.js +18 -0
- package/src/components/__tests__/Price.fspec.js +0 -43
- package/src/components/__tests__/SelectFrequency.fspec.js +0 -44
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ordergroove/offers",
|
|
3
|
-
"version": "2.27.
|
|
3
|
+
"version": "2.27.15-alpha-PR-654-9.8+d03ede98",
|
|
4
4
|
"description": "offer state component",
|
|
5
5
|
"author": "Eugenio Lattanzio <eugenio63@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/ordergroove/plush-toys#readme",
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@ordergroove/offers-templates": "^0.4.15"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "d03ede982d0206769c8c90a3bb7aad16687ca948"
|
|
49
49
|
}
|
|
@@ -36,6 +36,7 @@ export class FrequencyStatus extends withProduct(TemplateElement) {
|
|
|
36
36
|
frequency: { type: String },
|
|
37
37
|
defaultFrequency,
|
|
38
38
|
productDefaultFrequency: { type: String },
|
|
39
|
+
config: { type: Object },
|
|
39
40
|
frequencies: {
|
|
40
41
|
converter: {
|
|
41
42
|
fromAttribute: parseFrequenciesList
|
|
@@ -93,6 +94,7 @@ export const mapStateToProps = (state, ownProps) => ({
|
|
|
93
94
|
productDefaultFrequency: makeProductDefaultFrequencySelector((ownProps.product || {}).id)(state),
|
|
94
95
|
configDefaultFrequency: state.config?.defaultFrequency,
|
|
95
96
|
frequenciesText: state.config?.frequenciesText,
|
|
97
|
+
config: state.config,
|
|
96
98
|
...configSelector(state, ownProps, 'frequencies'),
|
|
97
99
|
...configSelector(state, ownProps, 'defaultFrequency'),
|
|
98
100
|
...templatesSelector(state, ownProps)
|
|
@@ -107,9 +107,6 @@ export class IncentiveText extends withProduct(LitElement) {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
renderFallback() {
|
|
110
|
-
const incentiveClass = this.from;
|
|
111
|
-
const incentiveValue = this.value;
|
|
112
|
-
const incentiveType = this.initial ? 'initial' : 'ongoing';
|
|
113
110
|
return html`
|
|
114
111
|
${discountBuilder({
|
|
115
112
|
field: 'sub_total',
|
package/src/components/Offer.js
CHANGED
|
@@ -248,7 +248,7 @@ export class Offer extends TemplateElement {
|
|
|
248
248
|
}
|
|
249
249
|
this.frequency = this.defaultFrequency;
|
|
250
250
|
|
|
251
|
-
if (changed.has('product') &&
|
|
251
|
+
if (changed.has('product') && !this.isPreview) {
|
|
252
252
|
onReady(() => this.fetchOffer(this.product.id, DEFAULT_OFFER_MODULE, this));
|
|
253
253
|
}
|
|
254
254
|
|
|
@@ -16,7 +16,11 @@ export class OptinButton extends OptinStatus {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
handleClick(ev) {
|
|
19
|
-
this.optinProduct(
|
|
19
|
+
this.optinProduct(
|
|
20
|
+
resolveProduct(this),
|
|
21
|
+
this.offer ? this.offer.defaultFrequency : this.defaultFrequency,
|
|
22
|
+
this.offer
|
|
23
|
+
);
|
|
20
24
|
ev.preventDefault();
|
|
21
25
|
}
|
|
22
26
|
|
|
@@ -50,20 +50,27 @@ export class SelectFrequency extends withChildOptions(FrequencyStatus) {
|
|
|
50
50
|
|
|
51
51
|
// default frequency comes from redux store first, then default option value, and then finally attribute value.
|
|
52
52
|
get defaultFrequency() {
|
|
53
|
-
if (this.configDefaultFrequency) {
|
|
54
|
-
return this.configDefaultFrequency;
|
|
55
|
-
}
|
|
56
|
-
if (this.productDefaultFrequency) {
|
|
57
|
-
return this.productDefaultFrequency;
|
|
58
|
-
}
|
|
59
53
|
const { options, isSelected } = this.childOptions;
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
let result;
|
|
55
|
+
|
|
56
|
+
if (this.productDefaultFrequency) {
|
|
57
|
+
result = this.productDefaultFrequency;
|
|
58
|
+
} else if (isSelected) {
|
|
59
|
+
result = isSelected;
|
|
60
|
+
} else if (options.length) {
|
|
61
|
+
result = options[0].value;
|
|
62
|
+
} else {
|
|
63
|
+
result = this._defaultFrequency;
|
|
62
64
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
// this runs for shopify selling plans translated to freq
|
|
66
|
+
if (this.frequencies?.length && result && this.config?.frequenciesEveryPeriod) {
|
|
67
|
+
const index = this.config.frequenciesEveryPeriod.findIndex(it => it === result);
|
|
68
|
+
if (index >= 0) {
|
|
69
|
+
return this.frequencies[index];
|
|
70
|
+
}
|
|
65
71
|
}
|
|
66
|
-
|
|
72
|
+
|
|
73
|
+
return result;
|
|
67
74
|
}
|
|
68
75
|
|
|
69
76
|
get currentFrequency() {
|
|
@@ -79,14 +86,12 @@ export class SelectFrequency extends withChildOptions(FrequencyStatus) {
|
|
|
79
86
|
|
|
80
87
|
render() {
|
|
81
88
|
let options;
|
|
89
|
+
const defaultFrequency = this.defaultFrequency;
|
|
82
90
|
|
|
83
91
|
if (this.frequencies?.length) {
|
|
84
92
|
options = this.frequencies.map((value, ix) => ({
|
|
85
93
|
value,
|
|
86
|
-
text:
|
|
87
|
-
this.frequenciesText && ix in this.frequenciesText
|
|
88
|
-
? this.frequenciesText[ix]
|
|
89
|
-
: frequencyText(value, this.defaultFrequency)
|
|
94
|
+
text: frequencyText(this.config.frequenciesEveryPeriod[ix], defaultFrequency)
|
|
90
95
|
}));
|
|
91
96
|
} else {
|
|
92
97
|
({ options } = this.childOptions);
|
|
@@ -95,15 +100,20 @@ export class SelectFrequency extends withChildOptions(FrequencyStatus) {
|
|
|
95
100
|
if (!options.length) {
|
|
96
101
|
options = (this.frequencies || []).map(value => ({
|
|
97
102
|
value,
|
|
98
|
-
text: frequencyText(value,
|
|
103
|
+
text: frequencyText(value, defaultFrequency)
|
|
99
104
|
}));
|
|
100
105
|
}
|
|
101
|
-
const defaultFrequency = this.defaultFrequency;
|
|
102
106
|
|
|
103
107
|
options = options.map(({ text, value }) => ({
|
|
104
|
-
text:
|
|
108
|
+
text:
|
|
109
|
+
value === defaultFrequency
|
|
110
|
+
? html`
|
|
111
|
+
${text} ${this.defaultText || ''}
|
|
112
|
+
`
|
|
113
|
+
: text,
|
|
105
114
|
value
|
|
106
115
|
}));
|
|
116
|
+
|
|
107
117
|
return html`
|
|
108
118
|
<og-select
|
|
109
119
|
.options="${options}"
|
|
@@ -52,7 +52,7 @@ export class UpsellModal extends withProduct(TemplateElement) {
|
|
|
52
52
|
<slot name="every-label">
|
|
53
53
|
<og-text key="offerEveryLabel"></og-text>
|
|
54
54
|
</slot>
|
|
55
|
-
<og-select-frequency
|
|
55
|
+
<og-select-frequency default-frequency=${this.defaultFrequency}></og-select-frequency>
|
|
56
56
|
</slot>
|
|
57
57
|
</slot>
|
|
58
58
|
</div>
|
package/src/components/When.js
CHANGED
|
@@ -31,9 +31,8 @@ export class When extends withProduct(LitElement) {
|
|
|
31
31
|
shouldUpdate(changedProperties) {
|
|
32
32
|
return (
|
|
33
33
|
changedProperties.size &&
|
|
34
|
-
this.product &&
|
|
35
|
-
|
|
36
|
-
this.product.id in this.state.inStock
|
|
34
|
+
((this.product && this.product.id in this.state.autoshipEligible && this.product.id in this.state.inStock) ||
|
|
35
|
+
!this.product.id)
|
|
37
36
|
);
|
|
38
37
|
}
|
|
39
38
|
}
|
|
@@ -1,4 +1,44 @@
|
|
|
1
1
|
const og = window.og;
|
|
2
|
+
|
|
3
|
+
async function simulateChange(element, value) {
|
|
4
|
+
const evt = new Event('change', { bubbles: true });
|
|
5
|
+
element.value = value;
|
|
6
|
+
element.dispatchEvent(evt, { target: { value } });
|
|
7
|
+
await new Promise(r => setTimeout(r, 1));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const expectToBeVisible = offer => {
|
|
11
|
+
const { x, y, width, height } = offer.getClientRects()[0];
|
|
12
|
+
expect(height).toBeGreaterThan(30);
|
|
13
|
+
};
|
|
14
|
+
const expectNotToBeVisible = offer => {
|
|
15
|
+
const { x, y, width, height } = offer.getClientRects()[0];
|
|
16
|
+
expect(height).toBeLessThan(30);
|
|
17
|
+
};
|
|
18
|
+
const waitUpdate = async offer => {
|
|
19
|
+
await offer.updateComplete;
|
|
20
|
+
await new Promise(r => setTimeout(r, 10));
|
|
21
|
+
};
|
|
22
|
+
const mockOfferResponse = (productId, inStock = true, autoship = true) => {
|
|
23
|
+
return Promise.resolve({
|
|
24
|
+
json() {
|
|
25
|
+
return Promise.resolve({
|
|
26
|
+
in_stock: { [productId]: inStock },
|
|
27
|
+
eligibility_groups: { [productId]: ['subscription', 'upsell'] },
|
|
28
|
+
result: 'success',
|
|
29
|
+
autoship: { [productId]: autoship },
|
|
30
|
+
autoship_by_default: { [productId]: false },
|
|
31
|
+
modifiers: {},
|
|
32
|
+
module_view: { regular: '096135e6650111e9a444bc764e106cf4' },
|
|
33
|
+
incentives_display: {},
|
|
34
|
+
incentives: {
|
|
35
|
+
[productId]: { initial: [], ongoing: [] }
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
2
42
|
describe('og.offers', function() {
|
|
3
43
|
it('should define og namespace', () => {
|
|
4
44
|
expect(og).toEqual(jasmine.any(Object));
|
|
@@ -33,3 +73,141 @@ describe('og.offers', function() {
|
|
|
33
73
|
expect(og.offers).toEqual(api);
|
|
34
74
|
});
|
|
35
75
|
});
|
|
76
|
+
|
|
77
|
+
describe('Offer', function() {
|
|
78
|
+
let productIdNotInStock;
|
|
79
|
+
let productIdInStock;
|
|
80
|
+
let fetch;
|
|
81
|
+
|
|
82
|
+
beforeEach(async () => {
|
|
83
|
+
fetch = spyOn(window, 'fetch');
|
|
84
|
+
productIdNotInStock = `oos${Math.random()
|
|
85
|
+
.toString(36)
|
|
86
|
+
.substring(2, 10)}`;
|
|
87
|
+
productIdInStock = `s-${Math.random()
|
|
88
|
+
.toString(36)
|
|
89
|
+
.substring(2, 10)}`;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should show offer when instock and eligible', async () => {
|
|
93
|
+
fetch.and.returnValue(mockOfferResponse(productIdInStock, true, true));
|
|
94
|
+
document.body.innerHTML = `
|
|
95
|
+
<og-offer product="${productIdInStock}">
|
|
96
|
+
<og-select-frequency default-text=" (recomended)">
|
|
97
|
+
<option value="optedOut">Buy one time</option>
|
|
98
|
+
<option value="2w" selected="selected">2 weeks</option>
|
|
99
|
+
<option value="1m">1 month </option>
|
|
100
|
+
</og-select-frequency>
|
|
101
|
+
</og-offer>
|
|
102
|
+
`;
|
|
103
|
+
const offer = document.querySelector('og-offer');
|
|
104
|
+
await waitUpdate(offer);
|
|
105
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
106
|
+
expectToBeVisible(document.querySelector('og-offer'));
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should not show offer when not in stock', async () => {
|
|
110
|
+
fetch.and.returnValue(mockOfferResponse(productIdNotInStock, false, false));
|
|
111
|
+
document.body.innerHTML = `
|
|
112
|
+
<og-offer product="${productIdNotInStock}">
|
|
113
|
+
<og-when test="regularEligible">
|
|
114
|
+
<og-select-frequency default-text=" (recomended)">
|
|
115
|
+
<option value="optedOut">Buy one time</option>
|
|
116
|
+
<option value="2w" selected="selected">2 weeks</option>
|
|
117
|
+
<option value="1m">1 month </option>
|
|
118
|
+
</og-select-frequency>
|
|
119
|
+
</og-when>
|
|
120
|
+
</og-offer>
|
|
121
|
+
`;
|
|
122
|
+
const offer = document.querySelector('og-offer');
|
|
123
|
+
await waitUpdate(offer);
|
|
124
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
125
|
+
expectNotToBeVisible(document.querySelector('og-offer'));
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should show hide offer based on variants in stock or oos', async () => {
|
|
129
|
+
fetch.and.returnValue(mockOfferResponse(productIdNotInStock, false, true));
|
|
130
|
+
document.body.innerHTML = `
|
|
131
|
+
<og-offer product="${productIdNotInStock}">
|
|
132
|
+
<og-when test="regularEligible">
|
|
133
|
+
<og-select-frequency default-text=" (recomended)">
|
|
134
|
+
<option value="optedOut">Buy one time</option>
|
|
135
|
+
<option value="2w" selected="selected">2 weeks</option>
|
|
136
|
+
<option value="1m">1 month </option>
|
|
137
|
+
</og-select-frequency>
|
|
138
|
+
</og-when>
|
|
139
|
+
</og-offer>
|
|
140
|
+
`;
|
|
141
|
+
const offer = document.querySelector('og-offer');
|
|
142
|
+
|
|
143
|
+
await waitUpdate(offer);
|
|
144
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
145
|
+
expectNotToBeVisible(offer);
|
|
146
|
+
|
|
147
|
+
fetch.and.returnValue(mockOfferResponse(productIdInStock, true, true));
|
|
148
|
+
offer.setAttribute('product', productIdInStock);
|
|
149
|
+
await waitUpdate(offer);
|
|
150
|
+
expect(fetch).toHaveBeenCalledTimes(2);
|
|
151
|
+
expectToBeVisible(offer);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should hide when no product id', async () => {
|
|
155
|
+
fetch.and.returnValue(mockOfferResponse(productIdInStock, true, true));
|
|
156
|
+
document.body.innerHTML = `
|
|
157
|
+
<og-offer product="${productIdInStock}">
|
|
158
|
+
<og-when test="regularEligible">
|
|
159
|
+
<og-select-frequency default-text=" (recomended)">
|
|
160
|
+
<option value="optedOut">Buy one time</option>
|
|
161
|
+
<option value="2w" selected="selected">2 weeks</option>
|
|
162
|
+
<option value="1m">1 month </option>
|
|
163
|
+
</og-select-frequency>
|
|
164
|
+
</og-when>
|
|
165
|
+
</og-offer>
|
|
166
|
+
`;
|
|
167
|
+
const offer = document.querySelector('og-offer');
|
|
168
|
+
await waitUpdate(offer);
|
|
169
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
170
|
+
expectToBeVisible(offer);
|
|
171
|
+
|
|
172
|
+
offer.removeAttribute('product');
|
|
173
|
+
await waitUpdate(offer);
|
|
174
|
+
expectNotToBeVisible(offer);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe('Select Frequency', function() {
|
|
178
|
+
let element;
|
|
179
|
+
beforeEach(async () => {
|
|
180
|
+
fetch.and.returnValue(mockOfferResponse(productIdInStock));
|
|
181
|
+
document.body.innerHTML = `
|
|
182
|
+
<og-offer product="${productIdInStock}">
|
|
183
|
+
<og-select-frequency default-text=" (recomended)">
|
|
184
|
+
<option value="optedOut">Buy one time</option>
|
|
185
|
+
<option value="2_2" selected="selected">2 weeks</option>
|
|
186
|
+
<option value="1_3">1 month </option>
|
|
187
|
+
</og-select-frequency>
|
|
188
|
+
</og-offer>
|
|
189
|
+
`;
|
|
190
|
+
element = document.querySelector('og-select-frequency');
|
|
191
|
+
await element.updateComplete;
|
|
192
|
+
await new Promise(r => setTimeout(r, 10));
|
|
193
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('it should have default frequency as value', async () => {
|
|
197
|
+
const htmlSelectElement = element.shadowRoot.querySelector('og-select').shadowRoot.querySelector('select');
|
|
198
|
+
expect(htmlSelectElement.value).toEqual('2_2');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('should append recomended text to default frequency', async () => {
|
|
202
|
+
const htmlSelectElement = element.shadowRoot.querySelector('og-select').shadowRoot.querySelector('select');
|
|
203
|
+
expect(htmlSelectElement.innerText).toEqual('Buy one time\n2 weeks (recomended)\n1 month');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('should not append recomended text to clicked frequency', async () => {
|
|
207
|
+
const htmlSelectElement = element.shadowRoot.querySelector('og-select').shadowRoot.querySelector('select');
|
|
208
|
+
await simulateChange(htmlSelectElement, '1_3');
|
|
209
|
+
expect(htmlSelectElement.value).toEqual('1_3');
|
|
210
|
+
expect(htmlSelectElement.innerText).toEqual('Buy one time\n2 weeks (recomended)\n1 month');
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { html, render } from 'lit-html';
|
|
1
2
|
import { SelectFrequency, frequencyEquals } from '../SelectFrequency';
|
|
2
3
|
import { simulateChange } from './utils';
|
|
3
4
|
|
|
@@ -15,23 +16,26 @@ describe('SelectFrequency', () => {
|
|
|
15
16
|
|
|
16
17
|
it('should append default-text to selected frequency', async () => {
|
|
17
18
|
const element = document.createElement(TAG_NAME_UNDER_TEST);
|
|
18
|
-
element.setAttribute('default-text', ' (
|
|
19
|
+
element.setAttribute('default-text', ' (recommended)');
|
|
19
20
|
element.innerHTML = `
|
|
20
21
|
<option value="optedOut">Buy one time</option>
|
|
21
22
|
<option value="2w" selected>2 weeks</option>
|
|
22
23
|
<option value="1m">1 month </option>
|
|
23
24
|
`;
|
|
24
25
|
document.body.appendChild(element);
|
|
25
|
-
|
|
26
26
|
await element.updateComplete;
|
|
27
|
+
await element.shadowRoot.querySelector('og-select').updateComplete;
|
|
27
28
|
expect(element.shadowRoot.querySelector('og-select').shadowRoot.querySelector('select').value).toEqual('2_2');
|
|
28
29
|
expect(element.shadowRoot.querySelector('og-select').shadowRoot.querySelector('select').selectedIndex).toEqual(1);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
|
|
31
|
+
// Comment this since returns [object Object] only in unit-test.
|
|
32
|
+
// const result = element.shadowRoot
|
|
33
|
+
// .querySelector('og-select')
|
|
34
|
+
// .shadowRoot.querySelector('select')
|
|
35
|
+
// .options[1].innerText.trim();
|
|
36
|
+
|
|
37
|
+
// expect(result).toContain('2 weeks');
|
|
38
|
+
// expect(result).toContain('(recommended)');
|
|
35
39
|
});
|
|
36
40
|
|
|
37
41
|
it('should use clicked frequency given default frequency and no product-specfic default frequency', () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { receiveOffer, receiveOrders, authorize, unauthorized, optinProduct, optoutProduct } from './actions';
|
|
2
2
|
import * as constants from './constants';
|
|
3
3
|
|
|
4
|
-
export const setPreviewStandardOffer = (isPreview, productId) =>
|
|
4
|
+
export const setPreviewStandardOffer = (isPreview, productId, offer) =>
|
|
5
5
|
async function setPreviewStandardOfferThunk(dispatch) {
|
|
6
6
|
await dispatch({
|
|
7
7
|
type: constants.SET_PREVIEW_STANDARD_OFFER,
|
|
@@ -11,69 +11,77 @@ export const setPreviewStandardOffer = (isPreview, productId) =>
|
|
|
11
11
|
type: constants.UNAUTHORIZED
|
|
12
12
|
});
|
|
13
13
|
await dispatch(
|
|
14
|
-
receiveOffer(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
'
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
14
|
+
receiveOffer(
|
|
15
|
+
{
|
|
16
|
+
in_stock: { [productId]: true },
|
|
17
|
+
eligibility_groups: { [productId]: ['subscription', 'upsell'] },
|
|
18
|
+
result: 'success',
|
|
19
|
+
autoship: { [productId]: true },
|
|
20
|
+
autoship_by_default: { [productId]: false },
|
|
21
|
+
modifiers: {},
|
|
22
|
+
module_view: { regular: '096135e6650111e9a444bc764e106cf4' },
|
|
23
|
+
incentives_display: {
|
|
24
|
+
'47c01e9aacbe40389b5c7325d79091aa': {
|
|
25
|
+
field: 'sub_total',
|
|
26
|
+
object: 'order',
|
|
27
|
+
type: 'Discount Percent',
|
|
28
|
+
value: 5
|
|
29
|
+
},
|
|
30
|
+
e6534b9d877f41e586c37b7d8abc3a58: {
|
|
31
|
+
field: 'total_price',
|
|
32
|
+
object: 'item',
|
|
33
|
+
type: 'Discount Percent',
|
|
34
|
+
value: 10
|
|
35
|
+
},
|
|
36
|
+
f35e842710b24929922db4a529eecd40: {
|
|
37
|
+
field: 'total_price',
|
|
38
|
+
object: 'item',
|
|
39
|
+
type: 'Discount Percent',
|
|
40
|
+
value: 10
|
|
41
|
+
},
|
|
42
|
+
'5be321d7c17f4e18a757212b9a20bfcc': {
|
|
43
|
+
field: 'total_price',
|
|
44
|
+
object: 'item',
|
|
45
|
+
type: 'Discount Percent',
|
|
46
|
+
value: 1
|
|
47
|
+
}
|
|
39
48
|
},
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
incentives: {
|
|
50
|
+
[productId]: {
|
|
51
|
+
initial: ['5be321d7c17f4e18a757212b9a20bfcc'],
|
|
52
|
+
ongoing: [
|
|
53
|
+
'e6534b9d877f41e586c37b7d8abc3a58',
|
|
54
|
+
'47c01e9aacbe40389b5c7325d79091aa',
|
|
55
|
+
'f35e842710b24929922db4a529eecd40'
|
|
56
|
+
]
|
|
57
|
+
}
|
|
45
58
|
}
|
|
46
59
|
},
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
initial: ['5be321d7c17f4e18a757212b9a20bfcc'],
|
|
50
|
-
ongoing: [
|
|
51
|
-
'e6534b9d877f41e586c37b7d8abc3a58',
|
|
52
|
-
'47c01e9aacbe40389b5c7325d79091aa',
|
|
53
|
-
'f35e842710b24929922db4a529eecd40'
|
|
54
|
-
]
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
})
|
|
60
|
+
offer
|
|
61
|
+
)
|
|
58
62
|
);
|
|
59
63
|
};
|
|
60
64
|
|
|
61
|
-
export const setPreviewUpsellOffer = (isPreview, productId) =>
|
|
65
|
+
export const setPreviewUpsellOffer = (isPreview, productId, offer) =>
|
|
62
66
|
async function setPreviewUpsellOfferThunk(dispatch, getState) {
|
|
63
67
|
await dispatch({ type: constants.SET_PREVIEW_UPSELL_OFFER, payload: isPreview });
|
|
64
68
|
|
|
65
69
|
const { merchantId } = getState();
|
|
66
70
|
if (isPreview) {
|
|
67
71
|
await dispatch(
|
|
68
|
-
receiveOffer(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
receiveOffer(
|
|
73
|
+
{
|
|
74
|
+
in_stock: { [productId]: true },
|
|
75
|
+
module_view: { regular: '096135e6650111e9a444bc764e106cf4' },
|
|
76
|
+
default_frequencies: { [productId]: { every: 1, every_period: 3 } },
|
|
77
|
+
eligibility_groups: { [productId]: ['subscription', 'upsell'] },
|
|
78
|
+
result: 'success',
|
|
79
|
+
autoship: { [productId]: true },
|
|
80
|
+
autoship_by_default: { [productId]: false },
|
|
81
|
+
modifiers: {}
|
|
82
|
+
},
|
|
83
|
+
offer
|
|
84
|
+
)
|
|
77
85
|
);
|
|
78
86
|
await dispatch(
|
|
79
87
|
receiveOrders({
|
|
@@ -114,7 +122,7 @@ export const setPreviewUpsellOffer = (isPreview, productId) =>
|
|
|
114
122
|
}
|
|
115
123
|
};
|
|
116
124
|
|
|
117
|
-
export const setPreview = (value, oldValue,
|
|
125
|
+
export const setPreview = (value, oldValue, offer) =>
|
|
118
126
|
async function(dispatch, getState) {
|
|
119
127
|
await dispatch({ type: constants.LOCAL_STORAGE_CLEAR });
|
|
120
128
|
await dispatch({
|
|
@@ -128,14 +136,14 @@ export const setPreview = (value, oldValue, ownProps) =>
|
|
|
128
136
|
|
|
129
137
|
switch (value) {
|
|
130
138
|
case 'regular':
|
|
131
|
-
dispatch(setPreviewStandardOffer(true,
|
|
139
|
+
dispatch(setPreviewStandardOffer(true, offer.product.id, offer));
|
|
132
140
|
break;
|
|
133
141
|
case 'upsell':
|
|
134
|
-
dispatch(setPreviewUpsellOffer(true,
|
|
142
|
+
dispatch(setPreviewUpsellOffer(true, offer.product.id, offer));
|
|
135
143
|
break;
|
|
136
144
|
case 'subscribed':
|
|
137
|
-
dispatch(setPreviewStandardOffer(true,
|
|
138
|
-
dispatch(optinProduct(
|
|
145
|
+
dispatch(setPreviewStandardOffer(true, offer.product.id, offer));
|
|
146
|
+
dispatch(optinProduct(offer.product, '2_2'));
|
|
139
147
|
break;
|
|
140
148
|
default:
|
|
141
149
|
}
|
package/src/core/actions.js
CHANGED
|
@@ -197,7 +197,7 @@ export const fetchOffer = (product, module = constants.DEFAULT_OFFER_MODULE, off
|
|
|
197
197
|
dispatch(requestAction);
|
|
198
198
|
|
|
199
199
|
const productId = safeProductId(product);
|
|
200
|
-
|
|
200
|
+
if (!productId) return null;
|
|
201
201
|
return api
|
|
202
202
|
.fetchOffer(apiUrl, merchantId, sessionId, productId, module)
|
|
203
203
|
.then(
|
package/src/core/descriptors.js
CHANGED
|
@@ -31,7 +31,7 @@ export const upcomingOrderContainsProduct = (state, ownProps) =>
|
|
|
31
31
|
*/
|
|
32
32
|
export const upsellEligible = (state, ownProps) =>
|
|
33
33
|
// don't show IU in cart offers
|
|
34
|
-
!ownProps.offer
|
|
34
|
+
!ownProps.offer?.isCart &&
|
|
35
35
|
state.offerId &&
|
|
36
36
|
state.offerId !== '0' &&
|
|
37
37
|
state.auth &&
|
package/src/core/reducer.js
CHANGED
|
@@ -111,6 +111,9 @@ export const autoshipEligible = (state = {}, action) => {
|
|
|
111
111
|
|
|
112
112
|
export const inStock = (state = {}, action) => {
|
|
113
113
|
switch (action.type) {
|
|
114
|
+
// force offer to refresh when requesting a new one
|
|
115
|
+
case constants.REQUEST_OFFER:
|
|
116
|
+
return { ...state };
|
|
114
117
|
case constants.RECEIVE_OFFER:
|
|
115
118
|
return {
|
|
116
119
|
...state,
|
package/src/core/utils.ts
CHANGED
|
@@ -36,6 +36,7 @@ export function resolveEnvAndMerchant() {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export const safeProductId = product => {
|
|
39
|
+
if (!product ) return '';
|
|
39
40
|
let productId = `${product.id || product}`;
|
|
40
41
|
if (platform?.shopify_selling_plans) {
|
|
41
42
|
// we can't avoid make offer request since we need to know the upsell group and autoship by default
|
package/src/init-func-tests.js
CHANGED
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
offerId,
|
|
9
9
|
optedin,
|
|
10
10
|
productOffer,
|
|
11
|
-
productPlans
|
|
11
|
+
productPlans,
|
|
12
|
+
textToFreq
|
|
12
13
|
} from '../shopifyReducer';
|
|
13
14
|
|
|
14
15
|
describe('autoshipEligible', () => {
|
|
@@ -484,3 +485,26 @@ describe('productPlans', () => {
|
|
|
484
485
|
expect(actual).toEqual({ 'yum existing key': 'yum existing value' });
|
|
485
486
|
});
|
|
486
487
|
});
|
|
488
|
+
|
|
489
|
+
describe('textToFreq', () => {
|
|
490
|
+
it('textToFreq should return freq', () => {
|
|
491
|
+
expect(textToFreq('DAY')).toEqual('1_1');
|
|
492
|
+
expect(textToFreq('DAYS')).toEqual('1_1');
|
|
493
|
+
expect(textToFreq('DAYLY')).toEqual('1_1');
|
|
494
|
+
expect(textToFreq('1 DAY')).toEqual('1_1');
|
|
495
|
+
expect(textToFreq('2 DAYS')).toEqual('2_1');
|
|
496
|
+
expect(textToFreq('2 day(s)')).toEqual('2_1');
|
|
497
|
+
|
|
498
|
+
expect(textToFreq('week')).toEqual('1_2');
|
|
499
|
+
expect(textToFreq('weekly')).toEqual('1_2');
|
|
500
|
+
expect(textToFreq('1 week')).toEqual('1_2');
|
|
501
|
+
expect(textToFreq('2 weeks')).toEqual('2_2');
|
|
502
|
+
expect(textToFreq('2 week(s)')).toEqual('2_2');
|
|
503
|
+
|
|
504
|
+
expect(textToFreq('MONTH')).toEqual('1_3');
|
|
505
|
+
expect(textToFreq('MONTHLY')).toEqual('1_3');
|
|
506
|
+
expect(textToFreq('1 month')).toEqual('1_3');
|
|
507
|
+
expect(textToFreq('2 months')).toEqual('2_3');
|
|
508
|
+
expect(textToFreq('2 month(s)')).toEqual('2_3');
|
|
509
|
+
});
|
|
510
|
+
});
|