@salesforce/retail-react-app 7.1.0-preview.1 → 8.0.0-dev

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/CHANGELOG.md +8 -2
  2. package/app/components/_app/index.jsx +9 -7
  3. package/app/components/_app/index.test.js +2 -2
  4. package/app/components/_app-config/index.jsx +9 -3
  5. package/app/components/drawer-menu/drawer-menu.jsx +3 -1
  6. package/app/components/footer/index.jsx +3 -1
  7. package/app/components/header/index.jsx +3 -1
  8. package/app/components/header/index.test.js +2 -2
  9. package/app/components/island/README.md +1 -1
  10. package/app/components/island/index.jsx +3 -1
  11. package/app/components/island/index.test.js +94 -5
  12. package/app/components/item-variant/item-attributes.jsx +12 -3
  13. package/app/components/multiship/multiship-order-summary.jsx +137 -0
  14. package/app/components/multiship/multiship-order-summary.test.js +121 -0
  15. package/app/components/order-summary/index.jsx +2 -4
  16. package/app/components/pickup-or-delivery/index.jsx +80 -0
  17. package/app/components/pickup-or-delivery/index.test.jsx +182 -0
  18. package/app/components/product-item/index.jsx +26 -16
  19. package/app/components/product-item/index.test.js +29 -2
  20. package/app/components/product-item-list/index.jsx +10 -0
  21. package/app/components/product-item-list/index.test.jsx +14 -0
  22. package/app/components/product-view/index.jsx +9 -6
  23. package/app/components/product-view/index.test.js +25 -21
  24. package/app/components/quantity-picker/index.test.jsx +12 -12
  25. package/app/components/reset-password/index.test.js +1 -1
  26. package/app/components/shared/ui/AlertDescription/index.jsx +8 -0
  27. package/app/components/shared/ui/index.jsx +1 -0
  28. package/app/components/store-display/index.jsx +28 -4
  29. package/app/components/store-display/index.test.js +71 -0
  30. package/app/components/store-locator/form.test.jsx +16 -4
  31. package/app/components/store-locator/list.jsx +9 -4
  32. package/app/components/toggle-card/index.jsx +14 -0
  33. package/app/components/unavailable-product-confirmation-modal/index.jsx +19 -5
  34. package/app/components/unavailable-product-confirmation-modal/index.test.js +122 -1
  35. package/app/constants.js +20 -6
  36. package/app/contexts/store-locator-provider.jsx +7 -1
  37. package/app/contexts/store-locator-provider.test.jsx +36 -1
  38. package/app/hooks/use-address-form.js +155 -0
  39. package/app/hooks/use-address-form.test.js +501 -0
  40. package/app/hooks/use-auth-modal.js +2 -6
  41. package/app/hooks/use-current-basket.js +71 -2
  42. package/app/hooks/use-current-basket.test.js +37 -1
  43. package/app/hooks/use-dnt-notification.js +4 -4
  44. package/app/hooks/use-dnt-notification.test.js +5 -5
  45. package/app/hooks/use-item-shipment-management.js +233 -0
  46. package/app/hooks/use-item-shipment-management.test.js +696 -0
  47. package/app/hooks/use-multiship.js +589 -0
  48. package/app/hooks/use-multiship.test.js +776 -0
  49. package/app/hooks/use-pickup-shipment.js +70 -106
  50. package/app/hooks/use-pickup-shipment.test.js +345 -209
  51. package/app/hooks/use-product-address-assignment.js +280 -0
  52. package/app/hooks/use-product-address-assignment.test.js +414 -0
  53. package/app/hooks/use-product-inventory.js +100 -0
  54. package/app/hooks/use-product-inventory.test.js +254 -0
  55. package/app/hooks/use-shipment-operations.js +168 -0
  56. package/app/hooks/use-shipment-operations.test.js +385 -0
  57. package/app/hooks/use-store-locator.js +24 -2
  58. package/app/hooks/use-store-locator.test.jsx +109 -1
  59. package/app/pages/account/index.test.js +1 -1
  60. package/app/pages/account/profile.test.js +0 -2
  61. package/app/pages/cart/index.jsx +397 -157
  62. package/app/pages/cart/index.test.js +353 -2
  63. package/app/pages/cart/partials/bonus-products-title.jsx +10 -8
  64. package/app/pages/cart/partials/cart-secondary-button-group.test.js +1 -1
  65. package/app/pages/cart/partials/order-type-display.jsx +68 -0
  66. package/app/pages/cart/partials/order-type-display.test.js +241 -0
  67. package/app/pages/checkout/confirmation.jsx +79 -158
  68. package/app/pages/checkout/index.jsx +34 -9
  69. package/app/pages/checkout/index.test.js +245 -118
  70. package/app/pages/checkout/partials/contact-info.jsx +2 -6
  71. package/app/pages/checkout/partials/contact-info.test.js +93 -7
  72. package/app/pages/checkout/partials/payment.jsx +19 -5
  73. package/app/pages/checkout/partials/pickup-address.jsx +340 -70
  74. package/app/pages/checkout/partials/pickup-address.test.js +1075 -82
  75. package/app/pages/checkout/partials/product-shipping-address-card.jsx +382 -0
  76. package/app/pages/checkout/partials/shipment-details.jsx +209 -0
  77. package/app/pages/checkout/partials/shipment-details.test.js +246 -0
  78. package/app/pages/checkout/partials/shipping-address.jsx +156 -68
  79. package/app/pages/checkout/partials/shipping-address.test.js +673 -0
  80. package/app/pages/checkout/partials/shipping-method-options.jsx +180 -0
  81. package/app/pages/checkout/partials/shipping-methods.jsx +403 -0
  82. package/app/pages/checkout/partials/shipping-methods.test.js +472 -0
  83. package/app/pages/checkout/partials/shipping-multi-address.jsx +259 -0
  84. package/app/pages/checkout/partials/shipping-multi-address.test.js +2088 -0
  85. package/app/pages/checkout/partials/shipping-product-cards.jsx +101 -0
  86. package/app/pages/checkout/util/checkout-context.js +25 -18
  87. package/app/pages/login/index.jsx +2 -6
  88. package/app/pages/product-detail/index.jsx +96 -81
  89. package/app/pages/product-detail/index.test.js +103 -19
  90. package/app/pages/product-list/index.jsx +3 -1
  91. package/app/pages/product-list/partials/inventory-filter.jsx +18 -21
  92. package/app/pages/product-list/partials/inventory-filter.test.js +15 -17
  93. package/app/pages/product-list/partials/selected-refinements.jsx +3 -1
  94. package/app/ssr.js +1 -1
  95. package/app/static/translations/compiled/en-GB.json +316 -30
  96. package/app/static/translations/compiled/en-US.json +316 -30
  97. package/app/static/translations/compiled/en-XA.json +673 -75
  98. package/app/utils/address-utils.js +112 -0
  99. package/app/utils/address-utils.test.js +484 -0
  100. package/app/utils/product-utils.js +17 -5
  101. package/app/utils/product-utils.test.js +17 -8
  102. package/app/utils/sfdc-user-agent-utils.js +32 -0
  103. package/app/utils/sfdc-user-agent-utils.test.js +82 -0
  104. package/app/utils/shipment-utils.js +196 -0
  105. package/app/utils/shipment-utils.test.js +458 -0
  106. package/app/utils/test-utils.js +4 -4
  107. package/app/utils/utils.js +6 -1
  108. package/config/default.js +4 -1
  109. package/config/mocks/default.js +3 -1
  110. package/package.json +9 -9
  111. package/translations/en-GB.json +127 -10
  112. package/translations/en-US.json +127 -10
  113. package/app/pages/checkout/partials/shipping-options.jsx +0 -269
@@ -0,0 +1,696 @@
1
+ /*
2
+ * Copyright (c) 2025, Salesforce, Inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
+ */
7
+
8
+ import {renderHook} from '@testing-library/react'
9
+ import {useShopperBasketsMutation} from '@salesforce/commerce-sdk-react'
10
+ import {useItemShipmentManagement} from '@salesforce/retail-react-app/app/hooks/use-item-shipment-management'
11
+
12
+ // Mock the commerce SDK hooks
13
+ jest.mock('@salesforce/commerce-sdk-react', () => ({
14
+ useShopperBasketsMutation: jest.fn()
15
+ }))
16
+
17
+ describe('useItemShipmentManagement', () => {
18
+ let mockUpdateItemInBasketMutation
19
+ let mockUpdateItemsInBasketMutation
20
+ let mockUseShopperBasketsMutation
21
+
22
+ beforeEach(() => {
23
+ mockUpdateItemInBasketMutation = {
24
+ mutateAsync: jest.fn()
25
+ }
26
+ mockUpdateItemsInBasketMutation = {
27
+ mutateAsync: jest.fn()
28
+ }
29
+
30
+ mockUseShopperBasketsMutation = jest.fn((mutationType) => {
31
+ switch (mutationType) {
32
+ case 'updateItemInBasket':
33
+ return mockUpdateItemInBasketMutation
34
+ case 'updateItemsInBasket':
35
+ return mockUpdateItemsInBasketMutation
36
+ default:
37
+ return {}
38
+ }
39
+ })
40
+
41
+ useShopperBasketsMutation.mockImplementation(mockUseShopperBasketsMutation)
42
+ })
43
+
44
+ afterEach(() => {
45
+ jest.clearAllMocks()
46
+ })
47
+
48
+ describe('updateItemToPickupShipment', () => {
49
+ test('should update item to pickup shipment', async () => {
50
+ const basketId = 'test-basket-id'
51
+ const productItem = {
52
+ itemId: 'item-1',
53
+ productId: 'prod-1',
54
+ quantity: 2
55
+ }
56
+ const targetShipmentId = 'pickup-shipment'
57
+ const inventoryId = 'store-inventory-1'
58
+ const mockResponse = {updated: true}
59
+
60
+ mockUpdateItemInBasketMutation.mutateAsync.mockResolvedValue(mockResponse)
61
+
62
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
63
+
64
+ const response = await result.current.updateItemToPickupShipment(
65
+ productItem,
66
+ targetShipmentId,
67
+ inventoryId
68
+ )
69
+
70
+ expect(mockUpdateItemInBasketMutation.mutateAsync).toHaveBeenCalledWith({
71
+ parameters: {
72
+ basketId,
73
+ itemId: 'item-1'
74
+ },
75
+ body: {
76
+ productId: 'prod-1',
77
+ quantity: 2,
78
+ shipmentId: 'pickup-shipment',
79
+ inventoryId: 'store-inventory-1'
80
+ }
81
+ })
82
+ expect(response).toEqual(mockResponse)
83
+ })
84
+
85
+ test('should throw error for invalid basket or product item', async () => {
86
+ const basketId = null
87
+ const productItem = null
88
+ const targetShipmentId = 'pickup-shipment'
89
+ const inventoryId = 'store-inventory-1'
90
+
91
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
92
+
93
+ await expect(
94
+ result.current.updateItemToPickupShipment(
95
+ productItem,
96
+ targetShipmentId,
97
+ inventoryId
98
+ )
99
+ ).rejects.toThrow('Invalid basket or product item')
100
+ })
101
+
102
+ test('should throw error when API call fails', async () => {
103
+ const basketId = 'test-basket-id'
104
+ const productItem = {
105
+ itemId: 'item-1',
106
+ productId: 'prod-1',
107
+ quantity: 2
108
+ }
109
+ const targetShipmentId = 'pickup-shipment'
110
+ const inventoryId = 'store-inventory-1'
111
+ const mockError = new Error('API Error')
112
+
113
+ mockUpdateItemInBasketMutation.mutateAsync.mockRejectedValue(mockError)
114
+
115
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
116
+
117
+ await expect(
118
+ result.current.updateItemToPickupShipment(
119
+ productItem,
120
+ targetShipmentId,
121
+ inventoryId
122
+ )
123
+ ).rejects.toThrow('API Error')
124
+ })
125
+ })
126
+
127
+ describe('updateItemToDeliveryShipment', () => {
128
+ test('should update item to delivery shipment', async () => {
129
+ const basketId = 'test-basket-id'
130
+ const productItem = {
131
+ itemId: 'item-1',
132
+ productId: 'prod-1',
133
+ quantity: 2,
134
+ inventoryId: 'store-inventory-1'
135
+ }
136
+ const targetShipmentId = 'delivery-shipment'
137
+ const defaultInventoryId = 'default-inventory'
138
+ const mockResponse = {updated: true}
139
+
140
+ mockUpdateItemInBasketMutation.mutateAsync.mockResolvedValue(mockResponse)
141
+
142
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
143
+
144
+ const response = await result.current.updateItemToDeliveryShipment(
145
+ productItem,
146
+ targetShipmentId,
147
+ defaultInventoryId
148
+ )
149
+
150
+ expect(mockUpdateItemInBasketMutation.mutateAsync).toHaveBeenCalledWith({
151
+ parameters: {
152
+ basketId,
153
+ itemId: 'item-1'
154
+ },
155
+ body: {
156
+ productId: 'prod-1',
157
+ quantity: 2,
158
+ shipmentId: 'delivery-shipment',
159
+ inventoryId: 'default-inventory'
160
+ }
161
+ })
162
+ expect(response).toEqual(mockResponse)
163
+ })
164
+
165
+ test('should update item to delivery shipment without inventory ID', async () => {
166
+ const basketId = 'test-basket-id'
167
+ const productItem = {
168
+ itemId: 'item-1',
169
+ productId: 'prod-1',
170
+ quantity: 2
171
+ }
172
+ const targetShipmentId = 'delivery-shipment'
173
+ const defaultInventoryId = 'default-inventory'
174
+ const mockResponse = {updated: true}
175
+
176
+ mockUpdateItemInBasketMutation.mutateAsync.mockResolvedValue(mockResponse)
177
+
178
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
179
+
180
+ const response = await result.current.updateItemToDeliveryShipment(
181
+ productItem,
182
+ targetShipmentId,
183
+ defaultInventoryId
184
+ )
185
+
186
+ expect(mockUpdateItemInBasketMutation.mutateAsync).toHaveBeenCalledWith({
187
+ parameters: {
188
+ basketId,
189
+ itemId: 'item-1'
190
+ },
191
+ body: {
192
+ productId: 'prod-1',
193
+ quantity: 2,
194
+ shipmentId: 'delivery-shipment'
195
+ }
196
+ })
197
+ expect(response).toEqual(mockResponse)
198
+ })
199
+
200
+ test('should throw error for invalid basket or product item', async () => {
201
+ const basketId = null
202
+ const productItem = null
203
+ const targetShipmentId = 'delivery-shipment'
204
+ const defaultInventoryId = 'default-inventory'
205
+
206
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
207
+
208
+ await expect(
209
+ result.current.updateItemToDeliveryShipment(
210
+ productItem,
211
+ targetShipmentId,
212
+ defaultInventoryId
213
+ )
214
+ ).rejects.toThrow('Invalid basket or product item')
215
+ })
216
+
217
+ test('should throw error when API call fails', async () => {
218
+ const basketId = 'test-basket-id'
219
+ const productItem = {
220
+ itemId: 'item-1',
221
+ productId: 'prod-1',
222
+ quantity: 2
223
+ }
224
+ const targetShipmentId = 'delivery-shipment'
225
+ const defaultInventoryId = 'default-inventory'
226
+ const mockError = new Error('API Error')
227
+
228
+ mockUpdateItemInBasketMutation.mutateAsync.mockRejectedValue(mockError)
229
+
230
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
231
+
232
+ await expect(
233
+ result.current.updateItemToDeliveryShipment(
234
+ productItem,
235
+ targetShipmentId,
236
+ defaultInventoryId
237
+ )
238
+ ).rejects.toThrow('API Error')
239
+ })
240
+ })
241
+
242
+ describe('updateItemsToDeliveryShipment', () => {
243
+ test('should update multiple items to delivery shipment', async () => {
244
+ const basketId = 'test-basket-id'
245
+ const productItems = [
246
+ {
247
+ itemId: 'item-1',
248
+ productId: 'prod-1',
249
+ quantity: 2,
250
+ inventoryId: 'store-inventory-1'
251
+ },
252
+ {
253
+ itemId: 'item-2',
254
+ productId: 'prod-2',
255
+ quantity: 1,
256
+ inventoryId: 'store-inventory-2'
257
+ }
258
+ ]
259
+ const targetShipmentId = 'delivery-shipment'
260
+ const defaultInventoryId = 'default-inventory'
261
+ const mockResponse = {updated: true}
262
+
263
+ mockUpdateItemsInBasketMutation.mutateAsync.mockResolvedValue(mockResponse)
264
+
265
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
266
+
267
+ const response = await result.current.updateItemsToDeliveryShipment(
268
+ productItems,
269
+ targetShipmentId,
270
+ defaultInventoryId
271
+ )
272
+
273
+ expect(mockUpdateItemsInBasketMutation.mutateAsync).toHaveBeenCalledWith({
274
+ parameters: {
275
+ basketId
276
+ },
277
+ body: [
278
+ {
279
+ itemId: 'item-1',
280
+ productId: 'prod-1',
281
+ quantity: 2,
282
+ shipmentId: 'delivery-shipment',
283
+ inventoryId: 'default-inventory'
284
+ },
285
+ {
286
+ itemId: 'item-2',
287
+ productId: 'prod-2',
288
+ quantity: 1,
289
+ shipmentId: 'delivery-shipment',
290
+ inventoryId: 'default-inventory'
291
+ }
292
+ ]
293
+ })
294
+ expect(response).toEqual(mockResponse)
295
+ })
296
+
297
+ test('should return early for empty product items array', async () => {
298
+ const basketId = 'test-basket-id'
299
+ const productItems = []
300
+ const targetShipmentId = 'delivery-shipment'
301
+ const defaultInventoryId = 'default-inventory'
302
+
303
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
304
+
305
+ const response = await result.current.updateItemsToDeliveryShipment(
306
+ productItems,
307
+ targetShipmentId,
308
+ defaultInventoryId
309
+ )
310
+
311
+ expect(response).toEqual({updated: true})
312
+ expect(mockUpdateItemsInBasketMutation.mutateAsync).not.toHaveBeenCalled()
313
+ })
314
+
315
+ test('should throw error for invalid basket or product items', async () => {
316
+ const basketId = null
317
+ const productItems = null
318
+ const targetShipmentId = 'delivery-shipment'
319
+ const defaultInventoryId = 'default-inventory'
320
+
321
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
322
+
323
+ await expect(
324
+ result.current.updateItemsToDeliveryShipment(
325
+ productItems,
326
+ targetShipmentId,
327
+ defaultInventoryId
328
+ )
329
+ ).rejects.toThrow('Invalid basket or product items array')
330
+ })
331
+
332
+ test('should throw error when API call fails', async () => {
333
+ const basketId = 'test-basket-id'
334
+ const productItems = [
335
+ {
336
+ itemId: 'item-1',
337
+ productId: 'prod-1',
338
+ quantity: 2
339
+ }
340
+ ]
341
+ const targetShipmentId = 'delivery-shipment'
342
+ const defaultInventoryId = 'default-inventory'
343
+ const mockError = new Error('API Error')
344
+
345
+ mockUpdateItemsInBasketMutation.mutateAsync.mockRejectedValue(mockError)
346
+
347
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
348
+
349
+ await expect(
350
+ result.current.updateItemsToDeliveryShipment(
351
+ productItems,
352
+ targetShipmentId,
353
+ defaultInventoryId
354
+ )
355
+ ).rejects.toThrow('API Error')
356
+ })
357
+ })
358
+
359
+ describe('updateItemsToPickupShipment', () => {
360
+ test('should update multiple items to pickup shipment', async () => {
361
+ const basketId = 'test-basket-id'
362
+ const productItems = [
363
+ {
364
+ itemId: 'item-1',
365
+ productId: 'prod-1',
366
+ quantity: 2
367
+ },
368
+ {
369
+ itemId: 'item-2',
370
+ productId: 'prod-2',
371
+ quantity: 1
372
+ }
373
+ ]
374
+ const targetShipmentId = 'pickup-shipment'
375
+ const inventoryId = 'store-inventory-1'
376
+ const mockResponse = {updated: true}
377
+
378
+ mockUpdateItemsInBasketMutation.mutateAsync.mockResolvedValue(mockResponse)
379
+
380
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
381
+
382
+ const response = await result.current.updateItemsToPickupShipment(
383
+ productItems,
384
+ targetShipmentId,
385
+ inventoryId
386
+ )
387
+
388
+ expect(mockUpdateItemsInBasketMutation.mutateAsync).toHaveBeenCalledWith({
389
+ parameters: {
390
+ basketId
391
+ },
392
+ body: [
393
+ {
394
+ itemId: 'item-1',
395
+ productId: 'prod-1',
396
+ quantity: 2,
397
+ shipmentId: 'pickup-shipment',
398
+ inventoryId: 'store-inventory-1'
399
+ },
400
+ {
401
+ itemId: 'item-2',
402
+ productId: 'prod-2',
403
+ quantity: 1,
404
+ shipmentId: 'pickup-shipment',
405
+ inventoryId: 'store-inventory-1'
406
+ }
407
+ ]
408
+ })
409
+ expect(response).toEqual(mockResponse)
410
+ })
411
+
412
+ test('should return early for empty product items array', async () => {
413
+ const basketId = 'test-basket-id'
414
+ const productItems = []
415
+ const targetShipmentId = 'pickup-shipment'
416
+ const inventoryId = 'store-inventory-1'
417
+
418
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
419
+
420
+ const response = await result.current.updateItemsToPickupShipment(
421
+ productItems,
422
+ targetShipmentId,
423
+ inventoryId
424
+ )
425
+
426
+ expect(response).toEqual({updated: true})
427
+ expect(mockUpdateItemsInBasketMutation.mutateAsync).not.toHaveBeenCalled()
428
+ })
429
+
430
+ test('should throw error for invalid basket or product items', async () => {
431
+ const basketId = null
432
+ const productItems = null
433
+ const targetShipmentId = 'pickup-shipment'
434
+ const inventoryId = 'store-inventory-1'
435
+
436
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
437
+
438
+ await expect(
439
+ result.current.updateItemsToPickupShipment(
440
+ productItems,
441
+ targetShipmentId,
442
+ inventoryId
443
+ )
444
+ ).rejects.toThrow('Invalid basket or product items array')
445
+ })
446
+
447
+ test('should throw error when API call fails', async () => {
448
+ const basketId = 'test-basket-id'
449
+ const productItems = [
450
+ {
451
+ itemId: 'item-1',
452
+ productId: 'prod-1',
453
+ quantity: 2
454
+ }
455
+ ]
456
+ const targetShipmentId = 'pickup-shipment'
457
+ const inventoryId = 'store-inventory-1'
458
+ const mockError = new Error('API Error')
459
+
460
+ mockUpdateItemsInBasketMutation.mutateAsync.mockRejectedValue(mockError)
461
+
462
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
463
+
464
+ await expect(
465
+ result.current.updateItemsToPickupShipment(
466
+ productItems,
467
+ targetShipmentId,
468
+ inventoryId
469
+ )
470
+ ).rejects.toThrow('API Error')
471
+ })
472
+ })
473
+
474
+ describe('updateDeliveryOption', () => {
475
+ test('should update item to pickup shipment', async () => {
476
+ const basketId = 'test-basket-id'
477
+ const productItem = {
478
+ itemId: 'item-1',
479
+ productId: 'prod-1',
480
+ quantity: 2
481
+ }
482
+ const selectedPickup = true
483
+ const storeInfo = {
484
+ id: 'store-1',
485
+ inventoryId: 'store-inventory-1'
486
+ }
487
+ const defaultInventoryId = 'default-inventory'
488
+ const findOrCreatePickupShipment = jest.fn().mockResolvedValue({
489
+ shipmentId: 'pickup-shipment'
490
+ })
491
+ const findOrCreateDeliveryShipment = jest.fn()
492
+
493
+ mockUpdateItemInBasketMutation.mutateAsync.mockResolvedValue({updated: true})
494
+
495
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
496
+
497
+ await result.current.updateDeliveryOption(
498
+ productItem,
499
+ selectedPickup,
500
+ storeInfo,
501
+ defaultInventoryId,
502
+ findOrCreatePickupShipment,
503
+ findOrCreateDeliveryShipment
504
+ )
505
+
506
+ expect(findOrCreatePickupShipment).toHaveBeenCalledWith(storeInfo)
507
+ expect(mockUpdateItemInBasketMutation.mutateAsync).toHaveBeenCalledWith({
508
+ parameters: {
509
+ basketId,
510
+ itemId: 'item-1'
511
+ },
512
+ body: {
513
+ productId: 'prod-1',
514
+ quantity: 2,
515
+ shipmentId: 'pickup-shipment',
516
+ inventoryId: 'store-inventory-1'
517
+ }
518
+ })
519
+ })
520
+
521
+ test('should update item to delivery shipment', async () => {
522
+ const basketId = 'test-basket-id'
523
+ const productItem = {
524
+ itemId: 'item-1',
525
+ productId: 'prod-1',
526
+ quantity: 2,
527
+ inventoryId: 'store-inventory-1'
528
+ }
529
+ const selectedPickup = false
530
+ const storeInfo = null
531
+ const defaultInventoryId = 'default-inventory'
532
+ const findOrCreatePickupShipment = jest.fn()
533
+ const findOrCreateDeliveryShipment = jest.fn().mockResolvedValue({
534
+ shipmentId: 'delivery-shipment'
535
+ })
536
+
537
+ mockUpdateItemInBasketMutation.mutateAsync.mockResolvedValue({updated: true})
538
+
539
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
540
+
541
+ await result.current.updateDeliveryOption(
542
+ productItem,
543
+ selectedPickup,
544
+ storeInfo,
545
+ defaultInventoryId,
546
+ findOrCreatePickupShipment,
547
+ findOrCreateDeliveryShipment
548
+ )
549
+
550
+ expect(findOrCreateDeliveryShipment).toHaveBeenCalled()
551
+ expect(mockUpdateItemInBasketMutation.mutateAsync).toHaveBeenCalledWith({
552
+ parameters: {
553
+ basketId,
554
+ itemId: 'item-1'
555
+ },
556
+ body: {
557
+ productId: 'prod-1',
558
+ quantity: 2,
559
+ shipmentId: 'delivery-shipment',
560
+ inventoryId: 'default-inventory'
561
+ }
562
+ })
563
+ })
564
+
565
+ test('should throw error for invalid basket or product item', async () => {
566
+ const basketId = null
567
+ const productItem = null
568
+ const selectedPickup = true
569
+ const storeInfo = {id: 'store-1', inventoryId: 'store-inventory-1'}
570
+ const defaultInventoryId = 'default-inventory'
571
+ const findOrCreatePickupShipment = jest.fn()
572
+ const findOrCreateDeliveryShipment = jest.fn()
573
+
574
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
575
+
576
+ await expect(
577
+ result.current.updateDeliveryOption(
578
+ productItem,
579
+ selectedPickup,
580
+ storeInfo,
581
+ defaultInventoryId,
582
+ findOrCreatePickupShipment,
583
+ findOrCreateDeliveryShipment
584
+ )
585
+ ).rejects.toThrow('Invalid basket or product item')
586
+ })
587
+
588
+ test('should throw error when no store selected for pickup', async () => {
589
+ const basketId = 'test-basket-id'
590
+ const productItem = {
591
+ itemId: 'item-1',
592
+ productId: 'prod-1',
593
+ quantity: 2
594
+ }
595
+ const selectedPickup = true
596
+ const storeInfo = null
597
+ const defaultInventoryId = 'default-inventory'
598
+ const findOrCreatePickupShipment = jest.fn()
599
+ const findOrCreateDeliveryShipment = jest.fn()
600
+
601
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
602
+
603
+ await expect(
604
+ result.current.updateDeliveryOption(
605
+ productItem,
606
+ selectedPickup,
607
+ storeInfo,
608
+ defaultInventoryId,
609
+ findOrCreatePickupShipment,
610
+ findOrCreateDeliveryShipment
611
+ )
612
+ ).rejects.toThrow('No store selected for pickup')
613
+ })
614
+
615
+ test('should throw error when store has no inventory ID', async () => {
616
+ const basketId = 'test-basket-id'
617
+ const productItem = {
618
+ itemId: 'item-1',
619
+ productId: 'prod-1',
620
+ quantity: 2
621
+ }
622
+ const selectedPickup = true
623
+ const storeInfo = {id: 'store-1'}
624
+ const defaultInventoryId = 'default-inventory'
625
+ const findOrCreatePickupShipment = jest.fn()
626
+ const findOrCreateDeliveryShipment = jest.fn()
627
+
628
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
629
+
630
+ await expect(
631
+ result.current.updateDeliveryOption(
632
+ productItem,
633
+ selectedPickup,
634
+ storeInfo,
635
+ defaultInventoryId,
636
+ findOrCreatePickupShipment,
637
+ findOrCreateDeliveryShipment
638
+ )
639
+ ).rejects.toThrow('Selected store does not have an inventory ID')
640
+ })
641
+
642
+ test('should throw error when findOrCreatePickupShipment fails', async () => {
643
+ const basketId = 'test-basket-id'
644
+ const productItem = {
645
+ itemId: 'item-1',
646
+ productId: 'prod-1',
647
+ quantity: 2
648
+ }
649
+ const selectedPickup = true
650
+ const storeInfo = {id: 'store-1', inventoryId: 'store-inventory-1'}
651
+ const defaultInventoryId = 'default-inventory'
652
+ const findOrCreatePickupShipment = jest.fn().mockResolvedValue(null)
653
+ const findOrCreateDeliveryShipment = jest.fn()
654
+
655
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
656
+
657
+ await expect(
658
+ result.current.updateDeliveryOption(
659
+ productItem,
660
+ selectedPickup,
661
+ storeInfo,
662
+ defaultInventoryId,
663
+ findOrCreatePickupShipment,
664
+ findOrCreateDeliveryShipment
665
+ )
666
+ ).rejects.toThrow('Failed to find or create shipment')
667
+ })
668
+
669
+ test('should throw error when findOrCreateDeliveryShipment fails', async () => {
670
+ const basketId = 'test-basket-id'
671
+ const productItem = {
672
+ itemId: 'item-1',
673
+ productId: 'prod-1',
674
+ quantity: 2
675
+ }
676
+ const selectedPickup = false
677
+ const storeInfo = null
678
+ const defaultInventoryId = 'default-inventory'
679
+ const findOrCreatePickupShipment = jest.fn()
680
+ const findOrCreateDeliveryShipment = jest.fn().mockResolvedValue(null)
681
+
682
+ const {result} = renderHook(() => useItemShipmentManagement(basketId))
683
+
684
+ await expect(
685
+ result.current.updateDeliveryOption(
686
+ productItem,
687
+ selectedPickup,
688
+ storeInfo,
689
+ defaultInventoryId,
690
+ findOrCreatePickupShipment,
691
+ findOrCreateDeliveryShipment
692
+ )
693
+ ).rejects.toThrow('Failed to find or create shipment')
694
+ })
695
+ })
696
+ })