@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,673 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023, 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
|
+
import React from 'react'
|
|
8
|
+
import {render, screen, fireEvent, waitFor} from '@testing-library/react'
|
|
9
|
+
import {IntlProvider} from 'react-intl'
|
|
10
|
+
import {QueryClient, QueryClientProvider} from '@tanstack/react-query'
|
|
11
|
+
import ShippingAddress from '@salesforce/retail-react-app/app/pages/checkout/partials/shipping-address'
|
|
12
|
+
import {useCurrentCustomer} from '@salesforce/retail-react-app/app/hooks/use-current-customer'
|
|
13
|
+
import {useCurrentBasket} from '@salesforce/retail-react-app/app/hooks/use-current-basket'
|
|
14
|
+
import {useCheckout} from '@salesforce/retail-react-app/app/pages/checkout/util/checkout-context'
|
|
15
|
+
|
|
16
|
+
// Mock the hooks
|
|
17
|
+
jest.mock('@salesforce/retail-react-app/app/pages/checkout/util/checkout-context')
|
|
18
|
+
jest.mock('@salesforce/retail-react-app/app/hooks/use-current-customer')
|
|
19
|
+
jest.mock('@salesforce/retail-react-app/app/hooks/use-current-basket')
|
|
20
|
+
jest.mock('@salesforce/retail-react-app/app/hooks/use-toast')
|
|
21
|
+
|
|
22
|
+
// Mock the new multiship and pickup hooks
|
|
23
|
+
jest.mock('@salesforce/retail-react-app/app/hooks/use-multiship')
|
|
24
|
+
jest.mock('@salesforce/retail-react-app/app/hooks/use-pickup-shipment')
|
|
25
|
+
|
|
26
|
+
// Mock the constants and getConfig with dynamic values for testing
|
|
27
|
+
let mockMultishipEnabled = true
|
|
28
|
+
let mockStoreLocatorEnabled = true
|
|
29
|
+
|
|
30
|
+
jest.mock('@salesforce/pwa-kit-runtime/utils/ssr-config', () => ({
|
|
31
|
+
getConfig: jest.fn(() => ({
|
|
32
|
+
app: {
|
|
33
|
+
multishipEnabled: mockMultishipEnabled,
|
|
34
|
+
storeLocatorEnabled: mockStoreLocatorEnabled
|
|
35
|
+
}
|
|
36
|
+
}))
|
|
37
|
+
}))
|
|
38
|
+
|
|
39
|
+
jest.mock('@salesforce/retail-react-app/app/constants', () => ({
|
|
40
|
+
get DEFAULT_SHIPMENT_ID() {
|
|
41
|
+
return 'me'
|
|
42
|
+
},
|
|
43
|
+
get STORE_LOCATOR_IS_ENABLED() {
|
|
44
|
+
return mockStoreLocatorEnabled
|
|
45
|
+
}
|
|
46
|
+
}))
|
|
47
|
+
|
|
48
|
+
// Helper function to set multishipEnabled for tests
|
|
49
|
+
const setMultishipEnabled = (enabled) => {
|
|
50
|
+
mockMultishipEnabled = enabled
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
52
|
+
const {getConfig} = require('@salesforce/pwa-kit-runtime/utils/ssr-config')
|
|
53
|
+
getConfig.mockReturnValue({
|
|
54
|
+
app: {
|
|
55
|
+
multishipEnabled: enabled,
|
|
56
|
+
storeLocatorEnabled: mockStoreLocatorEnabled
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Helper function to set storeLocatorEnabled for tests
|
|
62
|
+
const setStoreLocatorEnabled = (enabled) => {
|
|
63
|
+
mockStoreLocatorEnabled = enabled
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
65
|
+
const {getConfig} = require('@salesforce/pwa-kit-runtime/utils/ssr-config')
|
|
66
|
+
getConfig.mockReturnValue({
|
|
67
|
+
app: {
|
|
68
|
+
multishipEnabled: mockMultishipEnabled,
|
|
69
|
+
storeLocatorEnabled: enabled
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Mock mutation hooks to prevent QueryClient errors
|
|
75
|
+
const mockMutateAsync = jest.fn().mockResolvedValue({})
|
|
76
|
+
const mockCustomerMutateAsync = jest.fn().mockResolvedValue({})
|
|
77
|
+
|
|
78
|
+
jest.mock('@salesforce/commerce-sdk-react', () => {
|
|
79
|
+
const originalModule = jest.requireActual('@salesforce/commerce-sdk-react')
|
|
80
|
+
return {
|
|
81
|
+
...originalModule,
|
|
82
|
+
useShopperCustomersMutation: () => ({
|
|
83
|
+
mutateAsync: mockCustomerMutateAsync
|
|
84
|
+
}),
|
|
85
|
+
useShopperBasketsMutation: () => ({
|
|
86
|
+
mutateAsync: mockMutateAsync
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// Mock the toggle card components
|
|
92
|
+
jest.mock('@salesforce/retail-react-app/app/components/toggle-card', () => {
|
|
93
|
+
// eslint-disable-next-line react/prop-types
|
|
94
|
+
const ToggleCard = ({children, editing, onEdit, editLabel, editAction, onEditActionClick}) => (
|
|
95
|
+
<div data-testid="toggle-card" data-editing={editing ? 'true' : 'false'}>
|
|
96
|
+
{!editing && (
|
|
97
|
+
<button data-testid="edit-button" onClick={onEdit}>
|
|
98
|
+
{editLabel}
|
|
99
|
+
</button>
|
|
100
|
+
)}
|
|
101
|
+
{editing && editAction && onEditActionClick && (
|
|
102
|
+
<button data-testid="edit-action-button" onClick={onEditActionClick}>
|
|
103
|
+
{editAction}
|
|
104
|
+
</button>
|
|
105
|
+
)}
|
|
106
|
+
{children}
|
|
107
|
+
</div>
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
// eslint-disable-next-line react/prop-types
|
|
111
|
+
const ToggleCardEdit = ({children}) => <div data-testid="toggle-card-edit">{children}</div>
|
|
112
|
+
|
|
113
|
+
// eslint-disable-next-line react/prop-types
|
|
114
|
+
const ToggleCardSummary = ({children}) => (
|
|
115
|
+
<div data-testid="toggle-card-summary">{children}</div>
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
ToggleCard,
|
|
120
|
+
ToggleCardEdit,
|
|
121
|
+
ToggleCardSummary
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
// Mock the shipping address selection component
|
|
126
|
+
jest.mock(
|
|
127
|
+
'@salesforce/retail-react-app/app/pages/checkout/partials/shipping-address-selection',
|
|
128
|
+
() => {
|
|
129
|
+
// eslint-disable-next-line react/prop-types
|
|
130
|
+
function MockShippingAddressSelection({onSubmit}) {
|
|
131
|
+
const mockAddress = {
|
|
132
|
+
addressId: 'addr-1',
|
|
133
|
+
address1: '123 Test St',
|
|
134
|
+
city: 'Test City',
|
|
135
|
+
countryCode: 'US',
|
|
136
|
+
firstName: 'John',
|
|
137
|
+
lastName: 'Doe',
|
|
138
|
+
phone: '555-555-5555',
|
|
139
|
+
postalCode: '12345',
|
|
140
|
+
stateCode: 'CA'
|
|
141
|
+
}
|
|
142
|
+
return (
|
|
143
|
+
<div data-testid="shipping-address-selection" role="button" tabIndex={0}>
|
|
144
|
+
Mock Shipping Address Selection
|
|
145
|
+
<button data-testid="submit-address" onClick={() => onSubmit(mockAddress)}>
|
|
146
|
+
Submit Address
|
|
147
|
+
</button>
|
|
148
|
+
</div>
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
return MockShippingAddressSelection
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
// Mock the multi-shipping component
|
|
156
|
+
jest.mock(
|
|
157
|
+
'@salesforce/retail-react-app/app/pages/checkout/partials/shipping-multi-address',
|
|
158
|
+
() => ({
|
|
159
|
+
__esModule: true,
|
|
160
|
+
default: function MockMultiShipping() {
|
|
161
|
+
const {
|
|
162
|
+
useCheckout
|
|
163
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
164
|
+
} = require('@salesforce/retail-react-app/app/pages/checkout/util/checkout-context')
|
|
165
|
+
|
|
166
|
+
const {goToStep, STEPS} = useCheckout()
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<div data-testid="multi-shipping" role="button" tabIndex={0}>
|
|
170
|
+
Mock Multi Shipping
|
|
171
|
+
<button
|
|
172
|
+
data-testid="submit-multi-address"
|
|
173
|
+
onClick={() => {
|
|
174
|
+
goToStep(STEPS.SHIPPING_OPTIONS)
|
|
175
|
+
}}
|
|
176
|
+
>
|
|
177
|
+
Submit Multi Address
|
|
178
|
+
</button>
|
|
179
|
+
</div>
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
const mockCustomer = {
|
|
186
|
+
customerId: 'customer-1',
|
|
187
|
+
isRegistered: true,
|
|
188
|
+
addresses: [
|
|
189
|
+
{
|
|
190
|
+
addressId: 'addr-1',
|
|
191
|
+
firstName: 'John',
|
|
192
|
+
lastName: 'Doe',
|
|
193
|
+
address1: '123 Test St',
|
|
194
|
+
city: 'Test City',
|
|
195
|
+
stateCode: 'CA',
|
|
196
|
+
postalCode: '12345'
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
addressId: 'addr-2',
|
|
200
|
+
firstName: 'Jane',
|
|
201
|
+
lastName: 'Smith',
|
|
202
|
+
address1: '456 Another St',
|
|
203
|
+
city: 'Another City',
|
|
204
|
+
stateCode: 'NY',
|
|
205
|
+
postalCode: '67890'
|
|
206
|
+
}
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const mockBasket = {
|
|
211
|
+
basketId: 'basket-1',
|
|
212
|
+
productItems: [
|
|
213
|
+
{
|
|
214
|
+
productId: 'product-1',
|
|
215
|
+
productName: 'Test Product 1',
|
|
216
|
+
quantity: 2,
|
|
217
|
+
itemId: 'item-1',
|
|
218
|
+
shipmentId: 'me'
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
productId: 'product-2',
|
|
222
|
+
productName: 'Test Product 2',
|
|
223
|
+
quantity: 1,
|
|
224
|
+
itemId: 'item-2',
|
|
225
|
+
shipmentId: 'me'
|
|
226
|
+
}
|
|
227
|
+
],
|
|
228
|
+
shipments: [
|
|
229
|
+
{
|
|
230
|
+
shipmentId: 'me',
|
|
231
|
+
shippingMethod: {
|
|
232
|
+
id: 'standard-shipping',
|
|
233
|
+
c_storePickupEnabled: false
|
|
234
|
+
},
|
|
235
|
+
shippingAddress: {
|
|
236
|
+
address1: '123 Test St',
|
|
237
|
+
city: 'Test City',
|
|
238
|
+
stateCode: 'CA',
|
|
239
|
+
postalCode: '12345',
|
|
240
|
+
countryCode: 'US',
|
|
241
|
+
firstName: 'John',
|
|
242
|
+
lastName: 'Doe'
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
]
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const mockShowToast = jest.fn()
|
|
249
|
+
|
|
250
|
+
const mockCheckoutContext = {
|
|
251
|
+
step: 3, // SHIPPING_ADDRESS
|
|
252
|
+
goToStep: jest.fn(),
|
|
253
|
+
STEPS: {
|
|
254
|
+
SHIPPING_ADDRESS: 3,
|
|
255
|
+
SHIPPING_OPTIONS: 4,
|
|
256
|
+
PAYMENT: 5,
|
|
257
|
+
REVIEW_ORDER: 6
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const defaultProps = {
|
|
262
|
+
basket: mockBasket,
|
|
263
|
+
customer: mockCustomer
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const renderWithIntl = (component) => {
|
|
267
|
+
const queryClient = new QueryClient({
|
|
268
|
+
defaultOptions: {
|
|
269
|
+
queries: {
|
|
270
|
+
retry: false
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
})
|
|
274
|
+
return render(
|
|
275
|
+
<QueryClientProvider client={queryClient}>
|
|
276
|
+
<IntlProvider locale="en">{component}</IntlProvider>
|
|
277
|
+
</QueryClientProvider>
|
|
278
|
+
)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
describe('ShippingAddress', () => {
|
|
282
|
+
beforeEach(() => {
|
|
283
|
+
// Reset multishipEnabled to default value for test isolation
|
|
284
|
+
setMultishipEnabled(true)
|
|
285
|
+
setStoreLocatorEnabled(true)
|
|
286
|
+
|
|
287
|
+
mockCheckoutContext.goToStep.mockClear()
|
|
288
|
+
mockShowToast.mockClear()
|
|
289
|
+
mockMutateAsync.mockClear()
|
|
290
|
+
mockCustomerMutateAsync.mockClear()
|
|
291
|
+
useCurrentCustomer.mockReturnValue({
|
|
292
|
+
data: mockCustomer
|
|
293
|
+
})
|
|
294
|
+
useCurrentBasket.mockReturnValue({
|
|
295
|
+
data: mockBasket
|
|
296
|
+
})
|
|
297
|
+
useCheckout.mockReturnValue(mockCheckoutContext)
|
|
298
|
+
|
|
299
|
+
// Mock useMultiship hook
|
|
300
|
+
const useMultiship =
|
|
301
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
302
|
+
require('@salesforce/retail-react-app/app/hooks/use-multiship').useMultiship
|
|
303
|
+
useMultiship.mockReturnValue({
|
|
304
|
+
findExistingDeliveryShipment: jest.fn().mockReturnValue(mockBasket.shipments[0]),
|
|
305
|
+
moveItemsToDeliveryShipment: jest.fn().mockResolvedValue(mockBasket),
|
|
306
|
+
removeEmptyShipments: jest.fn().mockResolvedValue()
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
// Mock useToast hook
|
|
310
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
311
|
+
const useToast = require('@salesforce/retail-react-app/app/hooks/use-toast').useToast
|
|
312
|
+
useToast.mockReturnValue(mockShowToast)
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
afterEach(() => {
|
|
316
|
+
jest.clearAllMocks()
|
|
317
|
+
// Reset to default values
|
|
318
|
+
mockMultishipEnabled = true
|
|
319
|
+
mockStoreLocatorEnabled = true
|
|
320
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
321
|
+
const {getConfig} = require('@salesforce/pwa-kit-runtime/utils/ssr-config')
|
|
322
|
+
getConfig.mockReturnValue({
|
|
323
|
+
app: {
|
|
324
|
+
multishipEnabled: mockMultishipEnabled,
|
|
325
|
+
storeLocatorEnabled: mockStoreLocatorEnabled
|
|
326
|
+
}
|
|
327
|
+
})
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
it('should render shipping address selection for single shipping', () => {
|
|
331
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
332
|
+
|
|
333
|
+
expect(screen.getByTestId('shipping-address-selection')).toBeInTheDocument()
|
|
334
|
+
expect(screen.queryByTestId('multi-shipping')).not.toBeInTheDocument()
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
it('should render multi-shipping when multiple items and toggle is enabled', () => {
|
|
338
|
+
// Mock that we're in editing mode and have multiple items
|
|
339
|
+
const editingContext = {
|
|
340
|
+
...mockCheckoutContext,
|
|
341
|
+
step: 3 // SHIPPING_ADDRESS
|
|
342
|
+
}
|
|
343
|
+
useCheckout.mockReturnValue(editingContext)
|
|
344
|
+
|
|
345
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
346
|
+
|
|
347
|
+
// Should show shipping address selection by default
|
|
348
|
+
expect(screen.getByTestId('shipping-address-selection')).toBeInTheDocument()
|
|
349
|
+
// Multi-shipping is not shown by default, only when toggled
|
|
350
|
+
expect(screen.queryByTestId('multi-shipping')).not.toBeInTheDocument()
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
it('should handle single shipping submission', async () => {
|
|
354
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
355
|
+
|
|
356
|
+
fireEvent.click(screen.getByTestId('submit-address'))
|
|
357
|
+
|
|
358
|
+
// Should navigate to shipping options step
|
|
359
|
+
await waitFor(() => {
|
|
360
|
+
expect(mockCheckoutContext.goToStep).toHaveBeenCalledWith(4) // SHIPPING_OPTIONS
|
|
361
|
+
})
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
it('should handle multi-shipping submission', async () => {
|
|
365
|
+
// Mock that we're in editing mode
|
|
366
|
+
const editingContext = {
|
|
367
|
+
...mockCheckoutContext,
|
|
368
|
+
step: 3 // SHIPPING_ADDRESS
|
|
369
|
+
}
|
|
370
|
+
useCheckout.mockReturnValue(editingContext)
|
|
371
|
+
|
|
372
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
373
|
+
|
|
374
|
+
// First click the edit action button to enable multi-shipping
|
|
375
|
+
fireEvent.click(screen.getByTestId('edit-action-button'))
|
|
376
|
+
|
|
377
|
+
// Now the multi-shipping component should be visible
|
|
378
|
+
expect(screen.getByTestId('multi-shipping')).toBeInTheDocument()
|
|
379
|
+
|
|
380
|
+
fireEvent.click(screen.getByTestId('submit-multi-address'))
|
|
381
|
+
|
|
382
|
+
// Should navigate to shipping options step
|
|
383
|
+
await waitFor(() => {
|
|
384
|
+
expect(mockCheckoutContext.goToStep).toHaveBeenCalledWith(4) // SHIPPING_OPTIONS
|
|
385
|
+
})
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
it('should show edit button with correct label for single shipping', () => {
|
|
389
|
+
// Mock that we're NOT in editing mode to get "Edit Shipping Address" label
|
|
390
|
+
const summaryContext = {
|
|
391
|
+
...mockCheckoutContext,
|
|
392
|
+
step: 4 // SHIPPING_OPTIONS (not editing)
|
|
393
|
+
}
|
|
394
|
+
useCheckout.mockReturnValue(summaryContext)
|
|
395
|
+
|
|
396
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
397
|
+
|
|
398
|
+
const editButton = screen.getByTestId('edit-button')
|
|
399
|
+
expect(editButton).toBeInTheDocument()
|
|
400
|
+
expect(editButton).toHaveTextContent('Edit Shipping Address')
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
it('should show edit action button with correct label for multi-shipping when enabled', () => {
|
|
404
|
+
// Mock that we're in editing mode with multiple items
|
|
405
|
+
const editingContext = {
|
|
406
|
+
...mockCheckoutContext,
|
|
407
|
+
step: 3 // SHIPPING_ADDRESS
|
|
408
|
+
}
|
|
409
|
+
useCheckout.mockReturnValue(editingContext)
|
|
410
|
+
|
|
411
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
412
|
+
|
|
413
|
+
const editActionButton = screen.getByTestId('edit-action-button')
|
|
414
|
+
expect(editActionButton).toBeInTheDocument()
|
|
415
|
+
expect(editActionButton).toHaveTextContent('Ship to Multiple Addresses')
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
it('should handle edit button click for single shipping', () => {
|
|
419
|
+
// Mock that we're NOT in editing mode
|
|
420
|
+
const summaryContext = {
|
|
421
|
+
...mockCheckoutContext,
|
|
422
|
+
step: 4 // SHIPPING_OPTIONS (not editing)
|
|
423
|
+
}
|
|
424
|
+
useCheckout.mockReturnValue(summaryContext)
|
|
425
|
+
|
|
426
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
427
|
+
|
|
428
|
+
fireEvent.click(screen.getByTestId('edit-button'))
|
|
429
|
+
|
|
430
|
+
// Should navigate to shipping address step
|
|
431
|
+
expect(mockCheckoutContext.goToStep).toHaveBeenCalledWith(3) // SHIPPING_ADDRESS
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
it('should handle edit action button click for multi-shipping', () => {
|
|
435
|
+
// Mock that we're in editing mode
|
|
436
|
+
const editingContext = {
|
|
437
|
+
...mockCheckoutContext,
|
|
438
|
+
step: 3 // SHIPPING_ADDRESS
|
|
439
|
+
}
|
|
440
|
+
useCheckout.mockReturnValue(editingContext)
|
|
441
|
+
|
|
442
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
443
|
+
|
|
444
|
+
fireEvent.click(screen.getByTestId('edit-action-button'))
|
|
445
|
+
|
|
446
|
+
// Should enable multi-shipping mode
|
|
447
|
+
expect(screen.getByTestId('multi-shipping')).toBeInTheDocument()
|
|
448
|
+
})
|
|
449
|
+
|
|
450
|
+
it('should handle empty basket gracefully', () => {
|
|
451
|
+
const emptyBasket = {...mockBasket, productItems: []}
|
|
452
|
+
|
|
453
|
+
renderWithIntl(<ShippingAddress {...defaultProps} basket={emptyBasket} />)
|
|
454
|
+
|
|
455
|
+
// Should still render the component
|
|
456
|
+
expect(screen.getByTestId('toggle-card')).toBeInTheDocument()
|
|
457
|
+
})
|
|
458
|
+
|
|
459
|
+
it('should handle missing customer data gracefully', () => {
|
|
460
|
+
useCurrentCustomer.mockReturnValue({
|
|
461
|
+
data: null
|
|
462
|
+
})
|
|
463
|
+
|
|
464
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
465
|
+
|
|
466
|
+
// Should still render the component
|
|
467
|
+
expect(screen.getByTestId('toggle-card')).toBeInTheDocument()
|
|
468
|
+
})
|
|
469
|
+
|
|
470
|
+
it('should handle missing basket data gracefully', () => {
|
|
471
|
+
useCurrentBasket.mockReturnValue({
|
|
472
|
+
data: null
|
|
473
|
+
})
|
|
474
|
+
|
|
475
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
476
|
+
|
|
477
|
+
// Should still render the component
|
|
478
|
+
expect(screen.getByTestId('toggle-card')).toBeInTheDocument()
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
it('should show toast error when address submission fails', async () => {
|
|
482
|
+
// Mock the mutation to throw an error
|
|
483
|
+
mockMutateAsync.mockRejectedValueOnce(new Error('Network error'))
|
|
484
|
+
|
|
485
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
486
|
+
|
|
487
|
+
// Submit the address form
|
|
488
|
+
fireEvent.click(screen.getByTestId('submit-address'))
|
|
489
|
+
|
|
490
|
+
// Wait for the error to be handled
|
|
491
|
+
await waitFor(() => {
|
|
492
|
+
expect(mockShowToast).toHaveBeenCalledWith({
|
|
493
|
+
title: 'Something went wrong while updating the shipping address. Try again.',
|
|
494
|
+
status: 'error'
|
|
495
|
+
})
|
|
496
|
+
})
|
|
497
|
+
|
|
498
|
+
// Verify that goToStep was not called due to the error
|
|
499
|
+
expect(mockCheckoutContext.goToStep).not.toHaveBeenCalled()
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
describe('Toggle Card Behavior', () => {
|
|
503
|
+
it('should show edit mode when in shipping address step', () => {
|
|
504
|
+
const editingContext = {
|
|
505
|
+
...mockCheckoutContext,
|
|
506
|
+
step: 3 // SHIPPING_ADDRESS
|
|
507
|
+
}
|
|
508
|
+
useCheckout.mockReturnValue(editingContext)
|
|
509
|
+
|
|
510
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
511
|
+
|
|
512
|
+
const toggleCard = screen.getByTestId('toggle-card')
|
|
513
|
+
expect(toggleCard).toHaveAttribute('data-editing', 'true')
|
|
514
|
+
})
|
|
515
|
+
|
|
516
|
+
it('should show summary mode when not in shipping address step', () => {
|
|
517
|
+
const summaryContext = {
|
|
518
|
+
...mockCheckoutContext,
|
|
519
|
+
step: 4 // SHIPPING_OPTIONS
|
|
520
|
+
}
|
|
521
|
+
useCheckout.mockReturnValue(summaryContext)
|
|
522
|
+
|
|
523
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
524
|
+
|
|
525
|
+
const toggleCard = screen.getByTestId('toggle-card')
|
|
526
|
+
expect(toggleCard).toHaveAttribute('data-editing', 'false')
|
|
527
|
+
})
|
|
528
|
+
})
|
|
529
|
+
|
|
530
|
+
describe('Multi-shipping Toggle', () => {
|
|
531
|
+
it('should show multi-shipping option when multiple items exist', () => {
|
|
532
|
+
const editingContext = {
|
|
533
|
+
...mockCheckoutContext,
|
|
534
|
+
step: 3 // SHIPPING_ADDRESS
|
|
535
|
+
}
|
|
536
|
+
useCheckout.mockReturnValue(editingContext)
|
|
537
|
+
|
|
538
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
539
|
+
|
|
540
|
+
// Should show shipping address selection by default
|
|
541
|
+
expect(screen.getByTestId('shipping-address-selection')).toBeInTheDocument()
|
|
542
|
+
// Multi-shipping is not shown by default, only when toggled
|
|
543
|
+
expect(screen.queryByTestId('multi-shipping')).not.toBeInTheDocument()
|
|
544
|
+
|
|
545
|
+
// Click edit action button to enable multi-shipping
|
|
546
|
+
fireEvent.click(screen.getByTestId('edit-action-button'))
|
|
547
|
+
|
|
548
|
+
// Now multi-shipping should be visible
|
|
549
|
+
expect(screen.getByTestId('multi-shipping')).toBeInTheDocument()
|
|
550
|
+
})
|
|
551
|
+
|
|
552
|
+
it('should not show multi-shipping option when only one item exists', () => {
|
|
553
|
+
const singleItemBasket = {
|
|
554
|
+
...mockBasket,
|
|
555
|
+
productItems: [mockBasket.productItems[0]]
|
|
556
|
+
}
|
|
557
|
+
const editingContext = {
|
|
558
|
+
...mockCheckoutContext,
|
|
559
|
+
step: 3 // SHIPPING_ADDRESS
|
|
560
|
+
}
|
|
561
|
+
useCheckout.mockReturnValue(editingContext)
|
|
562
|
+
|
|
563
|
+
renderWithIntl(<ShippingAddress {...defaultProps} basket={singleItemBasket} />)
|
|
564
|
+
|
|
565
|
+
// Should show shipping address selection
|
|
566
|
+
expect(screen.getByTestId('shipping-address-selection')).toBeInTheDocument()
|
|
567
|
+
// Multi-shipping should not be available for single item
|
|
568
|
+
expect(screen.queryByTestId('multi-shipping')).not.toBeInTheDocument()
|
|
569
|
+
})
|
|
570
|
+
})
|
|
571
|
+
|
|
572
|
+
describe('multishipEnabled behavior', () => {
|
|
573
|
+
describe('when multishipEnabled is true', () => {
|
|
574
|
+
beforeEach(() => {
|
|
575
|
+
setMultishipEnabled(true)
|
|
576
|
+
})
|
|
577
|
+
|
|
578
|
+
it('should show "Ship to Multiple Addresses" button when multishipEnabled is true', () => {
|
|
579
|
+
// Mock that we're in editing mode with multiple items
|
|
580
|
+
const editingContext = {
|
|
581
|
+
...mockCheckoutContext,
|
|
582
|
+
step: 3 // SHIPPING_ADDRESS
|
|
583
|
+
}
|
|
584
|
+
useCheckout.mockReturnValue(editingContext)
|
|
585
|
+
|
|
586
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
587
|
+
|
|
588
|
+
const editActionButton = screen.getByTestId('edit-action-button')
|
|
589
|
+
expect(editActionButton).toBeInTheDocument()
|
|
590
|
+
expect(editActionButton).toHaveTextContent('Ship to Multiple Addresses')
|
|
591
|
+
})
|
|
592
|
+
|
|
593
|
+
it('should handle "Ship to Multiple Addresses" button click to toggle multi-shipping', () => {
|
|
594
|
+
// Mock that we're in editing mode
|
|
595
|
+
const editingContext = {
|
|
596
|
+
...mockCheckoutContext,
|
|
597
|
+
step: 3 // SHIPPING_ADDRESS
|
|
598
|
+
}
|
|
599
|
+
useCheckout.mockReturnValue(editingContext)
|
|
600
|
+
|
|
601
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
602
|
+
|
|
603
|
+
const editActionButton = screen.getByTestId('edit-action-button')
|
|
604
|
+
fireEvent.click(editActionButton)
|
|
605
|
+
expect(screen.getByTestId('multi-shipping')).toBeInTheDocument()
|
|
606
|
+
})
|
|
607
|
+
})
|
|
608
|
+
|
|
609
|
+
describe('when multishipEnabled is false', () => {
|
|
610
|
+
beforeEach(() => {
|
|
611
|
+
setMultishipEnabled(false)
|
|
612
|
+
})
|
|
613
|
+
|
|
614
|
+
it('should not show "Ship to Multiple Addresses" button when multishipEnabled is false', () => {
|
|
615
|
+
const editingContext = {
|
|
616
|
+
...mockCheckoutContext,
|
|
617
|
+
step: 3 // SHIPPING_ADDRESS
|
|
618
|
+
}
|
|
619
|
+
useCheckout.mockReturnValue(editingContext)
|
|
620
|
+
|
|
621
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
622
|
+
|
|
623
|
+
expect(screen.queryByTestId('edit-action-button')).not.toBeInTheDocument()
|
|
624
|
+
})
|
|
625
|
+
|
|
626
|
+
it('should still show shipping address selection when multishipEnabled is false', () => {
|
|
627
|
+
// Mock that we're in editing mode
|
|
628
|
+
const editingContext = {
|
|
629
|
+
...mockCheckoutContext,
|
|
630
|
+
step: 3 // SHIPPING_ADDRESS
|
|
631
|
+
}
|
|
632
|
+
useCheckout.mockReturnValue(editingContext)
|
|
633
|
+
|
|
634
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
635
|
+
|
|
636
|
+
// Should still show shipping address selection
|
|
637
|
+
expect(screen.getByTestId('shipping-address-selection')).toBeInTheDocument()
|
|
638
|
+
// But no "Ship to Multiple Addresses" button
|
|
639
|
+
expect(screen.queryByTestId('edit-action-button')).not.toBeInTheDocument()
|
|
640
|
+
})
|
|
641
|
+
})
|
|
642
|
+
|
|
643
|
+
describe('common behavior regardless of multishipEnabled setting', () => {
|
|
644
|
+
it('should show edit mode when in shipping address step', () => {
|
|
645
|
+
setMultishipEnabled(true) // Test with enabled
|
|
646
|
+
const editingContext = {
|
|
647
|
+
...mockCheckoutContext,
|
|
648
|
+
step: 3 // SHIPPING_ADDRESS
|
|
649
|
+
}
|
|
650
|
+
useCheckout.mockReturnValue(editingContext)
|
|
651
|
+
|
|
652
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
653
|
+
|
|
654
|
+
const toggleCard = screen.getByTestId('toggle-card')
|
|
655
|
+
expect(toggleCard).toHaveAttribute('data-editing', 'true')
|
|
656
|
+
})
|
|
657
|
+
|
|
658
|
+
it('should show summary mode when not in shipping address step', () => {
|
|
659
|
+
setMultishipEnabled(false) // Test with disabled
|
|
660
|
+
const summaryContext = {
|
|
661
|
+
...mockCheckoutContext,
|
|
662
|
+
step: 4 // SHIPPING_OPTIONS
|
|
663
|
+
}
|
|
664
|
+
useCheckout.mockReturnValue(summaryContext)
|
|
665
|
+
|
|
666
|
+
renderWithIntl(<ShippingAddress {...defaultProps} />)
|
|
667
|
+
|
|
668
|
+
const toggleCard = screen.getByTestId('toggle-card')
|
|
669
|
+
expect(toggleCard).toHaveAttribute('data-editing', 'false')
|
|
670
|
+
})
|
|
671
|
+
})
|
|
672
|
+
})
|
|
673
|
+
})
|