@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.
- package/CHANGELOG.md +8 -2
- package/app/components/_app/index.jsx +9 -7
- package/app/components/_app/index.test.js +2 -2
- package/app/components/_app-config/index.jsx +9 -3
- package/app/components/drawer-menu/drawer-menu.jsx +3 -1
- package/app/components/footer/index.jsx +3 -1
- package/app/components/header/index.jsx +3 -1
- package/app/components/header/index.test.js +2 -2
- package/app/components/island/README.md +1 -1
- package/app/components/island/index.jsx +3 -1
- package/app/components/island/index.test.js +94 -5
- package/app/components/item-variant/item-attributes.jsx +12 -3
- package/app/components/multiship/multiship-order-summary.jsx +137 -0
- package/app/components/multiship/multiship-order-summary.test.js +121 -0
- package/app/components/order-summary/index.jsx +2 -4
- package/app/components/pickup-or-delivery/index.jsx +80 -0
- package/app/components/pickup-or-delivery/index.test.jsx +182 -0
- package/app/components/product-item/index.jsx +26 -16
- package/app/components/product-item/index.test.js +29 -2
- package/app/components/product-item-list/index.jsx +10 -0
- package/app/components/product-item-list/index.test.jsx +14 -0
- package/app/components/product-view/index.jsx +9 -6
- package/app/components/product-view/index.test.js +25 -21
- package/app/components/quantity-picker/index.test.jsx +12 -12
- package/app/components/reset-password/index.test.js +1 -1
- package/app/components/shared/ui/AlertDescription/index.jsx +8 -0
- package/app/components/shared/ui/index.jsx +1 -0
- package/app/components/store-display/index.jsx +28 -4
- package/app/components/store-display/index.test.js +71 -0
- package/app/components/store-locator/form.test.jsx +16 -4
- package/app/components/store-locator/list.jsx +9 -4
- package/app/components/toggle-card/index.jsx +14 -0
- package/app/components/unavailable-product-confirmation-modal/index.jsx +19 -5
- package/app/components/unavailable-product-confirmation-modal/index.test.js +122 -1
- package/app/constants.js +20 -6
- package/app/contexts/store-locator-provider.jsx +7 -1
- package/app/contexts/store-locator-provider.test.jsx +36 -1
- package/app/hooks/use-address-form.js +155 -0
- package/app/hooks/use-address-form.test.js +501 -0
- package/app/hooks/use-auth-modal.js +2 -6
- package/app/hooks/use-current-basket.js +71 -2
- package/app/hooks/use-current-basket.test.js +37 -1
- package/app/hooks/use-dnt-notification.js +4 -4
- package/app/hooks/use-dnt-notification.test.js +5 -5
- package/app/hooks/use-item-shipment-management.js +233 -0
- package/app/hooks/use-item-shipment-management.test.js +696 -0
- package/app/hooks/use-multiship.js +589 -0
- package/app/hooks/use-multiship.test.js +776 -0
- package/app/hooks/use-pickup-shipment.js +70 -106
- package/app/hooks/use-pickup-shipment.test.js +345 -209
- package/app/hooks/use-product-address-assignment.js +280 -0
- package/app/hooks/use-product-address-assignment.test.js +414 -0
- package/app/hooks/use-product-inventory.js +100 -0
- package/app/hooks/use-product-inventory.test.js +254 -0
- package/app/hooks/use-shipment-operations.js +168 -0
- package/app/hooks/use-shipment-operations.test.js +385 -0
- package/app/hooks/use-store-locator.js +24 -2
- package/app/hooks/use-store-locator.test.jsx +109 -1
- package/app/pages/account/index.test.js +1 -1
- package/app/pages/account/profile.test.js +0 -2
- package/app/pages/cart/index.jsx +397 -157
- package/app/pages/cart/index.test.js +353 -2
- package/app/pages/cart/partials/bonus-products-title.jsx +10 -8
- package/app/pages/cart/partials/cart-secondary-button-group.test.js +1 -1
- package/app/pages/cart/partials/order-type-display.jsx +68 -0
- package/app/pages/cart/partials/order-type-display.test.js +241 -0
- package/app/pages/checkout/confirmation.jsx +79 -158
- package/app/pages/checkout/index.jsx +34 -9
- package/app/pages/checkout/index.test.js +245 -118
- package/app/pages/checkout/partials/contact-info.jsx +2 -6
- package/app/pages/checkout/partials/contact-info.test.js +93 -7
- package/app/pages/checkout/partials/payment.jsx +19 -5
- package/app/pages/checkout/partials/pickup-address.jsx +340 -70
- package/app/pages/checkout/partials/pickup-address.test.js +1075 -82
- package/app/pages/checkout/partials/product-shipping-address-card.jsx +382 -0
- package/app/pages/checkout/partials/shipment-details.jsx +209 -0
- package/app/pages/checkout/partials/shipment-details.test.js +246 -0
- package/app/pages/checkout/partials/shipping-address.jsx +156 -68
- package/app/pages/checkout/partials/shipping-address.test.js +673 -0
- package/app/pages/checkout/partials/shipping-method-options.jsx +180 -0
- package/app/pages/checkout/partials/shipping-methods.jsx +403 -0
- package/app/pages/checkout/partials/shipping-methods.test.js +472 -0
- package/app/pages/checkout/partials/shipping-multi-address.jsx +259 -0
- package/app/pages/checkout/partials/shipping-multi-address.test.js +2088 -0
- package/app/pages/checkout/partials/shipping-product-cards.jsx +101 -0
- package/app/pages/checkout/util/checkout-context.js +25 -18
- package/app/pages/login/index.jsx +2 -6
- package/app/pages/product-detail/index.jsx +96 -81
- package/app/pages/product-detail/index.test.js +103 -19
- package/app/pages/product-list/index.jsx +3 -1
- package/app/pages/product-list/partials/inventory-filter.jsx +18 -21
- package/app/pages/product-list/partials/inventory-filter.test.js +15 -17
- package/app/pages/product-list/partials/selected-refinements.jsx +3 -1
- package/app/ssr.js +1 -1
- package/app/static/translations/compiled/en-GB.json +316 -30
- package/app/static/translations/compiled/en-US.json +316 -30
- package/app/static/translations/compiled/en-XA.json +673 -75
- package/app/utils/address-utils.js +112 -0
- package/app/utils/address-utils.test.js +484 -0
- package/app/utils/product-utils.js +17 -5
- package/app/utils/product-utils.test.js +17 -8
- package/app/utils/sfdc-user-agent-utils.js +32 -0
- package/app/utils/sfdc-user-agent-utils.test.js +82 -0
- package/app/utils/shipment-utils.js +196 -0
- package/app/utils/shipment-utils.test.js +458 -0
- package/app/utils/test-utils.js +4 -4
- package/app/utils/utils.js +6 -1
- package/config/default.js +4 -1
- package/config/mocks/default.js +3 -1
- package/package.json +9 -9
- package/translations/en-GB.json +127 -10
- package/translations/en-US.json +127 -10
- 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
|
+
})
|