fontdue-js 2.19.0 → 2.19.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 +10 -0
- package/dist/__tests__/collectionBundleSelection.test.d.ts +1 -0
- package/dist/__tests__/collectionBundleSelection.test.js +1634 -0
- package/dist/fontdue.css +190 -126
- package/dist/reducer.js +32 -6
- package/dist/utils.d.ts +3 -0
- package/dist/utils.js +21 -10
- package/package.json +5 -2
- package/vitest.config.ts +10 -0
|
@@ -0,0 +1,1634 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _vitest = require("vitest");
|
|
4
|
+
var _utils = require("../utils");
|
|
5
|
+
var _reducer = _interopRequireWildcard(require("../reducer"));
|
|
6
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
7
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
8
|
+
// Mock react-relay's graphql tag since productState.ts uses it at import time
|
|
9
|
+
_vitest.vi.mock('react-relay', () => ({
|
|
10
|
+
graphql: () => null,
|
|
11
|
+
fetchQuery: _vitest.vi.fn()
|
|
12
|
+
}));
|
|
13
|
+
// Helper to build a minimal FontdueState for testing
|
|
14
|
+
function makeState() {
|
|
15
|
+
let overrides = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
16
|
+
return {
|
|
17
|
+
stylesheets: [],
|
|
18
|
+
cartOpen: false,
|
|
19
|
+
precartOpen: false,
|
|
20
|
+
selectedSkuIds: {},
|
|
21
|
+
licenseOptions: [],
|
|
22
|
+
collectionStyleSkus: {},
|
|
23
|
+
collectionSkus: {},
|
|
24
|
+
skuPrices: {},
|
|
25
|
+
fetchedCollectionIds: [],
|
|
26
|
+
storeModalRoute: {
|
|
27
|
+
name: 'index'
|
|
28
|
+
},
|
|
29
|
+
storeModalHistory: [],
|
|
30
|
+
orderVariableSelections: [],
|
|
31
|
+
licenseeIsBillingIdentity: null,
|
|
32
|
+
...overrides
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Shared fixtures — reusable across tests that need the full
|
|
38
|
+
// "superfamily + 3 families + 3 bundles" topology
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
function makeThunderState() {
|
|
41
|
+
let overrides = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
42
|
+
return makeState({
|
|
43
|
+
collectionStyleSkus: {
|
|
44
|
+
// Superfamily
|
|
45
|
+
'super-sku': {
|
|
46
|
+
fontStyleSkuIds: ['s1', 's2', 'ds1', 'ds2', 'cs1', 'cs2', 'vs1', 'vis1', 'dvs1', 'dvis1', 'cvs1', 'cvis1'],
|
|
47
|
+
fontStyleIds: ['f1', 'f2', 'df1', 'df2', 'cf1', 'cf2', 'vf1', 'vif1', 'dvf1', 'dvif1', 'cvf1', 'cvif1'],
|
|
48
|
+
childrenSkuIds: ['text-sku', 'display-sku', 'caption-sku', 'text-var-sku', 'text-var-italic-sku', 'display-var-sku', 'display-var-italic-sku', 'caption-var-sku', 'caption-var-italic-sku', 'text-bundle-sku', 'display-bundle-sku', 'caption-bundle-sku'],
|
|
49
|
+
name: 'Thunder Text Collection'
|
|
50
|
+
},
|
|
51
|
+
// Static families
|
|
52
|
+
'text-sku': {
|
|
53
|
+
fontStyleSkuIds: ['s1', 's2'],
|
|
54
|
+
fontStyleIds: ['f1', 'f2'],
|
|
55
|
+
childrenSkuIds: [],
|
|
56
|
+
name: 'Thunder Text'
|
|
57
|
+
},
|
|
58
|
+
'display-sku': {
|
|
59
|
+
fontStyleSkuIds: ['ds1', 'ds2'],
|
|
60
|
+
fontStyleIds: ['df1', 'df2'],
|
|
61
|
+
childrenSkuIds: [],
|
|
62
|
+
name: 'Thunder Display'
|
|
63
|
+
},
|
|
64
|
+
'caption-sku': {
|
|
65
|
+
fontStyleSkuIds: ['cs1', 'cs2'],
|
|
66
|
+
fontStyleIds: ['cf1', 'cf2'],
|
|
67
|
+
childrenSkuIds: [],
|
|
68
|
+
name: 'Thunder Caption'
|
|
69
|
+
},
|
|
70
|
+
// Variable families
|
|
71
|
+
'text-var-sku': {
|
|
72
|
+
fontStyleSkuIds: ['vs1'],
|
|
73
|
+
fontStyleIds: ['vf1'],
|
|
74
|
+
childrenSkuIds: [],
|
|
75
|
+
name: 'Thunder Text Variable'
|
|
76
|
+
},
|
|
77
|
+
'text-var-italic-sku': {
|
|
78
|
+
fontStyleSkuIds: ['vis1'],
|
|
79
|
+
fontStyleIds: ['vif1'],
|
|
80
|
+
childrenSkuIds: [],
|
|
81
|
+
name: 'Thunder Text Italic Variable'
|
|
82
|
+
},
|
|
83
|
+
'display-var-sku': {
|
|
84
|
+
fontStyleSkuIds: ['dvs1'],
|
|
85
|
+
fontStyleIds: ['dvf1'],
|
|
86
|
+
childrenSkuIds: [],
|
|
87
|
+
name: 'Thunder Display Variable'
|
|
88
|
+
},
|
|
89
|
+
'display-var-italic-sku': {
|
|
90
|
+
fontStyleSkuIds: ['dvis1'],
|
|
91
|
+
fontStyleIds: ['dvif1'],
|
|
92
|
+
childrenSkuIds: [],
|
|
93
|
+
name: 'Thunder Display Italic Variable'
|
|
94
|
+
},
|
|
95
|
+
'caption-var-sku': {
|
|
96
|
+
fontStyleSkuIds: ['cvs1'],
|
|
97
|
+
fontStyleIds: ['cvf1'],
|
|
98
|
+
childrenSkuIds: [],
|
|
99
|
+
name: 'Thunder Caption Variable'
|
|
100
|
+
},
|
|
101
|
+
'caption-var-italic-sku': {
|
|
102
|
+
fontStyleSkuIds: ['cvis1'],
|
|
103
|
+
fontStyleIds: ['cvif1'],
|
|
104
|
+
childrenSkuIds: [],
|
|
105
|
+
name: 'Thunder Caption Italic Variable'
|
|
106
|
+
},
|
|
107
|
+
// Collection bundles
|
|
108
|
+
'text-bundle-sku': {
|
|
109
|
+
fontStyleSkuIds: ['s1', 's2', 'vs1', 'vis1'],
|
|
110
|
+
fontStyleIds: ['f1', 'f2', 'vf1', 'vif1'],
|
|
111
|
+
childrenSkuIds: ['text-sku', 'text-var-sku', 'text-var-italic-sku'],
|
|
112
|
+
name: 'Thunder Text Family + Variable'
|
|
113
|
+
},
|
|
114
|
+
'display-bundle-sku': {
|
|
115
|
+
fontStyleSkuIds: ['ds1', 'ds2', 'dvs1', 'dvis1'],
|
|
116
|
+
fontStyleIds: ['df1', 'df2', 'dvf1', 'dvif1'],
|
|
117
|
+
childrenSkuIds: ['display-sku', 'display-var-sku', 'display-var-italic-sku'],
|
|
118
|
+
name: 'Thunder Display Family + Variable'
|
|
119
|
+
},
|
|
120
|
+
'caption-bundle-sku': {
|
|
121
|
+
fontStyleSkuIds: ['cs1', 'cs2', 'cvs1', 'cvis1'],
|
|
122
|
+
fontStyleIds: ['cf1', 'cf2', 'cvf1', 'cvif1'],
|
|
123
|
+
childrenSkuIds: ['caption-sku', 'caption-var-sku', 'caption-var-italic-sku'],
|
|
124
|
+
name: 'Thunder Caption Family + Variable'
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
skuPrices: {
|
|
128
|
+
'super-sku': 750,
|
|
129
|
+
'text-sku': 500,
|
|
130
|
+
'display-sku': 500,
|
|
131
|
+
'caption-sku': 500,
|
|
132
|
+
'text-var-sku': 300,
|
|
133
|
+
'text-var-italic-sku': 300,
|
|
134
|
+
'display-var-sku': 300,
|
|
135
|
+
'display-var-italic-sku': 300,
|
|
136
|
+
'caption-var-sku': 300,
|
|
137
|
+
'caption-var-italic-sku': 300,
|
|
138
|
+
'text-bundle-sku': 500,
|
|
139
|
+
'display-bundle-sku': 500,
|
|
140
|
+
'caption-bundle-sku': 500,
|
|
141
|
+
s1: 40,
|
|
142
|
+
s2: 40,
|
|
143
|
+
ds1: 40,
|
|
144
|
+
ds2: 40,
|
|
145
|
+
cs1: 40,
|
|
146
|
+
cs2: 40,
|
|
147
|
+
vs1: 40,
|
|
148
|
+
vis1: 40,
|
|
149
|
+
dvs1: 40,
|
|
150
|
+
dvis1: 40,
|
|
151
|
+
cvs1: 40,
|
|
152
|
+
cvis1: 40
|
|
153
|
+
},
|
|
154
|
+
...overrides
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// =========================================================================
|
|
159
|
+
// collContainsSkuId
|
|
160
|
+
// =========================================================================
|
|
161
|
+
(0, _vitest.describe)('collContainsSkuId', () => {
|
|
162
|
+
(0, _vitest.it)('returns true when skuId is in childrenSkuIds', () => {
|
|
163
|
+
const coll = {
|
|
164
|
+
fontStyleSkuIds: [],
|
|
165
|
+
fontStyleIds: [],
|
|
166
|
+
childrenSkuIds: ['child-sku-1', 'child-sku-2'],
|
|
167
|
+
name: 'Test'
|
|
168
|
+
};
|
|
169
|
+
(0, _vitest.expect)((0, _utils.collContainsSkuId)(coll, 'child-sku-1')).toBe(true);
|
|
170
|
+
});
|
|
171
|
+
(0, _vitest.it)('returns true when skuId is in fontStyleSkuIds', () => {
|
|
172
|
+
const coll = {
|
|
173
|
+
fontStyleSkuIds: ['style-sku-1'],
|
|
174
|
+
fontStyleIds: [],
|
|
175
|
+
childrenSkuIds: [],
|
|
176
|
+
name: 'Test'
|
|
177
|
+
};
|
|
178
|
+
(0, _vitest.expect)((0, _utils.collContainsSkuId)(coll, 'style-sku-1')).toBe(true);
|
|
179
|
+
});
|
|
180
|
+
(0, _vitest.it)('returns true when id is in fontStyleIds', () => {
|
|
181
|
+
const coll = {
|
|
182
|
+
fontStyleSkuIds: [],
|
|
183
|
+
fontStyleIds: ['style-id-1'],
|
|
184
|
+
childrenSkuIds: [],
|
|
185
|
+
name: 'Test'
|
|
186
|
+
};
|
|
187
|
+
(0, _vitest.expect)((0, _utils.collContainsSkuId)(coll, 'style-id-1')).toBe(true);
|
|
188
|
+
});
|
|
189
|
+
(0, _vitest.it)('returns false for unknown skuId', () => {
|
|
190
|
+
const coll = {
|
|
191
|
+
fontStyleSkuIds: ['style-sku-1'],
|
|
192
|
+
fontStyleIds: ['style-1'],
|
|
193
|
+
childrenSkuIds: ['child-sku-1'],
|
|
194
|
+
name: 'Test'
|
|
195
|
+
};
|
|
196
|
+
(0, _vitest.expect)((0, _utils.collContainsSkuId)(coll, 'unknown-sku')).toBe(false);
|
|
197
|
+
});
|
|
198
|
+
(0, _vitest.it)('returns false for undefined collection', () => {
|
|
199
|
+
(0, _vitest.expect)((0, _utils.collContainsSkuId)(undefined, 'any-sku')).toBe(false);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// =========================================================================
|
|
204
|
+
// isSelected
|
|
205
|
+
// =========================================================================
|
|
206
|
+
(0, _vitest.describe)('isSelected', () => {
|
|
207
|
+
(0, _vitest.it)('returns true when the SKU is directly selected', () => {
|
|
208
|
+
const state = makeState({
|
|
209
|
+
selectedSkuIds: {
|
|
210
|
+
'sku-1': true
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
(0, _vitest.expect)((0, _utils.isSelected)('sku-1')(state)).toBe(true);
|
|
214
|
+
});
|
|
215
|
+
(0, _vitest.it)('returns false when the SKU is explicitly deselected (false)', () => {
|
|
216
|
+
const state = makeState({
|
|
217
|
+
selectedSkuIds: {
|
|
218
|
+
'sku-1': false
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
(0, _vitest.expect)((0, _utils.isSelected)('sku-1')(state)).toBe(false);
|
|
222
|
+
});
|
|
223
|
+
(0, _vitest.it)('returns true when a parent collection is selected', () => {
|
|
224
|
+
const state = makeState({
|
|
225
|
+
selectedSkuIds: {
|
|
226
|
+
'bundle-sku': true
|
|
227
|
+
},
|
|
228
|
+
collectionStyleSkus: {
|
|
229
|
+
'bundle-sku': {
|
|
230
|
+
fontStyleSkuIds: ['s1'],
|
|
231
|
+
fontStyleIds: ['f1'],
|
|
232
|
+
childrenSkuIds: ['family-sku'],
|
|
233
|
+
name: 'Bundle'
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
// Style SKU contained via fontStyleSkuIds
|
|
238
|
+
(0, _vitest.expect)((0, _utils.isSelected)('s1')(state)).toBe(true);
|
|
239
|
+
// Family SKU contained via childrenSkuIds
|
|
240
|
+
(0, _vitest.expect)((0, _utils.isSelected)('family-sku')(state)).toBe(true);
|
|
241
|
+
// Font style ID contained via fontStyleIds
|
|
242
|
+
(0, _vitest.expect)((0, _utils.isSelected)('f1')(state)).toBe(true);
|
|
243
|
+
});
|
|
244
|
+
(0, _vitest.it)('returns false when a parent collection is deselected', () => {
|
|
245
|
+
const state = makeState({
|
|
246
|
+
selectedSkuIds: {
|
|
247
|
+
'bundle-sku': false,
|
|
248
|
+
's1': false
|
|
249
|
+
},
|
|
250
|
+
collectionStyleSkus: {
|
|
251
|
+
'bundle-sku': {
|
|
252
|
+
fontStyleSkuIds: ['s1'],
|
|
253
|
+
fontStyleIds: ['f1'],
|
|
254
|
+
childrenSkuIds: ['family-sku'],
|
|
255
|
+
name: 'Bundle'
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
(0, _vitest.expect)((0, _utils.isSelected)('s1')(state)).toBe(false);
|
|
260
|
+
(0, _vitest.expect)((0, _utils.isSelected)('family-sku')(state)).toBe(false);
|
|
261
|
+
});
|
|
262
|
+
(0, _vitest.it)('returns false for null skuId', () => {
|
|
263
|
+
const state = makeState();
|
|
264
|
+
(0, _vitest.expect)((0, _utils.isSelected)(null)(state)).toBe(false);
|
|
265
|
+
});
|
|
266
|
+
(0, _vitest.it)('returns true for member when bundle is selected, even if member is explicitly false', () => {
|
|
267
|
+
// After auto-selection, members are set to false but should still appear
|
|
268
|
+
// selected because the parent bundle is true
|
|
269
|
+
const state = makeState({
|
|
270
|
+
selectedSkuIds: {
|
|
271
|
+
'bundle-sku': true,
|
|
272
|
+
'family-sku': false,
|
|
273
|
+
'var-sku': false
|
|
274
|
+
},
|
|
275
|
+
collectionStyleSkus: {
|
|
276
|
+
'bundle-sku': {
|
|
277
|
+
fontStyleSkuIds: ['s1', 'vs1'],
|
|
278
|
+
fontStyleIds: ['f1', 'vf1'],
|
|
279
|
+
childrenSkuIds: ['family-sku', 'var-sku'],
|
|
280
|
+
name: 'Bundle'
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
(0, _vitest.expect)((0, _utils.isSelected)('family-sku')(state)).toBe(true);
|
|
285
|
+
(0, _vitest.expect)((0, _utils.isSelected)('var-sku')(state)).toBe(true);
|
|
286
|
+
(0, _vitest.expect)((0, _utils.isSelected)('s1')(state)).toBe(true);
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// =========================================================================
|
|
291
|
+
// collectionSkuIdsDifferences
|
|
292
|
+
// =========================================================================
|
|
293
|
+
(0, _vitest.describe)('collectionSkuIdsDifferences', () => {
|
|
294
|
+
(0, _vitest.it)('includes self when skuId matches a collection', () => {
|
|
295
|
+
const state = makeState({
|
|
296
|
+
collectionStyleSkus: {
|
|
297
|
+
'family-sku': {
|
|
298
|
+
fontStyleSkuIds: ['s1', 's2'],
|
|
299
|
+
fontStyleIds: ['f1', 'f2'],
|
|
300
|
+
childrenSkuIds: [],
|
|
301
|
+
name: 'Family'
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
skuPrices: {
|
|
305
|
+
'family-sku': 500,
|
|
306
|
+
s1: 40,
|
|
307
|
+
s2: 40
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
const diffs = (0, _utils.collectionSkuIdsDifferences)(state, 'family-sku');
|
|
311
|
+
(0, _vitest.expect)(diffs).toHaveProperty('family-sku');
|
|
312
|
+
(0, _vitest.expect)(diffs['family-sku']).toBe(500);
|
|
313
|
+
});
|
|
314
|
+
(0, _vitest.it)('includes parent collections that contain the skuId', () => {
|
|
315
|
+
const state = makeState({
|
|
316
|
+
collectionStyleSkus: {
|
|
317
|
+
'family-sku': {
|
|
318
|
+
fontStyleSkuIds: ['s1', 's2'],
|
|
319
|
+
fontStyleIds: ['f1', 'f2'],
|
|
320
|
+
childrenSkuIds: [],
|
|
321
|
+
name: 'Family'
|
|
322
|
+
},
|
|
323
|
+
'bundle-sku': {
|
|
324
|
+
fontStyleSkuIds: ['s1', 's2', 'vs1'],
|
|
325
|
+
fontStyleIds: ['f1', 'f2', 'vf1'],
|
|
326
|
+
childrenSkuIds: ['family-sku', 'var-family-sku'],
|
|
327
|
+
name: 'Bundle'
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
skuPrices: {
|
|
331
|
+
'family-sku': 500,
|
|
332
|
+
'bundle-sku': 500,
|
|
333
|
+
'var-family-sku': 300,
|
|
334
|
+
s1: 40,
|
|
335
|
+
s2: 40,
|
|
336
|
+
vs1: 40
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
const diffs = (0, _utils.collectionSkuIdsDifferences)(state, 'family-sku');
|
|
340
|
+
(0, _vitest.expect)(diffs).toHaveProperty('family-sku');
|
|
341
|
+
(0, _vitest.expect)(diffs).toHaveProperty('bundle-sku');
|
|
342
|
+
});
|
|
343
|
+
(0, _vitest.it)('subtracts already-selected items from the difference', () => {
|
|
344
|
+
const state = makeState({
|
|
345
|
+
selectedSkuIds: {
|
|
346
|
+
'family-sku': true
|
|
347
|
+
},
|
|
348
|
+
collectionStyleSkus: {
|
|
349
|
+
'family-sku': {
|
|
350
|
+
fontStyleSkuIds: ['s1', 's2'],
|
|
351
|
+
fontStyleIds: ['f1', 'f2'],
|
|
352
|
+
childrenSkuIds: [],
|
|
353
|
+
name: 'Family'
|
|
354
|
+
},
|
|
355
|
+
'bundle-sku': {
|
|
356
|
+
fontStyleSkuIds: ['s1', 's2', 'vs1'],
|
|
357
|
+
fontStyleIds: ['f1', 'f2', 'vf1'],
|
|
358
|
+
childrenSkuIds: ['family-sku', 'var-family-sku'],
|
|
359
|
+
name: 'Bundle'
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
skuPrices: {
|
|
363
|
+
'family-sku': 500,
|
|
364
|
+
'bundle-sku': 500,
|
|
365
|
+
'var-family-sku': 300
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
const diffs = (0, _utils.collectionSkuIdsDifferences)(state, 'var-family-sku');
|
|
369
|
+
(0, _vitest.expect)(diffs['bundle-sku']).toBe(0);
|
|
370
|
+
});
|
|
371
|
+
(0, _vitest.it)('treats missing prices as 0 rather than producing NaN', () => {
|
|
372
|
+
const state = makeState({
|
|
373
|
+
selectedSkuIds: {
|
|
374
|
+
'orphan-sku': true
|
|
375
|
+
},
|
|
376
|
+
collectionStyleSkus: {
|
|
377
|
+
'family-sku': {
|
|
378
|
+
fontStyleSkuIds: ['orphan-sku'],
|
|
379
|
+
fontStyleIds: [],
|
|
380
|
+
childrenSkuIds: [],
|
|
381
|
+
name: 'Family'
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
skuPrices: {
|
|
385
|
+
'family-sku': 500
|
|
386
|
+
// 'orphan-sku' deliberately missing
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
const diffs = (0, _utils.collectionSkuIdsDifferences)(state, 'orphan-sku');
|
|
390
|
+
// Missing price treated as 0, so difference = 500 - 0 = 500
|
|
391
|
+
(0, _vitest.expect)(diffs['family-sku']).toBe(500);
|
|
392
|
+
});
|
|
393
|
+
(0, _vitest.it)('only counts items that are selected true, not false', () => {
|
|
394
|
+
const state = makeState({
|
|
395
|
+
selectedSkuIds: {
|
|
396
|
+
'family-sku': false
|
|
397
|
+
},
|
|
398
|
+
collectionStyleSkus: {
|
|
399
|
+
'bundle-sku': {
|
|
400
|
+
fontStyleSkuIds: [],
|
|
401
|
+
fontStyleIds: [],
|
|
402
|
+
childrenSkuIds: ['family-sku'],
|
|
403
|
+
name: 'Bundle'
|
|
404
|
+
},
|
|
405
|
+
'family-sku': {
|
|
406
|
+
fontStyleSkuIds: [],
|
|
407
|
+
fontStyleIds: [],
|
|
408
|
+
childrenSkuIds: [],
|
|
409
|
+
name: 'Family'
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
skuPrices: {
|
|
413
|
+
'bundle-sku': 500,
|
|
414
|
+
'family-sku': 300
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
const diffs = (0, _utils.collectionSkuIdsDifferences)(state, 'family-sku');
|
|
418
|
+
// family-sku is false, not true, so it shouldn't be subtracted
|
|
419
|
+
(0, _vitest.expect)(diffs['bundle-sku']).toBe(500);
|
|
420
|
+
});
|
|
421
|
+
(0, _vitest.it)('returns empty object when no collections contain the skuId', () => {
|
|
422
|
+
const state = makeState({
|
|
423
|
+
collectionStyleSkus: {
|
|
424
|
+
'other-sku': {
|
|
425
|
+
fontStyleSkuIds: ['x1'],
|
|
426
|
+
fontStyleIds: ['xf1'],
|
|
427
|
+
childrenSkuIds: [],
|
|
428
|
+
name: 'Other'
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
skuPrices: {
|
|
432
|
+
'other-sku': 100,
|
|
433
|
+
'lonely-style': 40
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
const diffs = (0, _utils.collectionSkuIdsDifferences)(state, 'lonely-style');
|
|
437
|
+
(0, _vitest.expect)(Object.keys(diffs)).toHaveLength(0);
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// =========================================================================
|
|
442
|
+
// collectionSkuIdWithDiscount
|
|
443
|
+
// =========================================================================
|
|
444
|
+
(0, _vitest.describe)('collectionSkuIdWithDiscount', () => {
|
|
445
|
+
(0, _vitest.it)('returns null when no collection is a better deal', () => {
|
|
446
|
+
const state = makeState({
|
|
447
|
+
collectionStyleSkus: {
|
|
448
|
+
'family-sku': {
|
|
449
|
+
fontStyleSkuIds: ['s1'],
|
|
450
|
+
fontStyleIds: ['f1'],
|
|
451
|
+
childrenSkuIds: [],
|
|
452
|
+
name: 'Family'
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
skuPrices: {
|
|
456
|
+
s1: 40,
|
|
457
|
+
'family-sku': 500
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
const [collId, amount] = (0, _reducer.collectionSkuIdWithDiscount)(state, 's1');
|
|
461
|
+
(0, _vitest.expect)(collId).toBe(null);
|
|
462
|
+
(0, _vitest.expect)(amount).toBe(null);
|
|
463
|
+
});
|
|
464
|
+
(0, _vitest.it)('returns parent collection when it is cheaper for individual style', () => {
|
|
465
|
+
const state = makeState({
|
|
466
|
+
selectedSkuIds: {
|
|
467
|
+
s1: true,
|
|
468
|
+
s2: true,
|
|
469
|
+
s3: true,
|
|
470
|
+
s4: true
|
|
471
|
+
},
|
|
472
|
+
collectionStyleSkus: {
|
|
473
|
+
'family-sku': {
|
|
474
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5'],
|
|
475
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5'],
|
|
476
|
+
childrenSkuIds: [],
|
|
477
|
+
name: 'Family'
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
skuPrices: {
|
|
481
|
+
'family-sku': 200,
|
|
482
|
+
s1: 40,
|
|
483
|
+
s2: 40,
|
|
484
|
+
s3: 40,
|
|
485
|
+
s4: 40,
|
|
486
|
+
s5: 40
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
const [collId, amount] = (0, _reducer.collectionSkuIdWithDiscount)(state, 's5');
|
|
490
|
+
(0, _vitest.expect)(collId).toBe('family-sku');
|
|
491
|
+
(0, _vitest.expect)(amount).toBe(40);
|
|
492
|
+
});
|
|
493
|
+
(0, _vitest.it)('prefers collection bundle over self when prices are equal', () => {
|
|
494
|
+
const state = makeState({
|
|
495
|
+
collectionStyleSkus: {
|
|
496
|
+
'family-sku': {
|
|
497
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5'],
|
|
498
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5'],
|
|
499
|
+
childrenSkuIds: [],
|
|
500
|
+
name: 'Thunder Text'
|
|
501
|
+
},
|
|
502
|
+
'var-family-sku': {
|
|
503
|
+
fontStyleSkuIds: ['vs1'],
|
|
504
|
+
fontStyleIds: ['vf1'],
|
|
505
|
+
childrenSkuIds: [],
|
|
506
|
+
name: 'Thunder Text Variable'
|
|
507
|
+
},
|
|
508
|
+
'var-italic-sku': {
|
|
509
|
+
fontStyleSkuIds: ['vis1'],
|
|
510
|
+
fontStyleIds: ['vif1'],
|
|
511
|
+
childrenSkuIds: [],
|
|
512
|
+
name: 'Thunder Text Italic Variable'
|
|
513
|
+
},
|
|
514
|
+
'bundle-sku': {
|
|
515
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5', 'vs1', 'vis1'],
|
|
516
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5', 'vf1', 'vif1'],
|
|
517
|
+
childrenSkuIds: ['family-sku', 'var-family-sku', 'var-italic-sku'],
|
|
518
|
+
name: 'Thunder Text Family + Variable'
|
|
519
|
+
}
|
|
520
|
+
},
|
|
521
|
+
skuPrices: {
|
|
522
|
+
'family-sku': 500,
|
|
523
|
+
'bundle-sku': 500,
|
|
524
|
+
'var-family-sku': 300,
|
|
525
|
+
'var-italic-sku': 300,
|
|
526
|
+
s1: 40,
|
|
527
|
+
s2: 40,
|
|
528
|
+
s3: 40,
|
|
529
|
+
s4: 40,
|
|
530
|
+
s5: 40,
|
|
531
|
+
vs1: 40,
|
|
532
|
+
vis1: 40
|
|
533
|
+
}
|
|
534
|
+
});
|
|
535
|
+
const [collId, amount] = (0, _reducer.collectionSkuIdWithDiscount)(state, 'family-sku');
|
|
536
|
+
(0, _vitest.expect)(collId).toBe('bundle-sku');
|
|
537
|
+
(0, _vitest.expect)(amount).toBe(500);
|
|
538
|
+
});
|
|
539
|
+
(0, _vitest.it)('prefers collection bundle regardless of object key order', () => {
|
|
540
|
+
const state = makeState({
|
|
541
|
+
collectionStyleSkus: {
|
|
542
|
+
'bundle-sku': {
|
|
543
|
+
fontStyleSkuIds: ['s1', 's2', 'vs1'],
|
|
544
|
+
fontStyleIds: ['f1', 'f2', 'vf1'],
|
|
545
|
+
childrenSkuIds: ['family-sku', 'var-family-sku'],
|
|
546
|
+
name: 'Bundle'
|
|
547
|
+
},
|
|
548
|
+
'family-sku': {
|
|
549
|
+
fontStyleSkuIds: ['s1', 's2'],
|
|
550
|
+
fontStyleIds: ['f1', 'f2'],
|
|
551
|
+
childrenSkuIds: [],
|
|
552
|
+
name: 'Family'
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
skuPrices: {
|
|
556
|
+
'family-sku': 500,
|
|
557
|
+
'bundle-sku': 500,
|
|
558
|
+
'var-family-sku': 300
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
const [collId] = (0, _reducer.collectionSkuIdWithDiscount)(state, 'family-sku');
|
|
562
|
+
(0, _vitest.expect)(collId).toBe('bundle-sku');
|
|
563
|
+
});
|
|
564
|
+
(0, _vitest.it)('still returns self for display pricing (superfamily with no parent)', () => {
|
|
565
|
+
const state = makeState({
|
|
566
|
+
selectedSkuIds: {
|
|
567
|
+
'family-sku': true
|
|
568
|
+
},
|
|
569
|
+
collectionStyleSkus: {
|
|
570
|
+
'superfamily-sku': {
|
|
571
|
+
fontStyleSkuIds: ['s1', 's2', 'ds1', 'ds2'],
|
|
572
|
+
fontStyleIds: ['f1', 'f2', 'df1', 'df2'],
|
|
573
|
+
childrenSkuIds: ['family-sku', 'display-family-sku'],
|
|
574
|
+
name: 'Superfamily'
|
|
575
|
+
},
|
|
576
|
+
'family-sku': {
|
|
577
|
+
fontStyleSkuIds: ['s1', 's2'],
|
|
578
|
+
fontStyleIds: ['f1', 'f2'],
|
|
579
|
+
childrenSkuIds: [],
|
|
580
|
+
name: 'Family'
|
|
581
|
+
},
|
|
582
|
+
'display-family-sku': {
|
|
583
|
+
fontStyleSkuIds: ['ds1', 'ds2'],
|
|
584
|
+
fontStyleIds: ['df1', 'df2'],
|
|
585
|
+
childrenSkuIds: [],
|
|
586
|
+
name: 'Display'
|
|
587
|
+
}
|
|
588
|
+
},
|
|
589
|
+
skuPrices: {
|
|
590
|
+
'superfamily-sku': 750,
|
|
591
|
+
'family-sku': 500,
|
|
592
|
+
'display-family-sku': 500
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
const [collId, amount] = (0, _reducer.collectionSkuIdWithDiscount)(state, 'superfamily-sku');
|
|
596
|
+
(0, _vitest.expect)(collId).toBe('superfamily-sku');
|
|
597
|
+
(0, _vitest.expect)(amount).toBe(250);
|
|
598
|
+
});
|
|
599
|
+
(0, _vitest.it)('prefers bundle at $0 when family is already selected', () => {
|
|
600
|
+
const state = makeState({
|
|
601
|
+
selectedSkuIds: {
|
|
602
|
+
'family-sku': true
|
|
603
|
+
},
|
|
604
|
+
collectionStyleSkus: {
|
|
605
|
+
'family-sku': {
|
|
606
|
+
fontStyleSkuIds: ['s1', 's2'],
|
|
607
|
+
fontStyleIds: ['f1', 'f2'],
|
|
608
|
+
childrenSkuIds: [],
|
|
609
|
+
name: 'Family'
|
|
610
|
+
},
|
|
611
|
+
'var-family-sku': {
|
|
612
|
+
fontStyleSkuIds: ['vs1'],
|
|
613
|
+
fontStyleIds: ['vf1'],
|
|
614
|
+
childrenSkuIds: [],
|
|
615
|
+
name: 'Variable'
|
|
616
|
+
},
|
|
617
|
+
'bundle-sku': {
|
|
618
|
+
fontStyleSkuIds: ['s1', 's2', 'vs1'],
|
|
619
|
+
fontStyleIds: ['f1', 'f2', 'vf1'],
|
|
620
|
+
childrenSkuIds: ['family-sku', 'var-family-sku'],
|
|
621
|
+
name: 'Bundle'
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
skuPrices: {
|
|
625
|
+
'family-sku': 500,
|
|
626
|
+
'var-family-sku': 300,
|
|
627
|
+
'bundle-sku': 500
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
const [collId, amount] = (0, _reducer.collectionSkuIdWithDiscount)(state, 'var-family-sku');
|
|
631
|
+
(0, _vitest.expect)(collId).toBe('bundle-sku');
|
|
632
|
+
(0, _vitest.expect)(amount).toBe(0);
|
|
633
|
+
});
|
|
634
|
+
(0, _vitest.it)('does not upgrade when collection bundle is more expensive', () => {
|
|
635
|
+
const state = makeState({
|
|
636
|
+
collectionStyleSkus: {
|
|
637
|
+
'family-sku': {
|
|
638
|
+
fontStyleSkuIds: ['s1', 's2'],
|
|
639
|
+
fontStyleIds: ['f1', 'f2'],
|
|
640
|
+
childrenSkuIds: [],
|
|
641
|
+
name: 'Family'
|
|
642
|
+
},
|
|
643
|
+
'bundle-sku': {
|
|
644
|
+
fontStyleSkuIds: ['s1', 's2', 'vs1'],
|
|
645
|
+
fontStyleIds: ['f1', 'f2', 'vf1'],
|
|
646
|
+
childrenSkuIds: ['family-sku', 'var-family-sku'],
|
|
647
|
+
name: 'Bundle'
|
|
648
|
+
}
|
|
649
|
+
},
|
|
650
|
+
skuPrices: {
|
|
651
|
+
'family-sku': 500,
|
|
652
|
+
'bundle-sku': 600
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
const [collId] = (0, _reducer.collectionSkuIdWithDiscount)(state, 'family-sku');
|
|
656
|
+
(0, _vitest.expect)(collId).toBe('family-sku');
|
|
657
|
+
});
|
|
658
|
+
(0, _vitest.it)('returns null when the selected SKU has no price (undefined)', () => {
|
|
659
|
+
// If skuPrices[skuId] is undefined, the filter comparison
|
|
660
|
+
// `difference <= undefined` is always false → no collection found
|
|
661
|
+
const state = makeState({
|
|
662
|
+
collectionStyleSkus: {
|
|
663
|
+
'family-sku': {
|
|
664
|
+
fontStyleSkuIds: ['s1'],
|
|
665
|
+
fontStyleIds: ['f1'],
|
|
666
|
+
childrenSkuIds: [],
|
|
667
|
+
name: 'Family'
|
|
668
|
+
}
|
|
669
|
+
},
|
|
670
|
+
skuPrices: {
|
|
671
|
+
'family-sku': 500
|
|
672
|
+
} // s1 deliberately missing
|
|
673
|
+
});
|
|
674
|
+
const [collId, amount] = (0, _reducer.collectionSkuIdWithDiscount)(state, 's1');
|
|
675
|
+
(0, _vitest.expect)(collId).toBe(null);
|
|
676
|
+
(0, _vitest.expect)(amount).toBe(null);
|
|
677
|
+
});
|
|
678
|
+
(0, _vitest.it)('treats collections with missing price as price 0', () => {
|
|
679
|
+
// A collection with no price entry is treated as $0, meaning its
|
|
680
|
+
// difference = 0 - sum_selected. With nothing selected, diff = 0.
|
|
681
|
+
const state = makeState({
|
|
682
|
+
collectionStyleSkus: {
|
|
683
|
+
'ghost-bundle': {
|
|
684
|
+
fontStyleSkuIds: ['s1'],
|
|
685
|
+
fontStyleIds: ['f1'],
|
|
686
|
+
childrenSkuIds: ['family-sku'],
|
|
687
|
+
name: 'Ghost Bundle'
|
|
688
|
+
},
|
|
689
|
+
'family-sku': {
|
|
690
|
+
fontStyleSkuIds: ['s1'],
|
|
691
|
+
fontStyleIds: ['f1'],
|
|
692
|
+
childrenSkuIds: [],
|
|
693
|
+
name: 'Family'
|
|
694
|
+
}
|
|
695
|
+
},
|
|
696
|
+
skuPrices: {
|
|
697
|
+
'family-sku': 500,
|
|
698
|
+
s1: 40
|
|
699
|
+
}
|
|
700
|
+
// 'ghost-bundle' price deliberately missing → treated as 0
|
|
701
|
+
});
|
|
702
|
+
const [collId, amount] = (0, _reducer.collectionSkuIdWithDiscount)(state, 'family-sku');
|
|
703
|
+
// ghost-bundle diff = 0, family diff = 500. ghost-bundle is cheaper.
|
|
704
|
+
// 0 <= 500 ✓ → ghost-bundle wins
|
|
705
|
+
(0, _vitest.expect)(collId).toBe('ghost-bundle');
|
|
706
|
+
(0, _vitest.expect)(amount).toBe(0);
|
|
707
|
+
});
|
|
708
|
+
(0, _vitest.it)('handles zero-price SKUs without errors', () => {
|
|
709
|
+
const state = makeState({
|
|
710
|
+
collectionStyleSkus: {
|
|
711
|
+
'free-family': {
|
|
712
|
+
fontStyleSkuIds: ['fs1'],
|
|
713
|
+
fontStyleIds: ['ff1'],
|
|
714
|
+
childrenSkuIds: [],
|
|
715
|
+
name: 'Free Family'
|
|
716
|
+
},
|
|
717
|
+
'free-bundle': {
|
|
718
|
+
fontStyleSkuIds: ['fs1', 'fvs1'],
|
|
719
|
+
fontStyleIds: ['ff1', 'fvf1'],
|
|
720
|
+
childrenSkuIds: ['free-family', 'free-var'],
|
|
721
|
+
name: 'Free Bundle'
|
|
722
|
+
}
|
|
723
|
+
},
|
|
724
|
+
skuPrices: {
|
|
725
|
+
'free-family': 0,
|
|
726
|
+
'free-bundle': 0,
|
|
727
|
+
'free-var': 0,
|
|
728
|
+
fs1: 0,
|
|
729
|
+
fvs1: 0
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
// Both have diff = 0, filter: 0 <= 0 ✓, tie-break prefers bundle
|
|
734
|
+
const [collId, amount] = (0, _reducer.collectionSkuIdWithDiscount)(state, 'free-family');
|
|
735
|
+
(0, _vitest.expect)(collId).toBe('free-bundle');
|
|
736
|
+
(0, _vitest.expect)(amount).toBe(0);
|
|
737
|
+
});
|
|
738
|
+
(0, _vitest.it)('handles negative difference (collection cheaper than sum of selected)', () => {
|
|
739
|
+
// Unusual but possible: if prices change or a collection is deeply discounted
|
|
740
|
+
const state = makeState({
|
|
741
|
+
selectedSkuIds: {
|
|
742
|
+
s1: true,
|
|
743
|
+
s2: true,
|
|
744
|
+
s3: true
|
|
745
|
+
},
|
|
746
|
+
collectionStyleSkus: {
|
|
747
|
+
'family-sku': {
|
|
748
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4'],
|
|
749
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4'],
|
|
750
|
+
childrenSkuIds: [],
|
|
751
|
+
name: 'Family'
|
|
752
|
+
}
|
|
753
|
+
},
|
|
754
|
+
skuPrices: {
|
|
755
|
+
'family-sku': 100,
|
|
756
|
+
s1: 40,
|
|
757
|
+
s2: 40,
|
|
758
|
+
s3: 40,
|
|
759
|
+
s4: 40
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
// Family costs $100, 3 styles selected = $120, diff = $100 - $120 = -$20
|
|
764
|
+
// This means the collection is $20 cheaper than what's already selected
|
|
765
|
+
const [collId, amount] = (0, _reducer.collectionSkuIdWithDiscount)(state, 's4');
|
|
766
|
+
(0, _vitest.expect)(collId).toBe('family-sku');
|
|
767
|
+
(0, _vitest.expect)(amount).toBe(-20);
|
|
768
|
+
});
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
// =========================================================================
|
|
772
|
+
// reducer SELECT_SKU_ID — core auto-selection
|
|
773
|
+
// =========================================================================
|
|
774
|
+
(0, _vitest.describe)('reducer SELECT_SKU_ID with collection bundles', () => {
|
|
775
|
+
(0, _vitest.it)('auto-selects collection bundle when selecting a family at the same price', () => {
|
|
776
|
+
const state = makeThunderState();
|
|
777
|
+
const newState = (0, _reducer.default)(state, {
|
|
778
|
+
type: 'SELECT_SKU_ID',
|
|
779
|
+
skuId: 'text-sku',
|
|
780
|
+
selected: true
|
|
781
|
+
});
|
|
782
|
+
(0, _vitest.expect)(newState.selectedSkuIds['text-bundle-sku']).toBe(true);
|
|
783
|
+
(0, _vitest.expect)(newState.selectedSkuIds['text-sku']).toBe(false);
|
|
784
|
+
(0, _vitest.expect)(newState.selectedSkuIds['text-var-sku']).toBe(false);
|
|
785
|
+
(0, _vitest.expect)(newState.selectedSkuIds['text-var-italic-sku']).toBe(false);
|
|
786
|
+
});
|
|
787
|
+
(0, _vitest.it)('does not auto-select bundle when bundle is more expensive', () => {
|
|
788
|
+
const state = makeThunderState({
|
|
789
|
+
skuPrices: {
|
|
790
|
+
...makeThunderState().skuPrices,
|
|
791
|
+
'text-bundle-sku': 600 // more expensive than text-sku ($500)
|
|
792
|
+
}
|
|
793
|
+
});
|
|
794
|
+
const newState = (0, _reducer.default)(state, {
|
|
795
|
+
type: 'SELECT_SKU_ID',
|
|
796
|
+
skuId: 'text-sku',
|
|
797
|
+
selected: true
|
|
798
|
+
});
|
|
799
|
+
(0, _vitest.expect)(newState.selectedSkuIds['text-sku']).toBe(true);
|
|
800
|
+
(0, _vitest.expect)(newState.selectedSkuIds['text-bundle-sku']).toBeUndefined();
|
|
801
|
+
});
|
|
802
|
+
(0, _vitest.it)('auto-selects bundle when clicking variable family with static already selected', () => {
|
|
803
|
+
const state = makeThunderState({
|
|
804
|
+
selectedSkuIds: {
|
|
805
|
+
'text-sku': true
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
const newState = (0, _reducer.default)(state, {
|
|
809
|
+
type: 'SELECT_SKU_ID',
|
|
810
|
+
skuId: 'text-var-sku',
|
|
811
|
+
selected: true
|
|
812
|
+
});
|
|
813
|
+
(0, _vitest.expect)(newState.selectedSkuIds['text-bundle-sku']).toBe(true);
|
|
814
|
+
(0, _vitest.expect)(newState.selectedSkuIds['text-sku']).toBe(false);
|
|
815
|
+
(0, _vitest.expect)(newState.selectedSkuIds['text-var-sku']).toBe(false);
|
|
816
|
+
});
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
// =========================================================================
|
|
820
|
+
// reducer SELECT_SKU_ID — multiple bundles don't interfere
|
|
821
|
+
// =========================================================================
|
|
822
|
+
(0, _vitest.describe)('reducer SELECT_SKU_ID with multiple bundles', () => {
|
|
823
|
+
(0, _vitest.it)('only selects the correct bundle for the family being clicked', () => {
|
|
824
|
+
const state = makeThunderState();
|
|
825
|
+
const newState = (0, _reducer.default)(state, {
|
|
826
|
+
type: 'SELECT_SKU_ID',
|
|
827
|
+
skuId: 'display-sku',
|
|
828
|
+
selected: true
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
// Display bundle should be selected
|
|
832
|
+
(0, _vitest.expect)(newState.selectedSkuIds['display-bundle-sku']).toBe(true);
|
|
833
|
+
(0, _vitest.expect)(newState.selectedSkuIds['display-sku']).toBe(false);
|
|
834
|
+
// Text and caption bundles should be untouched
|
|
835
|
+
(0, _vitest.expect)(newState.selectedSkuIds['text-bundle-sku']).toBeUndefined();
|
|
836
|
+
(0, _vitest.expect)(newState.selectedSkuIds['caption-bundle-sku']).toBeUndefined();
|
|
837
|
+
});
|
|
838
|
+
(0, _vitest.it)('upgrades to superfamily when selecting a second family bundle', () => {
|
|
839
|
+
// Selecting two families ($500+$500) costs more than the superfamily ($750),
|
|
840
|
+
// so the system correctly upgrades to the superfamily on the second click
|
|
841
|
+
const state = makeThunderState();
|
|
842
|
+
let newState = (0, _reducer.default)(state, {
|
|
843
|
+
type: 'SELECT_SKU_ID',
|
|
844
|
+
skuId: 'text-sku',
|
|
845
|
+
selected: true
|
|
846
|
+
});
|
|
847
|
+
(0, _vitest.expect)(newState.selectedSkuIds['text-bundle-sku']).toBe(true);
|
|
848
|
+
newState = (0, _reducer.default)(newState, {
|
|
849
|
+
type: 'SELECT_SKU_ID',
|
|
850
|
+
skuId: 'display-sku',
|
|
851
|
+
selected: true
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
// Superfamily ($750) beats two bundles ($500 + $500 = $1000)
|
|
855
|
+
(0, _vitest.expect)(newState.selectedSkuIds['super-sku']).toBe(true);
|
|
856
|
+
// Previous bundle should be deselected (subsumed by superfamily)
|
|
857
|
+
(0, _vitest.expect)(newState.selectedSkuIds['text-bundle-sku']).toBe(false);
|
|
858
|
+
(0, _vitest.expect)(newState.selectedSkuIds['display-bundle-sku']).toBe(false);
|
|
859
|
+
});
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
// =========================================================================
|
|
863
|
+
// reducer SELECT_SKU_ID — deselection flow
|
|
864
|
+
// =========================================================================
|
|
865
|
+
(0, _vitest.describe)('reducer deselection', () => {
|
|
866
|
+
(0, _vitest.it)('deselecting a bundle member deselects the entire bundle', () => {
|
|
867
|
+
// Start with the text bundle selected (members are false)
|
|
868
|
+
const state = makeThunderState({
|
|
869
|
+
selectedSkuIds: {
|
|
870
|
+
'text-bundle-sku': true,
|
|
871
|
+
'text-sku': false,
|
|
872
|
+
'text-var-sku': false,
|
|
873
|
+
'text-var-italic-sku': false,
|
|
874
|
+
s1: false,
|
|
875
|
+
s2: false,
|
|
876
|
+
vs1: false,
|
|
877
|
+
vis1: false
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
// User clicks to deselect "Thunder Text Variable"
|
|
882
|
+
const newState = (0, _reducer.default)(state, {
|
|
883
|
+
type: 'SELECT_SKU_ID',
|
|
884
|
+
skuId: 'text-var-sku',
|
|
885
|
+
selected: false
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
// The bundle should be deselected
|
|
889
|
+
(0, _vitest.expect)(newState.selectedSkuIds['text-bundle-sku']).toBe(false);
|
|
890
|
+
});
|
|
891
|
+
(0, _vitest.it)('deselecting a font style deselects the parent bundle', () => {
|
|
892
|
+
const state = makeThunderState({
|
|
893
|
+
selectedSkuIds: {
|
|
894
|
+
'text-bundle-sku': true,
|
|
895
|
+
'text-sku': false,
|
|
896
|
+
'text-var-sku': false,
|
|
897
|
+
'text-var-italic-sku': false,
|
|
898
|
+
s1: false,
|
|
899
|
+
s2: false,
|
|
900
|
+
vs1: false,
|
|
901
|
+
vis1: false
|
|
902
|
+
}
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
// User clicks to deselect individual style "s1"
|
|
906
|
+
const newState = (0, _reducer.default)(state, {
|
|
907
|
+
type: 'SELECT_SKU_ID',
|
|
908
|
+
skuId: 's1',
|
|
909
|
+
selected: false
|
|
910
|
+
});
|
|
911
|
+
(0, _vitest.expect)(newState.selectedSkuIds['text-bundle-sku']).toBe(false);
|
|
912
|
+
});
|
|
913
|
+
(0, _vitest.it)('deselecting a family bundle deselects the parent collection bundle (transitive)', () => {
|
|
914
|
+
// Collection bundle is selected. A family bundle (style package) within
|
|
915
|
+
// it is shown as selected via transitive isSelected. Clicking to deselect
|
|
916
|
+
// it should find and deselect the collection bundle.
|
|
917
|
+
const state = makeState({
|
|
918
|
+
selectedSkuIds: {
|
|
919
|
+
'coll-bundle-sku': true,
|
|
920
|
+
'family-sku': false,
|
|
921
|
+
'var-sku': false,
|
|
922
|
+
'pkg-upright-sku': false,
|
|
923
|
+
'pkg-italic-sku': false,
|
|
924
|
+
s1: false,
|
|
925
|
+
s2: false,
|
|
926
|
+
si1: false,
|
|
927
|
+
si2: false,
|
|
928
|
+
vs1: false
|
|
929
|
+
},
|
|
930
|
+
collectionStyleSkus: {
|
|
931
|
+
'coll-bundle-sku': {
|
|
932
|
+
fontStyleSkuIds: ['s1', 's2', 'si1', 'si2', 'vs1'],
|
|
933
|
+
fontStyleIds: ['f1', 'f2', 'fi1', 'fi2', 'vf1'],
|
|
934
|
+
childrenSkuIds: ['family-sku', 'var-sku'],
|
|
935
|
+
name: 'Text + Variable'
|
|
936
|
+
},
|
|
937
|
+
'family-sku': {
|
|
938
|
+
fontStyleSkuIds: ['s1', 's2', 'si1', 'si2'],
|
|
939
|
+
fontStyleIds: ['f1', 'f2', 'fi1', 'fi2'],
|
|
940
|
+
childrenSkuIds: ['pkg-upright-sku', 'pkg-italic-sku'],
|
|
941
|
+
name: 'Text Family'
|
|
942
|
+
},
|
|
943
|
+
'var-sku': {
|
|
944
|
+
fontStyleSkuIds: ['vs1'],
|
|
945
|
+
fontStyleIds: ['vf1'],
|
|
946
|
+
childrenSkuIds: [],
|
|
947
|
+
name: 'Variable'
|
|
948
|
+
},
|
|
949
|
+
'pkg-upright-sku': {
|
|
950
|
+
fontStyleSkuIds: ['s1', 's2'],
|
|
951
|
+
fontStyleIds: ['f1', 'f2'],
|
|
952
|
+
childrenSkuIds: [],
|
|
953
|
+
name: 'Package Text'
|
|
954
|
+
},
|
|
955
|
+
'pkg-italic-sku': {
|
|
956
|
+
fontStyleSkuIds: ['si1', 'si2'],
|
|
957
|
+
fontStyleIds: ['fi1', 'fi2'],
|
|
958
|
+
childrenSkuIds: [],
|
|
959
|
+
name: 'Package Italic'
|
|
960
|
+
}
|
|
961
|
+
},
|
|
962
|
+
skuPrices: {
|
|
963
|
+
'coll-bundle-sku': 500,
|
|
964
|
+
'family-sku': 500,
|
|
965
|
+
'var-sku': 300,
|
|
966
|
+
'pkg-upright-sku': 250,
|
|
967
|
+
'pkg-italic-sku': 250,
|
|
968
|
+
s1: 100,
|
|
969
|
+
s2: 100,
|
|
970
|
+
si1: 100,
|
|
971
|
+
si2: 100,
|
|
972
|
+
vs1: 100
|
|
973
|
+
}
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
// Bundle shows as selected via transitive isSelected
|
|
977
|
+
(0, _vitest.expect)((0, _utils.isSelected)('pkg-upright-sku')(state)).toBe(true);
|
|
978
|
+
|
|
979
|
+
// Click to deselect the bundle
|
|
980
|
+
const newState = (0, _reducer.default)(state, {
|
|
981
|
+
type: 'SELECT_SKU_ID',
|
|
982
|
+
skuId: 'pkg-upright-sku',
|
|
983
|
+
selected: false
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
// Collection bundle should be deselected
|
|
987
|
+
(0, _vitest.expect)(newState.selectedSkuIds['coll-bundle-sku']).toBe(false);
|
|
988
|
+
// Nothing should be selected anymore
|
|
989
|
+
(0, _vitest.expect)((0, _utils.isSelected)('pkg-upright-sku')(newState)).toBe(false);
|
|
990
|
+
(0, _vitest.expect)((0, _utils.isSelected)('family-sku')(newState)).toBe(false);
|
|
991
|
+
(0, _vitest.expect)((0, _utils.isSelected)('s1')(newState)).toBe(false);
|
|
992
|
+
});
|
|
993
|
+
(0, _vitest.it)('deselecting a bundle does not affect other bundles', () => {
|
|
994
|
+
const state = makeThunderState({
|
|
995
|
+
selectedSkuIds: {
|
|
996
|
+
'text-bundle-sku': true,
|
|
997
|
+
'text-sku': false,
|
|
998
|
+
'text-var-sku': false,
|
|
999
|
+
'text-var-italic-sku': false,
|
|
1000
|
+
s1: false,
|
|
1001
|
+
s2: false,
|
|
1002
|
+
vs1: false,
|
|
1003
|
+
vis1: false,
|
|
1004
|
+
'display-bundle-sku': true,
|
|
1005
|
+
'display-sku': false,
|
|
1006
|
+
'display-var-sku': false,
|
|
1007
|
+
'display-var-italic-sku': false,
|
|
1008
|
+
ds1: false,
|
|
1009
|
+
ds2: false,
|
|
1010
|
+
dvs1: false,
|
|
1011
|
+
dvis1: false
|
|
1012
|
+
}
|
|
1013
|
+
});
|
|
1014
|
+
const newState = (0, _reducer.default)(state, {
|
|
1015
|
+
type: 'SELECT_SKU_ID',
|
|
1016
|
+
skuId: 'text-var-sku',
|
|
1017
|
+
selected: false
|
|
1018
|
+
});
|
|
1019
|
+
(0, _vitest.expect)(newState.selectedSkuIds['text-bundle-sku']).toBe(false);
|
|
1020
|
+
// Display bundle should be unaffected
|
|
1021
|
+
(0, _vitest.expect)(newState.selectedSkuIds['display-bundle-sku']).toBe(true);
|
|
1022
|
+
});
|
|
1023
|
+
});
|
|
1024
|
+
|
|
1025
|
+
// =========================================================================
|
|
1026
|
+
// reducer SELECT_SKU_ID — re-selection after deselection
|
|
1027
|
+
// =========================================================================
|
|
1028
|
+
(0, _vitest.describe)('reducer re-selection cycle', () => {
|
|
1029
|
+
(0, _vitest.it)('re-selecting family after bundle was deselected re-selects the bundle', () => {
|
|
1030
|
+
// 1. Start with bundle selected
|
|
1031
|
+
const state = makeThunderState({
|
|
1032
|
+
selectedSkuIds: {
|
|
1033
|
+
'text-bundle-sku': true,
|
|
1034
|
+
'text-sku': false,
|
|
1035
|
+
'text-var-sku': false,
|
|
1036
|
+
'text-var-italic-sku': false,
|
|
1037
|
+
s1: false,
|
|
1038
|
+
s2: false,
|
|
1039
|
+
vs1: false,
|
|
1040
|
+
vis1: false
|
|
1041
|
+
}
|
|
1042
|
+
});
|
|
1043
|
+
|
|
1044
|
+
// 2. Deselect a member → bundle goes away
|
|
1045
|
+
const afterDeselect = (0, _reducer.default)(state, {
|
|
1046
|
+
type: 'SELECT_SKU_ID',
|
|
1047
|
+
skuId: 'text-var-sku',
|
|
1048
|
+
selected: false
|
|
1049
|
+
});
|
|
1050
|
+
(0, _vitest.expect)(afterDeselect.selectedSkuIds['text-bundle-sku']).toBe(false);
|
|
1051
|
+
|
|
1052
|
+
// 3. Re-select the family → bundle should come back
|
|
1053
|
+
const afterReselect = (0, _reducer.default)(afterDeselect, {
|
|
1054
|
+
type: 'SELECT_SKU_ID',
|
|
1055
|
+
skuId: 'text-sku',
|
|
1056
|
+
selected: true
|
|
1057
|
+
});
|
|
1058
|
+
(0, _vitest.expect)(afterReselect.selectedSkuIds['text-bundle-sku']).toBe(true);
|
|
1059
|
+
(0, _vitest.expect)(afterReselect.selectedSkuIds['text-sku']).toBe(false);
|
|
1060
|
+
(0, _vitest.expect)(afterReselect.selectedSkuIds['text-var-sku']).toBe(false);
|
|
1061
|
+
});
|
|
1062
|
+
});
|
|
1063
|
+
|
|
1064
|
+
// =========================================================================
|
|
1065
|
+
// reducer SELECT_SKU_ID — superfamily upgrade
|
|
1066
|
+
// =========================================================================
|
|
1067
|
+
(0, _vitest.describe)('reducer superfamily upgrade', () => {
|
|
1068
|
+
(0, _vitest.it)('upgrades to superfamily when cumulative selections make it cheaper', () => {
|
|
1069
|
+
// With two bundles selected ($500 each = $1000 total), the superfamily
|
|
1070
|
+
// at $750 should be a better deal when selecting the third family
|
|
1071
|
+
const state = makeThunderState({
|
|
1072
|
+
selectedSkuIds: {
|
|
1073
|
+
'text-bundle-sku': true,
|
|
1074
|
+
'text-sku': false,
|
|
1075
|
+
'text-var-sku': false,
|
|
1076
|
+
'text-var-italic-sku': false,
|
|
1077
|
+
s1: false,
|
|
1078
|
+
s2: false,
|
|
1079
|
+
vs1: false,
|
|
1080
|
+
vis1: false,
|
|
1081
|
+
'display-bundle-sku': true,
|
|
1082
|
+
'display-sku': false,
|
|
1083
|
+
'display-var-sku': false,
|
|
1084
|
+
'display-var-italic-sku': false,
|
|
1085
|
+
ds1: false,
|
|
1086
|
+
ds2: false,
|
|
1087
|
+
dvs1: false,
|
|
1088
|
+
dvis1: false
|
|
1089
|
+
}
|
|
1090
|
+
});
|
|
1091
|
+
const newState = (0, _reducer.default)(state, {
|
|
1092
|
+
type: 'SELECT_SKU_ID',
|
|
1093
|
+
skuId: 'caption-sku',
|
|
1094
|
+
selected: true
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
// Superfamily ($750) - two bundles ($500+$500) = -$250 difference
|
|
1098
|
+
// Caption-sku costs $500, so -$250 <= $500 → superfamily is preferred
|
|
1099
|
+
(0, _vitest.expect)(newState.selectedSkuIds['super-sku']).toBe(true);
|
|
1100
|
+
});
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
// =========================================================================
|
|
1104
|
+
// reducer SELECT_SKU_ID — individual style to family upgrade
|
|
1105
|
+
// =========================================================================
|
|
1106
|
+
(0, _vitest.describe)('reducer individual style upgrade', () => {
|
|
1107
|
+
(0, _vitest.it)('upgrades to family when enough styles are individually selected', () => {
|
|
1108
|
+
const state = makeState({
|
|
1109
|
+
selectedSkuIds: {
|
|
1110
|
+
s1: true,
|
|
1111
|
+
s2: true,
|
|
1112
|
+
s3: true,
|
|
1113
|
+
s4: true
|
|
1114
|
+
},
|
|
1115
|
+
collectionStyleSkus: {
|
|
1116
|
+
'family-sku': {
|
|
1117
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5'],
|
|
1118
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5'],
|
|
1119
|
+
childrenSkuIds: [],
|
|
1120
|
+
name: 'Family'
|
|
1121
|
+
}
|
|
1122
|
+
},
|
|
1123
|
+
skuPrices: {
|
|
1124
|
+
'family-sku': 200,
|
|
1125
|
+
s1: 40,
|
|
1126
|
+
s2: 40,
|
|
1127
|
+
s3: 40,
|
|
1128
|
+
s4: 40,
|
|
1129
|
+
s5: 40
|
|
1130
|
+
}
|
|
1131
|
+
});
|
|
1132
|
+
|
|
1133
|
+
// Family is $200, 4 selected = $160, diff = $40. Style s5 costs $40.
|
|
1134
|
+
// $40 <= $40 → family is a better deal
|
|
1135
|
+
const newState = (0, _reducer.default)(state, {
|
|
1136
|
+
type: 'SELECT_SKU_ID',
|
|
1137
|
+
skuId: 's5',
|
|
1138
|
+
selected: true
|
|
1139
|
+
});
|
|
1140
|
+
(0, _vitest.expect)(newState.selectedSkuIds['family-sku']).toBe(true);
|
|
1141
|
+
// Individual styles should be deselected (included via family)
|
|
1142
|
+
(0, _vitest.expect)(newState.selectedSkuIds['s1']).toBe(false);
|
|
1143
|
+
(0, _vitest.expect)(newState.selectedSkuIds['s5']).toBe(false);
|
|
1144
|
+
});
|
|
1145
|
+
(0, _vitest.it)('does not upgrade to family when not yet cost-effective', () => {
|
|
1146
|
+
const state = makeState({
|
|
1147
|
+
selectedSkuIds: {
|
|
1148
|
+
s1: true
|
|
1149
|
+
},
|
|
1150
|
+
collectionStyleSkus: {
|
|
1151
|
+
'family-sku': {
|
|
1152
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5'],
|
|
1153
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5'],
|
|
1154
|
+
childrenSkuIds: [],
|
|
1155
|
+
name: 'Family'
|
|
1156
|
+
}
|
|
1157
|
+
},
|
|
1158
|
+
skuPrices: {
|
|
1159
|
+
'family-sku': 200,
|
|
1160
|
+
s1: 40,
|
|
1161
|
+
s2: 40,
|
|
1162
|
+
s3: 40,
|
|
1163
|
+
s4: 40,
|
|
1164
|
+
s5: 40
|
|
1165
|
+
}
|
|
1166
|
+
});
|
|
1167
|
+
|
|
1168
|
+
// Family is $200, 1 selected = $40, diff = $160. Style s2 costs $40.
|
|
1169
|
+
// $160 > $40 → not worth upgrading
|
|
1170
|
+
const newState = (0, _reducer.default)(state, {
|
|
1171
|
+
type: 'SELECT_SKU_ID',
|
|
1172
|
+
skuId: 's2',
|
|
1173
|
+
selected: true
|
|
1174
|
+
});
|
|
1175
|
+
(0, _vitest.expect)(newState.selectedSkuIds['family-sku']).toBeUndefined();
|
|
1176
|
+
(0, _vitest.expect)(newState.selectedSkuIds['s1']).toBe(true);
|
|
1177
|
+
(0, _vitest.expect)(newState.selectedSkuIds['s2']).toBe(true);
|
|
1178
|
+
});
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
// =========================================================================
|
|
1182
|
+
// reducer SELECT_SKU_ID — edge cases with missing data
|
|
1183
|
+
// =========================================================================
|
|
1184
|
+
(0, _vitest.describe)('reducer edge cases', () => {
|
|
1185
|
+
(0, _vitest.it)('handles selection when collectionStyleSkus is empty', () => {
|
|
1186
|
+
const state = makeState({
|
|
1187
|
+
skuPrices: {
|
|
1188
|
+
'some-sku': 100
|
|
1189
|
+
}
|
|
1190
|
+
});
|
|
1191
|
+
const newState = (0, _reducer.default)(state, {
|
|
1192
|
+
type: 'SELECT_SKU_ID',
|
|
1193
|
+
skuId: 'some-sku',
|
|
1194
|
+
selected: true
|
|
1195
|
+
});
|
|
1196
|
+
(0, _vitest.expect)(newState.selectedSkuIds['some-sku']).toBe(true);
|
|
1197
|
+
});
|
|
1198
|
+
(0, _vitest.it)('handles selection when skuPrices is empty', () => {
|
|
1199
|
+
const state = makeState({
|
|
1200
|
+
collectionStyleSkus: {
|
|
1201
|
+
'family-sku': {
|
|
1202
|
+
fontStyleSkuIds: ['s1'],
|
|
1203
|
+
fontStyleIds: ['f1'],
|
|
1204
|
+
childrenSkuIds: [],
|
|
1205
|
+
name: 'Family'
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
// No prices → no collection upgrade possible, just select directly
|
|
1211
|
+
const newState = (0, _reducer.default)(state, {
|
|
1212
|
+
type: 'SELECT_SKU_ID',
|
|
1213
|
+
skuId: 's1',
|
|
1214
|
+
selected: true
|
|
1215
|
+
});
|
|
1216
|
+
(0, _vitest.expect)(newState.selectedSkuIds['s1']).toBe(true);
|
|
1217
|
+
(0, _vitest.expect)(newState.selectedSkuIds['family-sku']).toBeUndefined();
|
|
1218
|
+
});
|
|
1219
|
+
(0, _vitest.it)('re-selecting a bundle member may upgrade to superfamily', () => {
|
|
1220
|
+
// When the text bundle is selected and the user clicks text-sku (a member),
|
|
1221
|
+
// the system re-evaluates: the superfamily sees the text bundle ($500) as
|
|
1222
|
+
// already selected, making its difference only $250 — cheaper than text-sku
|
|
1223
|
+
// at $500. So it upgrades to the superfamily.
|
|
1224
|
+
//
|
|
1225
|
+
// This is a documented consequence of the greedy price-optimization: every
|
|
1226
|
+
// SELECT_SKU_ID re-evaluates the cheapest collection, even for members
|
|
1227
|
+
// that are already effectively selected.
|
|
1228
|
+
const state = makeThunderState({
|
|
1229
|
+
selectedSkuIds: {
|
|
1230
|
+
'text-bundle-sku': true,
|
|
1231
|
+
'text-sku': false,
|
|
1232
|
+
'text-var-sku': false,
|
|
1233
|
+
'text-var-italic-sku': false,
|
|
1234
|
+
s1: false,
|
|
1235
|
+
s2: false,
|
|
1236
|
+
vs1: false,
|
|
1237
|
+
vis1: false
|
|
1238
|
+
}
|
|
1239
|
+
});
|
|
1240
|
+
const newState = (0, _reducer.default)(state, {
|
|
1241
|
+
type: 'SELECT_SKU_ID',
|
|
1242
|
+
skuId: 'text-sku',
|
|
1243
|
+
selected: true
|
|
1244
|
+
});
|
|
1245
|
+
(0, _vitest.expect)(newState.selectedSkuIds['super-sku']).toBe(true);
|
|
1246
|
+
(0, _vitest.expect)(newState.selectedSkuIds['text-bundle-sku']).toBe(false);
|
|
1247
|
+
});
|
|
1248
|
+
(0, _vitest.it)('UNSELECT_SKUS clears all selections cleanly', () => {
|
|
1249
|
+
const state = makeThunderState({
|
|
1250
|
+
selectedSkuIds: {
|
|
1251
|
+
'text-bundle-sku': true,
|
|
1252
|
+
'text-sku': false,
|
|
1253
|
+
'text-var-sku': false
|
|
1254
|
+
}
|
|
1255
|
+
});
|
|
1256
|
+
const newState = (0, _reducer.default)(state, {
|
|
1257
|
+
type: 'UNSELECT_SKUS'
|
|
1258
|
+
});
|
|
1259
|
+
(0, _vitest.expect)(Object.keys(newState.selectedSkuIds)).toHaveLength(0);
|
|
1260
|
+
});
|
|
1261
|
+
});
|
|
1262
|
+
|
|
1263
|
+
// =========================================================================
|
|
1264
|
+
// isSelected — family bundles (style packages within a family)
|
|
1265
|
+
// =========================================================================
|
|
1266
|
+
(0, _vitest.describe)('isSelected with family bundles (style packages)', () => {
|
|
1267
|
+
// Helper: a family with two style bundles (upright + italic packages)
|
|
1268
|
+
function makeFamilyWithBundlesState() {
|
|
1269
|
+
let overrides = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
1270
|
+
return makeState({
|
|
1271
|
+
collectionStyleSkus: {
|
|
1272
|
+
'family-sku': {
|
|
1273
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5', 'si1', 'si2', 'si3', 'si4', 'si5'],
|
|
1274
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5', 'fi1', 'fi2', 'fi3', 'fi4', 'fi5'],
|
|
1275
|
+
childrenSkuIds: ['pkg-upright-sku', 'pkg-italic-sku'],
|
|
1276
|
+
name: 'Thunder Text Family'
|
|
1277
|
+
},
|
|
1278
|
+
'pkg-upright-sku': {
|
|
1279
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5'],
|
|
1280
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5'],
|
|
1281
|
+
childrenSkuIds: [],
|
|
1282
|
+
name: 'Package Text'
|
|
1283
|
+
},
|
|
1284
|
+
'pkg-italic-sku': {
|
|
1285
|
+
fontStyleSkuIds: ['si1', 'si2', 'si3', 'si4', 'si5'],
|
|
1286
|
+
fontStyleIds: ['fi1', 'fi2', 'fi3', 'fi4', 'fi5'],
|
|
1287
|
+
childrenSkuIds: [],
|
|
1288
|
+
name: 'Package Text Italic'
|
|
1289
|
+
}
|
|
1290
|
+
},
|
|
1291
|
+
skuPrices: {
|
|
1292
|
+
'family-sku': 500,
|
|
1293
|
+
'pkg-upright-sku': 250,
|
|
1294
|
+
'pkg-italic-sku': 250,
|
|
1295
|
+
s1: 100,
|
|
1296
|
+
s2: 100,
|
|
1297
|
+
s3: 100,
|
|
1298
|
+
s4: 100,
|
|
1299
|
+
s5: 100,
|
|
1300
|
+
si1: 100,
|
|
1301
|
+
si2: 100,
|
|
1302
|
+
si3: 100,
|
|
1303
|
+
si4: 100,
|
|
1304
|
+
si5: 100
|
|
1305
|
+
},
|
|
1306
|
+
...overrides
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
(0, _vitest.it)('bundles show selected when family is directly selected', () => {
|
|
1310
|
+
const state = makeFamilyWithBundlesState({
|
|
1311
|
+
selectedSkuIds: {
|
|
1312
|
+
'family-sku': true,
|
|
1313
|
+
'pkg-upright-sku': false,
|
|
1314
|
+
'pkg-italic-sku': false,
|
|
1315
|
+
s1: false,
|
|
1316
|
+
s2: false,
|
|
1317
|
+
s3: false,
|
|
1318
|
+
s4: false,
|
|
1319
|
+
s5: false,
|
|
1320
|
+
si1: false,
|
|
1321
|
+
si2: false,
|
|
1322
|
+
si3: false,
|
|
1323
|
+
si4: false,
|
|
1324
|
+
si5: false
|
|
1325
|
+
}
|
|
1326
|
+
});
|
|
1327
|
+
(0, _vitest.expect)((0, _utils.isSelected)('pkg-upright-sku')(state)).toBe(true);
|
|
1328
|
+
(0, _vitest.expect)((0, _utils.isSelected)('pkg-italic-sku')(state)).toBe(true);
|
|
1329
|
+
(0, _vitest.expect)((0, _utils.isSelected)('s1')(state)).toBe(true);
|
|
1330
|
+
(0, _vitest.expect)((0, _utils.isSelected)('si1')(state)).toBe(true);
|
|
1331
|
+
});
|
|
1332
|
+
(0, _vitest.it)('bundles show selected when collection bundle is selected (transitive)', () => {
|
|
1333
|
+
// Collection bundle is selected → family is false → family bundles should
|
|
1334
|
+
// still show selected because the collection bundle covers all styles
|
|
1335
|
+
const state = makeFamilyWithBundlesState({
|
|
1336
|
+
collectionStyleSkus: {
|
|
1337
|
+
// Collection bundle: groups family + variable family
|
|
1338
|
+
'coll-bundle-sku': {
|
|
1339
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5', 'si1', 'si2', 'si3', 'si4', 'si5', 'vs1'],
|
|
1340
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5', 'fi1', 'fi2', 'fi3', 'fi4', 'fi5', 'vf1'],
|
|
1341
|
+
childrenSkuIds: ['family-sku', 'var-sku'],
|
|
1342
|
+
name: 'Thunder Text + Variable'
|
|
1343
|
+
},
|
|
1344
|
+
'family-sku': {
|
|
1345
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5', 'si1', 'si2', 'si3', 'si4', 'si5'],
|
|
1346
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5', 'fi1', 'fi2', 'fi3', 'fi4', 'fi5'],
|
|
1347
|
+
childrenSkuIds: ['pkg-upright-sku', 'pkg-italic-sku'],
|
|
1348
|
+
name: 'Thunder Text Family'
|
|
1349
|
+
},
|
|
1350
|
+
'var-sku': {
|
|
1351
|
+
fontStyleSkuIds: ['vs1'],
|
|
1352
|
+
fontStyleIds: ['vf1'],
|
|
1353
|
+
childrenSkuIds: [],
|
|
1354
|
+
name: 'Thunder Text Variable'
|
|
1355
|
+
},
|
|
1356
|
+
'pkg-upright-sku': {
|
|
1357
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5'],
|
|
1358
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5'],
|
|
1359
|
+
childrenSkuIds: [],
|
|
1360
|
+
name: 'Package Text'
|
|
1361
|
+
},
|
|
1362
|
+
'pkg-italic-sku': {
|
|
1363
|
+
fontStyleSkuIds: ['si1', 'si2', 'si3', 'si4', 'si5'],
|
|
1364
|
+
fontStyleIds: ['fi1', 'fi2', 'fi3', 'fi4', 'fi5'],
|
|
1365
|
+
childrenSkuIds: [],
|
|
1366
|
+
name: 'Package Text Italic'
|
|
1367
|
+
}
|
|
1368
|
+
},
|
|
1369
|
+
selectedSkuIds: {
|
|
1370
|
+
'coll-bundle-sku': true,
|
|
1371
|
+
'family-sku': false,
|
|
1372
|
+
'var-sku': false,
|
|
1373
|
+
'pkg-upright-sku': false,
|
|
1374
|
+
'pkg-italic-sku': false,
|
|
1375
|
+
s1: false,
|
|
1376
|
+
s2: false,
|
|
1377
|
+
s3: false,
|
|
1378
|
+
s4: false,
|
|
1379
|
+
s5: false,
|
|
1380
|
+
si1: false,
|
|
1381
|
+
si2: false,
|
|
1382
|
+
si3: false,
|
|
1383
|
+
si4: false,
|
|
1384
|
+
si5: false,
|
|
1385
|
+
vs1: false
|
|
1386
|
+
}
|
|
1387
|
+
});
|
|
1388
|
+
|
|
1389
|
+
// Family shows selected (direct child of collection bundle)
|
|
1390
|
+
(0, _vitest.expect)((0, _utils.isSelected)('family-sku')(state)).toBe(true);
|
|
1391
|
+
// Bundles should show selected (grandchildren — family is child of
|
|
1392
|
+
// collection bundle, bundles are children of family)
|
|
1393
|
+
(0, _vitest.expect)((0, _utils.isSelected)('pkg-upright-sku')(state)).toBe(true);
|
|
1394
|
+
(0, _vitest.expect)((0, _utils.isSelected)('pkg-italic-sku')(state)).toBe(true);
|
|
1395
|
+
// Individual styles should also show selected
|
|
1396
|
+
(0, _vitest.expect)((0, _utils.isSelected)('s1')(state)).toBe(true);
|
|
1397
|
+
});
|
|
1398
|
+
(0, _vitest.it)('selecting one bundle then another upgrades to family via reducer', () => {
|
|
1399
|
+
const state = makeFamilyWithBundlesState();
|
|
1400
|
+
|
|
1401
|
+
// Select first bundle
|
|
1402
|
+
let newState = (0, _reducer.default)(state, {
|
|
1403
|
+
type: 'SELECT_SKU_ID',
|
|
1404
|
+
skuId: 'pkg-upright-sku',
|
|
1405
|
+
selected: true
|
|
1406
|
+
});
|
|
1407
|
+
(0, _vitest.expect)(newState.selectedSkuIds['pkg-upright-sku']).toBe(true);
|
|
1408
|
+
|
|
1409
|
+
// Select second bundle — should upgrade to family
|
|
1410
|
+
newState = (0, _reducer.default)(newState, {
|
|
1411
|
+
type: 'SELECT_SKU_ID',
|
|
1412
|
+
skuId: 'pkg-italic-sku',
|
|
1413
|
+
selected: true
|
|
1414
|
+
});
|
|
1415
|
+
|
|
1416
|
+
// Family should be selected since family ($500) = two bundles ($250+$250)
|
|
1417
|
+
(0, _vitest.expect)(newState.selectedSkuIds['family-sku']).toBe(true);
|
|
1418
|
+
// Both bundles should report as selected
|
|
1419
|
+
(0, _vitest.expect)((0, _utils.isSelected)('pkg-upright-sku')(newState)).toBe(true);
|
|
1420
|
+
(0, _vitest.expect)((0, _utils.isSelected)('pkg-italic-sku')(newState)).toBe(true);
|
|
1421
|
+
});
|
|
1422
|
+
(0, _vitest.it)('auto-upgrades transitively: bundle → family → collection bundle', () => {
|
|
1423
|
+
// When a collection bundle exists at the same price as the family,
|
|
1424
|
+
// selecting both bundles should upgrade all the way to the collection
|
|
1425
|
+
// bundle (not stop at the family)
|
|
1426
|
+
const state = makeFamilyWithBundlesState({
|
|
1427
|
+
collectionStyleSkus: {
|
|
1428
|
+
'coll-bundle-sku': {
|
|
1429
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5', 'si1', 'si2', 'si3', 'si4', 'si5', 'vs1'],
|
|
1430
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5', 'fi1', 'fi2', 'fi3', 'fi4', 'fi5', 'vf1'],
|
|
1431
|
+
childrenSkuIds: ['family-sku', 'var-sku'],
|
|
1432
|
+
name: 'Thunder Text + Variable'
|
|
1433
|
+
},
|
|
1434
|
+
'family-sku': {
|
|
1435
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5', 'si1', 'si2', 'si3', 'si4', 'si5'],
|
|
1436
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5', 'fi1', 'fi2', 'fi3', 'fi4', 'fi5'],
|
|
1437
|
+
childrenSkuIds: ['pkg-upright-sku', 'pkg-italic-sku'],
|
|
1438
|
+
name: 'Thunder Text Family'
|
|
1439
|
+
},
|
|
1440
|
+
'var-sku': {
|
|
1441
|
+
fontStyleSkuIds: ['vs1'],
|
|
1442
|
+
fontStyleIds: ['vf1'],
|
|
1443
|
+
childrenSkuIds: [],
|
|
1444
|
+
name: 'Thunder Text Variable'
|
|
1445
|
+
},
|
|
1446
|
+
'pkg-upright-sku': {
|
|
1447
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5'],
|
|
1448
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5'],
|
|
1449
|
+
childrenSkuIds: [],
|
|
1450
|
+
name: 'Package Text'
|
|
1451
|
+
},
|
|
1452
|
+
'pkg-italic-sku': {
|
|
1453
|
+
fontStyleSkuIds: ['si1', 'si2', 'si3', 'si4', 'si5'],
|
|
1454
|
+
fontStyleIds: ['fi1', 'fi2', 'fi3', 'fi4', 'fi5'],
|
|
1455
|
+
childrenSkuIds: [],
|
|
1456
|
+
name: 'Package Text Italic'
|
|
1457
|
+
}
|
|
1458
|
+
},
|
|
1459
|
+
skuPrices: {
|
|
1460
|
+
'coll-bundle-sku': 500,
|
|
1461
|
+
'family-sku': 500,
|
|
1462
|
+
'var-sku': 300,
|
|
1463
|
+
'pkg-upright-sku': 250,
|
|
1464
|
+
'pkg-italic-sku': 250,
|
|
1465
|
+
s1: 100,
|
|
1466
|
+
s2: 100,
|
|
1467
|
+
s3: 100,
|
|
1468
|
+
s4: 100,
|
|
1469
|
+
s5: 100,
|
|
1470
|
+
si1: 100,
|
|
1471
|
+
si2: 100,
|
|
1472
|
+
si3: 100,
|
|
1473
|
+
si4: 100,
|
|
1474
|
+
si5: 100,
|
|
1475
|
+
vs1: 100
|
|
1476
|
+
},
|
|
1477
|
+
selectedSkuIds: {
|
|
1478
|
+
'pkg-upright-sku': true
|
|
1479
|
+
}
|
|
1480
|
+
});
|
|
1481
|
+
const newState = (0, _reducer.default)(state, {
|
|
1482
|
+
type: 'SELECT_SKU_ID',
|
|
1483
|
+
skuId: 'pkg-italic-sku',
|
|
1484
|
+
selected: true
|
|
1485
|
+
});
|
|
1486
|
+
|
|
1487
|
+
// Should transitively upgrade: bundle → family → collection bundle
|
|
1488
|
+
(0, _vitest.expect)(newState.selectedSkuIds['coll-bundle-sku']).toBe(true);
|
|
1489
|
+
(0, _vitest.expect)(newState.selectedSkuIds['family-sku']).toBe(false);
|
|
1490
|
+
(0, _vitest.expect)(newState.selectedSkuIds['pkg-upright-sku']).toBe(false);
|
|
1491
|
+
(0, _vitest.expect)(newState.selectedSkuIds['pkg-italic-sku']).toBe(false);
|
|
1492
|
+
|
|
1493
|
+
// All items should report as selected via isSelected
|
|
1494
|
+
(0, _vitest.expect)((0, _utils.isSelected)('family-sku')(newState)).toBe(true);
|
|
1495
|
+
(0, _vitest.expect)((0, _utils.isSelected)('pkg-upright-sku')(newState)).toBe(true);
|
|
1496
|
+
(0, _vitest.expect)((0, _utils.isSelected)('pkg-italic-sku')(newState)).toBe(true);
|
|
1497
|
+
(0, _vitest.expect)((0, _utils.isSelected)('s1')(newState)).toBe(true);
|
|
1498
|
+
(0, _vitest.expect)((0, _utils.isSelected)('vs1')(newState)).toBe(true);
|
|
1499
|
+
});
|
|
1500
|
+
(0, _vitest.it)('prefers collection bundle over family when family has duplicate fontStyleSkuIds from bundles', () => {
|
|
1501
|
+
// In production data, flattenSkuData produces a family entry whose
|
|
1502
|
+
// fontStyleSkuIds includes BOTH the family's own styles AND the
|
|
1503
|
+
// overlapping styles from its child bundles, creating duplicates.
|
|
1504
|
+
// The tie-breaker must use non-self preference (not raw array length)
|
|
1505
|
+
// to avoid the family's inflated count beating the collection bundle.
|
|
1506
|
+
const state = makeState({
|
|
1507
|
+
collectionStyleSkus: {
|
|
1508
|
+
'family-sku': {
|
|
1509
|
+
// 10 unique styles, but listed 20 times (family's own + bundle overlap)
|
|
1510
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5', 'si1', 'si2', 'si3', 'si4', 'si5', 's1', 's2', 's3', 's4', 's5', 'si1', 'si2', 'si3', 'si4', 'si5'],
|
|
1511
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5', 'fi1', 'fi2', 'fi3', 'fi4', 'fi5'],
|
|
1512
|
+
childrenSkuIds: ['pkg-upright-sku', 'pkg-italic-sku'],
|
|
1513
|
+
name: 'Thunder Caption Family'
|
|
1514
|
+
},
|
|
1515
|
+
'coll-bundle-sku': {
|
|
1516
|
+
// 12 unique styles (family 10 + variable 2)
|
|
1517
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5', 'si1', 'si2', 'si3', 'si4', 'si5', 'vs1', 'vs2'],
|
|
1518
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5', 'fi1', 'fi2', 'fi3', 'fi4', 'fi5', 'vf1', 'vf2'],
|
|
1519
|
+
childrenSkuIds: ['family-sku', 'var-sku'],
|
|
1520
|
+
name: 'Thunder Caption Family + Variable'
|
|
1521
|
+
},
|
|
1522
|
+
'var-sku': {
|
|
1523
|
+
fontStyleSkuIds: ['vs1', 'vs2'],
|
|
1524
|
+
fontStyleIds: ['vf1', 'vf2'],
|
|
1525
|
+
childrenSkuIds: [],
|
|
1526
|
+
name: 'Thunder Caption Variable'
|
|
1527
|
+
},
|
|
1528
|
+
'pkg-upright-sku': {
|
|
1529
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5'],
|
|
1530
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5'],
|
|
1531
|
+
childrenSkuIds: [],
|
|
1532
|
+
name: 'Package Caption'
|
|
1533
|
+
},
|
|
1534
|
+
'pkg-italic-sku': {
|
|
1535
|
+
fontStyleSkuIds: ['si1', 'si2', 'si3', 'si4', 'si5'],
|
|
1536
|
+
fontStyleIds: ['fi1', 'fi2', 'fi3', 'fi4', 'fi5'],
|
|
1537
|
+
childrenSkuIds: [],
|
|
1538
|
+
name: 'Package Caption Italic'
|
|
1539
|
+
}
|
|
1540
|
+
},
|
|
1541
|
+
skuPrices: {
|
|
1542
|
+
'family-sku': 500,
|
|
1543
|
+
'coll-bundle-sku': 500,
|
|
1544
|
+
'var-sku': 500,
|
|
1545
|
+
'pkg-upright-sku': 300,
|
|
1546
|
+
'pkg-italic-sku': 300,
|
|
1547
|
+
s1: 100,
|
|
1548
|
+
s2: 100,
|
|
1549
|
+
s3: 100,
|
|
1550
|
+
s4: 100,
|
|
1551
|
+
s5: 100,
|
|
1552
|
+
si1: 100,
|
|
1553
|
+
si2: 100,
|
|
1554
|
+
si3: 100,
|
|
1555
|
+
si4: 100,
|
|
1556
|
+
si5: 100,
|
|
1557
|
+
vs1: 100,
|
|
1558
|
+
vs2: 100
|
|
1559
|
+
}
|
|
1560
|
+
});
|
|
1561
|
+
|
|
1562
|
+
// family-sku fontStyleSkuIds.length is 20 (duplicates), coll-bundle is 12.
|
|
1563
|
+
// A naive .length comparison would pick the family. The non-self
|
|
1564
|
+
// preference must win so the collection bundle is auto-selected.
|
|
1565
|
+
const [collId] = (0, _reducer.collectionSkuIdWithDiscount)(state, 'family-sku');
|
|
1566
|
+
(0, _vitest.expect)(collId).toBe('coll-bundle-sku');
|
|
1567
|
+
});
|
|
1568
|
+
(0, _vitest.it)('transitive upgrade prefers the collection with the most font styles', () => {
|
|
1569
|
+
// When two collections have the same price difference, prefer the one
|
|
1570
|
+
// covering more font styles
|
|
1571
|
+
const state = makeFamilyWithBundlesState({
|
|
1572
|
+
collectionStyleSkus: {
|
|
1573
|
+
'small-bundle-sku': {
|
|
1574
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5', 'si1', 'si2', 'si3', 'si4', 'si5'],
|
|
1575
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5', 'fi1', 'fi2', 'fi3', 'fi4', 'fi5'],
|
|
1576
|
+
childrenSkuIds: ['family-sku'],
|
|
1577
|
+
name: 'Small Bundle'
|
|
1578
|
+
},
|
|
1579
|
+
'big-bundle-sku': {
|
|
1580
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5', 'si1', 'si2', 'si3', 'si4', 'si5', 'vs1'],
|
|
1581
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5', 'fi1', 'fi2', 'fi3', 'fi4', 'fi5', 'vf1'],
|
|
1582
|
+
childrenSkuIds: ['family-sku', 'var-sku'],
|
|
1583
|
+
name: 'Big Bundle'
|
|
1584
|
+
},
|
|
1585
|
+
'family-sku': {
|
|
1586
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5', 'si1', 'si2', 'si3', 'si4', 'si5'],
|
|
1587
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5', 'fi1', 'fi2', 'fi3', 'fi4', 'fi5'],
|
|
1588
|
+
childrenSkuIds: ['pkg-upright-sku', 'pkg-italic-sku'],
|
|
1589
|
+
name: 'Thunder Text Family'
|
|
1590
|
+
},
|
|
1591
|
+
'var-sku': {
|
|
1592
|
+
fontStyleSkuIds: ['vs1'],
|
|
1593
|
+
fontStyleIds: ['vf1'],
|
|
1594
|
+
childrenSkuIds: [],
|
|
1595
|
+
name: 'Variable'
|
|
1596
|
+
},
|
|
1597
|
+
'pkg-upright-sku': {
|
|
1598
|
+
fontStyleSkuIds: ['s1', 's2', 's3', 's4', 's5'],
|
|
1599
|
+
fontStyleIds: ['f1', 'f2', 'f3', 'f4', 'f5'],
|
|
1600
|
+
childrenSkuIds: [],
|
|
1601
|
+
name: 'Package Text'
|
|
1602
|
+
},
|
|
1603
|
+
'pkg-italic-sku': {
|
|
1604
|
+
fontStyleSkuIds: ['si1', 'si2', 'si3', 'si4', 'si5'],
|
|
1605
|
+
fontStyleIds: ['fi1', 'fi2', 'fi3', 'fi4', 'fi5'],
|
|
1606
|
+
childrenSkuIds: [],
|
|
1607
|
+
name: 'Package Text Italic'
|
|
1608
|
+
}
|
|
1609
|
+
},
|
|
1610
|
+
skuPrices: {
|
|
1611
|
+
'small-bundle-sku': 500,
|
|
1612
|
+
'big-bundle-sku': 500,
|
|
1613
|
+
'family-sku': 500,
|
|
1614
|
+
'var-sku': 300,
|
|
1615
|
+
'pkg-upright-sku': 250,
|
|
1616
|
+
'pkg-italic-sku': 250,
|
|
1617
|
+
s1: 100,
|
|
1618
|
+
s2: 100,
|
|
1619
|
+
s3: 100,
|
|
1620
|
+
s4: 100,
|
|
1621
|
+
s5: 100,
|
|
1622
|
+
si1: 100,
|
|
1623
|
+
si2: 100,
|
|
1624
|
+
si3: 100,
|
|
1625
|
+
si4: 100,
|
|
1626
|
+
si5: 100,
|
|
1627
|
+
vs1: 100
|
|
1628
|
+
}
|
|
1629
|
+
});
|
|
1630
|
+
const [collId] = (0, _reducer.collectionSkuIdWithDiscount)(state, 'family-sku');
|
|
1631
|
+
// Both bundles have the same price, but big-bundle has more styles
|
|
1632
|
+
(0, _vitest.expect)(collId).toBe('big-bundle-sku');
|
|
1633
|
+
});
|
|
1634
|
+
});
|