@faststore/core 4.10.0-dev.0 → 4.10.0-dev.1
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/.turbo/turbo-generate.log +3 -3
- package/.turbo/turbo-test.log +161 -136
- package/@generated/gql.ts +3 -3
- package/@generated/graphql.ts +12 -3
- package/@generated/persisted-documents.json +1 -1
- package/@generated/schema.graphql +21 -5
- package/CHANGELOG.md +16 -0
- package/package.json +6 -6
- package/src/components/region/RegionSlider/RegionSlider.tsx +1 -0
- package/src/sdk/deliveryPromise/provider.tsx +138 -9
- package/src/sdk/deliveryPromise/queries.ts +19 -2
- package/src/sdk/deliveryPromise/reducer.ts +7 -1
- package/test/sdk/deliveryPromise/provider.effect.test.tsx +440 -0
- package/test/sdk/deliveryPromise/provider.test.ts +99 -0
- package/test/sdk/deliveryPromise/queries.test.ts +123 -0
- package/test/sdk/deliveryPromise/reducer.test.ts +46 -0
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vitest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import { act, render, waitFor } from '@testing-library/react'
|
|
5
|
+
import type { Dispatch } from 'react'
|
|
6
|
+
import { useEffect } from 'react'
|
|
7
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
8
|
+
|
|
9
|
+
import type { DeliveryPromiseReducerAction } from '../../../src/sdk/deliveryPromise/reducer'
|
|
10
|
+
|
|
11
|
+
const { getPickupPoints, session } = vi.hoisted(() => ({
|
|
12
|
+
getPickupPoints: vi.fn(),
|
|
13
|
+
session: {
|
|
14
|
+
postalCode: '01310100' as string | null,
|
|
15
|
+
country: 'BRA' as string | null,
|
|
16
|
+
channel: '{"salesChannel":"1"}' as string | null,
|
|
17
|
+
geoCoordinates: { latitude: -23.5, longitude: -46.6 } as {
|
|
18
|
+
latitude: number
|
|
19
|
+
longitude: number
|
|
20
|
+
} | null,
|
|
21
|
+
},
|
|
22
|
+
}))
|
|
23
|
+
|
|
24
|
+
vi.mock('src/sdk/session', () => ({
|
|
25
|
+
useSession: () => session,
|
|
26
|
+
}))
|
|
27
|
+
|
|
28
|
+
vi.mock('../../../src/sdk/deliveryPromise/queries', () => ({
|
|
29
|
+
getPickupPoints,
|
|
30
|
+
}))
|
|
31
|
+
|
|
32
|
+
import {
|
|
33
|
+
DeliveryPromiseProvider,
|
|
34
|
+
useDeliveryPromiseContext,
|
|
35
|
+
} from '../../../src/sdk/deliveryPromise/provider'
|
|
36
|
+
import { deliveryPromiseStore } from '../../../src/sdk/deliveryPromise/useDeliveryPromise'
|
|
37
|
+
|
|
38
|
+
// Mirrors the store → reducer subscription in useDeliveryPromise.
|
|
39
|
+
// This suite does not cover that hook: if the hook stops syncing, these tests still pass.
|
|
40
|
+
function StoreSync() {
|
|
41
|
+
const { dispatchDeliveryPromiseAction } = useDeliveryPromiseContext()
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
return deliveryPromiseStore.subscribe((storeValue) => {
|
|
45
|
+
dispatchDeliveryPromiseAction({
|
|
46
|
+
type: 'updateDeliveryPromiseState',
|
|
47
|
+
payload: storeValue,
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
}, [dispatchDeliveryPromiseAction])
|
|
51
|
+
|
|
52
|
+
return null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function renderProvider(
|
|
56
|
+
onDispatch: (dispatch: Dispatch<DeliveryPromiseReducerAction>) => void,
|
|
57
|
+
onLoadingFlag?: (shouldUpdatePickupPoints: boolean) => void
|
|
58
|
+
) {
|
|
59
|
+
return render(
|
|
60
|
+
<DeliveryPromiseProvider>
|
|
61
|
+
<StoreSync />
|
|
62
|
+
<DispatchBridge onDispatch={onDispatch} />
|
|
63
|
+
{onLoadingFlag ? <LoadingFlagProbe onFlag={onLoadingFlag} /> : null}
|
|
64
|
+
</DeliveryPromiseProvider>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function LoadingFlagProbe({
|
|
69
|
+
onFlag,
|
|
70
|
+
}: {
|
|
71
|
+
onFlag: (shouldUpdatePickupPoints: boolean) => void
|
|
72
|
+
}) {
|
|
73
|
+
const { shouldUpdatePickupPoints } = useDeliveryPromiseContext()
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
onFlag(shouldUpdatePickupPoints)
|
|
77
|
+
}, [onFlag, shouldUpdatePickupPoints])
|
|
78
|
+
|
|
79
|
+
return null
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function DispatchBridge({
|
|
83
|
+
onDispatch,
|
|
84
|
+
}: {
|
|
85
|
+
onDispatch: (dispatch: Dispatch<DeliveryPromiseReducerAction>) => void
|
|
86
|
+
}) {
|
|
87
|
+
const { dispatchDeliveryPromiseAction } = useDeliveryPromiseContext()
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
onDispatch(dispatchDeliveryPromiseAction)
|
|
91
|
+
}, [dispatchDeliveryPromiseAction, onDispatch])
|
|
92
|
+
|
|
93
|
+
return null
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function deferred<T>() {
|
|
97
|
+
let resolve: (value: T) => void = () => {}
|
|
98
|
+
let reject: (error: unknown) => void = () => {}
|
|
99
|
+
const promise = new Promise<T>((res, rej) => {
|
|
100
|
+
resolve = res
|
|
101
|
+
reject = rej
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
return { promise, resolve, reject }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
describe('DeliveryPromiseProvider pickup fetch', () => {
|
|
108
|
+
let dispatch: Dispatch<DeliveryPromiseReducerAction> = () => {}
|
|
109
|
+
const captureDispatch = (next: Dispatch<DeliveryPromiseReducerAction>) => {
|
|
110
|
+
dispatch = next
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
beforeEach(() => {
|
|
114
|
+
getPickupPoints.mockReset()
|
|
115
|
+
session.postalCode = '01310100'
|
|
116
|
+
session.country = 'BRA'
|
|
117
|
+
session.channel = '{"salesChannel":"1"}'
|
|
118
|
+
session.geoCoordinates = { latitude: -23.5, longitude: -46.6 }
|
|
119
|
+
deliveryPromiseStore.set({
|
|
120
|
+
pickupPoints: [],
|
|
121
|
+
defaultPickupPoint: null,
|
|
122
|
+
globalPickupPoint: null,
|
|
123
|
+
shouldUpdatePickupPoints: false,
|
|
124
|
+
simulatePickupPoints: false,
|
|
125
|
+
pickupPointsSimulation: {
|
|
126
|
+
pickupPoints: [],
|
|
127
|
+
geoCoordinates: null,
|
|
128
|
+
postalCode: null,
|
|
129
|
+
country: null,
|
|
130
|
+
},
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('loads pickup points for the shopper channel', async () => {
|
|
135
|
+
getPickupPoints.mockResolvedValueOnce([
|
|
136
|
+
{ id: 'vendemo_sp', name: 'VTEX SP' },
|
|
137
|
+
])
|
|
138
|
+
|
|
139
|
+
renderProvider(captureDispatch)
|
|
140
|
+
|
|
141
|
+
await act(async () => {
|
|
142
|
+
dispatch({
|
|
143
|
+
type: 'onPostalCodeChange',
|
|
144
|
+
payload: {
|
|
145
|
+
simulatePickupPoints: false,
|
|
146
|
+
validatedSession: { postalCode: '01310100', country: 'BRA' },
|
|
147
|
+
},
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
await waitFor(() => {
|
|
152
|
+
expect(deliveryPromiseStore.read()?.pickupPoints).toEqual([
|
|
153
|
+
{ id: 'vendemo_sp', name: 'VTEX SP' },
|
|
154
|
+
])
|
|
155
|
+
})
|
|
156
|
+
expect(getPickupPoints).toHaveBeenCalledWith({
|
|
157
|
+
geoCoordinates: session.geoCoordinates,
|
|
158
|
+
postalCode: '01310100',
|
|
159
|
+
country: 'BRA',
|
|
160
|
+
channel: '{"salesChannel":"1"}',
|
|
161
|
+
})
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('keeps the newer response when the channel changes mid-request', async () => {
|
|
165
|
+
const first = deferred<Array<{ id: string; name: string }>>()
|
|
166
|
+
const second = deferred<Array<{ id: string; name: string }>>()
|
|
167
|
+
getPickupPoints
|
|
168
|
+
.mockImplementationOnce(() => first.promise)
|
|
169
|
+
.mockImplementationOnce(() => second.promise)
|
|
170
|
+
|
|
171
|
+
const view = renderProvider(captureDispatch)
|
|
172
|
+
|
|
173
|
+
await act(async () => {
|
|
174
|
+
dispatch({
|
|
175
|
+
type: 'onPostalCodeChange',
|
|
176
|
+
payload: {
|
|
177
|
+
simulatePickupPoints: false,
|
|
178
|
+
validatedSession: { postalCode: '01310100', country: 'BRA' },
|
|
179
|
+
},
|
|
180
|
+
})
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
session.channel = '{"salesChannel":"2"}'
|
|
184
|
+
view.rerender(
|
|
185
|
+
<DeliveryPromiseProvider>
|
|
186
|
+
<StoreSync />
|
|
187
|
+
<DispatchBridge onDispatch={captureDispatch} />
|
|
188
|
+
</DeliveryPromiseProvider>
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
await waitFor(() => {
|
|
192
|
+
expect(getPickupPoints).toHaveBeenCalledTimes(2)
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
await act(async () => {
|
|
196
|
+
second.resolve([{ id: 'vendemo_rio', name: 'VTEX Rio' }])
|
|
197
|
+
})
|
|
198
|
+
await act(async () => {
|
|
199
|
+
first.resolve([{ id: 'vendemo_stale', name: 'Stale' }])
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
await waitFor(() => {
|
|
203
|
+
expect(deliveryPromiseStore.read()?.pickupPoints).toEqual([
|
|
204
|
+
{ id: 'vendemo_rio', name: 'VTEX Rio' },
|
|
205
|
+
])
|
|
206
|
+
})
|
|
207
|
+
expect(getPickupPoints).toHaveBeenNthCalledWith(
|
|
208
|
+
2,
|
|
209
|
+
expect.objectContaining({ channel: '{"salesChannel":"2"}' })
|
|
210
|
+
)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('drops a response that arrives after the postal code is cleared', async () => {
|
|
214
|
+
const pending = deferred<Array<{ id: string; name: string }>>()
|
|
215
|
+
getPickupPoints.mockImplementationOnce(() => pending.promise)
|
|
216
|
+
|
|
217
|
+
const view = renderProvider(captureDispatch)
|
|
218
|
+
|
|
219
|
+
await act(async () => {
|
|
220
|
+
dispatch({
|
|
221
|
+
type: 'onPostalCodeChange',
|
|
222
|
+
payload: {
|
|
223
|
+
simulatePickupPoints: false,
|
|
224
|
+
validatedSession: { postalCode: '01310100', country: 'BRA' },
|
|
225
|
+
},
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
await waitFor(() => {
|
|
230
|
+
expect(getPickupPoints).toHaveBeenCalledTimes(1)
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
session.postalCode = null
|
|
234
|
+
view.rerender(
|
|
235
|
+
<DeliveryPromiseProvider>
|
|
236
|
+
<StoreSync />
|
|
237
|
+
<DispatchBridge onDispatch={captureDispatch} />
|
|
238
|
+
</DeliveryPromiseProvider>
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
await act(async () => {
|
|
242
|
+
pending.resolve([{ id: 'vendemo_stale', name: 'Stale' }])
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
expect(deliveryPromiseStore.read()?.pickupPoints).toEqual([])
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
it('clears the loading flag after a failed request and retries on the next update', async () => {
|
|
249
|
+
getPickupPoints
|
|
250
|
+
.mockRejectedValueOnce(new Error('network'))
|
|
251
|
+
.mockResolvedValueOnce([{ id: 'vendemo_sp', name: 'VTEX SP' }])
|
|
252
|
+
|
|
253
|
+
let shouldUpdatePickupPoints = true
|
|
254
|
+
renderProvider(captureDispatch, (flag) => {
|
|
255
|
+
shouldUpdatePickupPoints = flag
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
await act(async () => {
|
|
259
|
+
dispatch({
|
|
260
|
+
type: 'onPostalCodeChange',
|
|
261
|
+
payload: {
|
|
262
|
+
simulatePickupPoints: false,
|
|
263
|
+
validatedSession: { postalCode: '01310100', country: 'BRA' },
|
|
264
|
+
},
|
|
265
|
+
})
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
await waitFor(() => {
|
|
269
|
+
expect(getPickupPoints).toHaveBeenCalledTimes(1)
|
|
270
|
+
})
|
|
271
|
+
await waitFor(() => {
|
|
272
|
+
expect(shouldUpdatePickupPoints).toBe(false)
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
await act(async () => {
|
|
276
|
+
dispatch({
|
|
277
|
+
type: 'onPostalCodeChange',
|
|
278
|
+
payload: {
|
|
279
|
+
simulatePickupPoints: false,
|
|
280
|
+
validatedSession: { postalCode: '01310100', country: 'BRA' },
|
|
281
|
+
},
|
|
282
|
+
})
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
await waitFor(() => {
|
|
286
|
+
expect(deliveryPromiseStore.read()?.pickupPoints).toEqual([
|
|
287
|
+
{ id: 'vendemo_sp', name: 'VTEX SP' },
|
|
288
|
+
])
|
|
289
|
+
})
|
|
290
|
+
expect(getPickupPoints).toHaveBeenCalledTimes(2)
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
it('stores a simulated postal code without replacing the session list', async () => {
|
|
294
|
+
getPickupPoints.mockResolvedValueOnce([
|
|
295
|
+
{ id: 'vendemo_rio', name: 'VTEX Rio' },
|
|
296
|
+
])
|
|
297
|
+
|
|
298
|
+
renderProvider(captureDispatch)
|
|
299
|
+
|
|
300
|
+
await act(async () => {
|
|
301
|
+
dispatch({
|
|
302
|
+
type: 'onPostalCodeChange',
|
|
303
|
+
payload: {
|
|
304
|
+
simulatePickupPoints: true,
|
|
305
|
+
validatedSession: {
|
|
306
|
+
postalCode: '22041080',
|
|
307
|
+
country: 'BRA',
|
|
308
|
+
geoCoordinates: { latitude: -22.95, longitude: -43.19 },
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
})
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
await waitFor(() => {
|
|
315
|
+
expect(
|
|
316
|
+
deliveryPromiseStore.read()?.pickupPointsSimulation?.pickupPoints
|
|
317
|
+
).toEqual([{ id: 'vendemo_rio', name: 'VTEX Rio' }])
|
|
318
|
+
})
|
|
319
|
+
expect(getPickupPoints).toHaveBeenCalledWith(
|
|
320
|
+
expect.objectContaining({
|
|
321
|
+
postalCode: '22041080',
|
|
322
|
+
country: 'BRA',
|
|
323
|
+
geoCoordinates: { latitude: -22.95, longitude: -43.19 },
|
|
324
|
+
})
|
|
325
|
+
)
|
|
326
|
+
expect(deliveryPromiseStore.read()?.pickupPoints).toEqual([])
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
it('clears the simulated list when the next postal code fails', async () => {
|
|
330
|
+
getPickupPoints
|
|
331
|
+
.mockResolvedValueOnce([{ id: 'vendemo_sp', name: 'VTEX SP' }])
|
|
332
|
+
.mockRejectedValueOnce(new Error('network'))
|
|
333
|
+
|
|
334
|
+
renderProvider(captureDispatch)
|
|
335
|
+
|
|
336
|
+
await act(async () => {
|
|
337
|
+
dispatch({
|
|
338
|
+
type: 'onPostalCodeChange',
|
|
339
|
+
payload: {
|
|
340
|
+
simulatePickupPoints: true,
|
|
341
|
+
validatedSession: { postalCode: '01310100', country: 'BRA' },
|
|
342
|
+
},
|
|
343
|
+
})
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
await waitFor(() => {
|
|
347
|
+
expect(
|
|
348
|
+
deliveryPromiseStore.read()?.pickupPointsSimulation?.pickupPoints
|
|
349
|
+
).toEqual([{ id: 'vendemo_sp', name: 'VTEX SP' }])
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
await act(async () => {
|
|
353
|
+
dispatch({
|
|
354
|
+
type: 'onPostalCodeChange',
|
|
355
|
+
payload: {
|
|
356
|
+
simulatePickupPoints: true,
|
|
357
|
+
validatedSession: { postalCode: '22041080', country: 'BRA' },
|
|
358
|
+
},
|
|
359
|
+
})
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
await waitFor(() => {
|
|
363
|
+
expect(
|
|
364
|
+
deliveryPromiseStore.read()?.pickupPointsSimulation?.pickupPoints
|
|
365
|
+
).toEqual([])
|
|
366
|
+
})
|
|
367
|
+
expect(deliveryPromiseStore.read()?.pickupPoints).toEqual([])
|
|
368
|
+
expect(deliveryPromiseStore.read()?.shouldUpdatePickupPoints).toBe(false)
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
it('keeps the selected store when it is still in the new list', async () => {
|
|
372
|
+
const point = { id: 'vendemo_sp', name: 'VTEX SP' }
|
|
373
|
+
getPickupPoints.mockResolvedValueOnce([point])
|
|
374
|
+
|
|
375
|
+
renderProvider(captureDispatch)
|
|
376
|
+
|
|
377
|
+
await act(async () => {
|
|
378
|
+
dispatch({ type: 'changeGlobalPickupPoint', payload: point })
|
|
379
|
+
dispatch({
|
|
380
|
+
type: 'onPostalCodeChange',
|
|
381
|
+
payload: {
|
|
382
|
+
simulatePickupPoints: false,
|
|
383
|
+
validatedSession: { postalCode: '01310100', country: 'BRA' },
|
|
384
|
+
},
|
|
385
|
+
})
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
await waitFor(() => {
|
|
389
|
+
expect(deliveryPromiseStore.read()?.globalPickupPoint).toEqual(point)
|
|
390
|
+
})
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
it('clears the selected store when it is missing from the new list', async () => {
|
|
394
|
+
getPickupPoints.mockResolvedValueOnce([
|
|
395
|
+
{ id: 'vendemo_rio', name: 'VTEX Rio' },
|
|
396
|
+
])
|
|
397
|
+
|
|
398
|
+
renderProvider(captureDispatch)
|
|
399
|
+
|
|
400
|
+
await act(async () => {
|
|
401
|
+
dispatch({
|
|
402
|
+
type: 'changeGlobalPickupPoint',
|
|
403
|
+
payload: { id: 'vendemo_sp', name: 'VTEX SP' },
|
|
404
|
+
})
|
|
405
|
+
dispatch({
|
|
406
|
+
type: 'onPostalCodeChange',
|
|
407
|
+
payload: {
|
|
408
|
+
simulatePickupPoints: false,
|
|
409
|
+
validatedSession: { postalCode: '01310100', country: 'BRA' },
|
|
410
|
+
},
|
|
411
|
+
})
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
await waitFor(() => {
|
|
415
|
+
expect(deliveryPromiseStore.read()?.globalPickupPoint).toBeNull()
|
|
416
|
+
})
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
it('does not rewrite the list when the fetched points are unchanged', async () => {
|
|
420
|
+
const point = { id: 'vendemo_sp', name: 'VTEX SP' }
|
|
421
|
+
getPickupPoints.mockResolvedValueOnce([point])
|
|
422
|
+
|
|
423
|
+
renderProvider(captureDispatch)
|
|
424
|
+
|
|
425
|
+
await act(async () => {
|
|
426
|
+
dispatch({
|
|
427
|
+
type: 'updatePickupPoints',
|
|
428
|
+
payload: {
|
|
429
|
+
pickupPoints: [point],
|
|
430
|
+
shouldUpdatePickupPoints: true,
|
|
431
|
+
},
|
|
432
|
+
})
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
await waitFor(() => {
|
|
436
|
+
expect(getPickupPoints).toHaveBeenCalledTimes(1)
|
|
437
|
+
})
|
|
438
|
+
expect(deliveryPromiseStore.read()?.pickupPoints).toEqual([])
|
|
439
|
+
})
|
|
440
|
+
})
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
isCurrentPickupRequest,
|
|
5
|
+
pickupLocationKey,
|
|
6
|
+
shouldRefreshPickupPoints,
|
|
7
|
+
} from '../../../src/sdk/deliveryPromise/provider'
|
|
8
|
+
|
|
9
|
+
describe('shouldRefreshPickupPoints', () => {
|
|
10
|
+
const channel = '{"salesChannel":"1"}'
|
|
11
|
+
const locationKey = pickupLocationKey(channel, 'BRA')
|
|
12
|
+
|
|
13
|
+
it('does not fetch without a postal code', () => {
|
|
14
|
+
expect(shouldRefreshPickupPoints(null, true, null, locationKey)).toBe(false)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('fetches when the update flag is set', () => {
|
|
18
|
+
expect(shouldRefreshPickupPoints('01310100', true, null, locationKey)).toBe(
|
|
19
|
+
true
|
|
20
|
+
)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('does not refetch the same channel and country after the flag is cleared', () => {
|
|
24
|
+
expect(
|
|
25
|
+
shouldRefreshPickupPoints('01310100', false, locationKey, locationKey)
|
|
26
|
+
).toBe(false)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('refetches when the shopper channel changes after the flag is cleared', () => {
|
|
30
|
+
const nextKey = pickupLocationKey('{"salesChannel":"2"}', 'BRA')
|
|
31
|
+
|
|
32
|
+
expect(
|
|
33
|
+
shouldRefreshPickupPoints('01310100', false, locationKey, nextKey)
|
|
34
|
+
).toBe(true)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('refetches when the country changes and the channel stays the same', () => {
|
|
38
|
+
const nextKey = pickupLocationKey(channel, 'USA')
|
|
39
|
+
|
|
40
|
+
expect(
|
|
41
|
+
shouldRefreshPickupPoints('01310100', false, locationKey, nextKey)
|
|
42
|
+
).toBe(true)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('does not refetch a location that just failed', () => {
|
|
46
|
+
const nextKey = pickupLocationKey('{"salesChannel":"2"}', 'BRA')
|
|
47
|
+
|
|
48
|
+
expect(
|
|
49
|
+
shouldRefreshPickupPoints(
|
|
50
|
+
'01310100',
|
|
51
|
+
false,
|
|
52
|
+
locationKey,
|
|
53
|
+
nextKey,
|
|
54
|
+
nextKey
|
|
55
|
+
)
|
|
56
|
+
).toBe(false)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('refetches a stored list before this page has fetched', () => {
|
|
60
|
+
expect(
|
|
61
|
+
shouldRefreshPickupPoints(
|
|
62
|
+
'01310100',
|
|
63
|
+
false,
|
|
64
|
+
null,
|
|
65
|
+
locationKey,
|
|
66
|
+
null,
|
|
67
|
+
true
|
|
68
|
+
)
|
|
69
|
+
).toBe(true)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('does not fetch an empty stored list before the shopper updates the postal code', () => {
|
|
73
|
+
expect(
|
|
74
|
+
shouldRefreshPickupPoints('01310100', false, null, locationKey)
|
|
75
|
+
).toBe(false)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('retries a failed location when the update flag is set again', () => {
|
|
79
|
+
expect(
|
|
80
|
+
shouldRefreshPickupPoints(
|
|
81
|
+
'01310100',
|
|
82
|
+
true,
|
|
83
|
+
locationKey,
|
|
84
|
+
locationKey,
|
|
85
|
+
locationKey
|
|
86
|
+
)
|
|
87
|
+
).toBe(true)
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
describe('isCurrentPickupRequest', () => {
|
|
92
|
+
it('keeps the response that matches the latest request', () => {
|
|
93
|
+
expect(isCurrentPickupRequest(2, 2)).toBe(true)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('drops a response after a newer request has started', () => {
|
|
97
|
+
expect(isCurrentPickupRequest(1, 2)).toBe(false)
|
|
98
|
+
})
|
|
99
|
+
})
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
|
|
3
|
+
const { config, request } = vi.hoisted(() => ({
|
|
4
|
+
config: { enabled: true },
|
|
5
|
+
request: vi.fn(),
|
|
6
|
+
}))
|
|
7
|
+
|
|
8
|
+
vi.mock('discovery.config', () => ({
|
|
9
|
+
get deliveryPromise() {
|
|
10
|
+
return { enabled: config.enabled }
|
|
11
|
+
},
|
|
12
|
+
}))
|
|
13
|
+
|
|
14
|
+
vi.mock('src/sdk/graphql/request', () => ({
|
|
15
|
+
request,
|
|
16
|
+
}))
|
|
17
|
+
|
|
18
|
+
import { getPickupPoints } from '../../../src/sdk/deliveryPromise/queries'
|
|
19
|
+
|
|
20
|
+
describe('getPickupPoints', () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
config.enabled = true
|
|
23
|
+
request.mockReset()
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('skips the request when delivery promise is disabled', async () => {
|
|
27
|
+
config.enabled = false
|
|
28
|
+
|
|
29
|
+
await expect(
|
|
30
|
+
getPickupPoints({
|
|
31
|
+
geoCoordinates: { latitude: -23.56, longitude: -46.65 },
|
|
32
|
+
postalCode: '01310100',
|
|
33
|
+
country: 'BRA',
|
|
34
|
+
})
|
|
35
|
+
).resolves.toEqual([])
|
|
36
|
+
expect(request).not.toHaveBeenCalled()
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('skips the request when postal code and coordinates are both missing', async () => {
|
|
40
|
+
await expect(
|
|
41
|
+
getPickupPoints({
|
|
42
|
+
geoCoordinates: null,
|
|
43
|
+
postalCode: null,
|
|
44
|
+
country: 'BRA',
|
|
45
|
+
})
|
|
46
|
+
).resolves.toEqual([])
|
|
47
|
+
expect(request).not.toHaveBeenCalled()
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('requests pickup points with postal code and country and no coordinates', async () => {
|
|
51
|
+
request.mockResolvedValue({
|
|
52
|
+
pickupPoints: {
|
|
53
|
+
pickupPointDistances: [
|
|
54
|
+
{
|
|
55
|
+
pickupId: 'vendemo_1',
|
|
56
|
+
pickupName: 'VTEX Rio',
|
|
57
|
+
isActive: true,
|
|
58
|
+
distance: 2.7,
|
|
59
|
+
address: {
|
|
60
|
+
street: 'Praia de Botafogo',
|
|
61
|
+
number: '300',
|
|
62
|
+
postalCode: '22250-040',
|
|
63
|
+
city: 'Rio de Janeiro',
|
|
64
|
+
state: 'RJ',
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
pickupId: 'inactive',
|
|
69
|
+
pickupName: 'Closed',
|
|
70
|
+
isActive: false,
|
|
71
|
+
distance: 1,
|
|
72
|
+
address: null,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
const channel = JSON.stringify({ salesChannel: '2', regionId: '' })
|
|
79
|
+
const points = await getPickupPoints({
|
|
80
|
+
geoCoordinates: null,
|
|
81
|
+
postalCode: '22041080',
|
|
82
|
+
country: 'BRA',
|
|
83
|
+
channel,
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
expect(request).toHaveBeenCalledWith(
|
|
87
|
+
expect.anything(),
|
|
88
|
+
expect.objectContaining({
|
|
89
|
+
postalCode: '22041080',
|
|
90
|
+
country: 'BRA',
|
|
91
|
+
channel,
|
|
92
|
+
geoCoordinates: undefined,
|
|
93
|
+
})
|
|
94
|
+
)
|
|
95
|
+
expect(points).toEqual([
|
|
96
|
+
{
|
|
97
|
+
id: 'vendemo_1',
|
|
98
|
+
name: 'VTEX Rio',
|
|
99
|
+
totalItems: 2,
|
|
100
|
+
address: {
|
|
101
|
+
street: 'Praia de Botafogo',
|
|
102
|
+
number: '300',
|
|
103
|
+
postalCode: '22250-040',
|
|
104
|
+
city: 'Rio de Janeiro',
|
|
105
|
+
state: 'RJ',
|
|
106
|
+
},
|
|
107
|
+
distance: 2.7,
|
|
108
|
+
},
|
|
109
|
+
])
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('returns an empty list when the request has no data', async () => {
|
|
113
|
+
request.mockResolvedValueOnce(null)
|
|
114
|
+
|
|
115
|
+
await expect(
|
|
116
|
+
getPickupPoints({
|
|
117
|
+
geoCoordinates: null,
|
|
118
|
+
postalCode: '01310100',
|
|
119
|
+
country: 'BRA',
|
|
120
|
+
})
|
|
121
|
+
).resolves.toEqual([])
|
|
122
|
+
})
|
|
123
|
+
})
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
deliveryPromiseReducer,
|
|
5
|
+
initializeDeliveryPromiseState,
|
|
6
|
+
} from '../../../src/sdk/deliveryPromise/reducer'
|
|
7
|
+
|
|
8
|
+
describe('deliveryPromiseReducer onPostalCodeChange', () => {
|
|
9
|
+
it('stores the simulated postal code and country', () => {
|
|
10
|
+
const next = deliveryPromiseReducer(initializeDeliveryPromiseState(), {
|
|
11
|
+
type: 'onPostalCodeChange',
|
|
12
|
+
payload: {
|
|
13
|
+
simulatePickupPoints: true,
|
|
14
|
+
validatedSession: {
|
|
15
|
+
postalCode: '22041080',
|
|
16
|
+
country: 'BRA',
|
|
17
|
+
geoCoordinates: { latitude: -22.95, longitude: -43.19 },
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
expect(next.simulatePickupPoints).toBe(true)
|
|
23
|
+
expect(next.shouldUpdatePickupPoints).toBe(true)
|
|
24
|
+
expect(next.pickupPointsSimulation).toMatchObject({
|
|
25
|
+
postalCode: '22041080',
|
|
26
|
+
country: 'BRA',
|
|
27
|
+
geoCoordinates: { latitude: -22.95, longitude: -43.19 },
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('stores null when the validated session omits the location', () => {
|
|
32
|
+
const next = deliveryPromiseReducer(initializeDeliveryPromiseState(), {
|
|
33
|
+
type: 'onPostalCodeChange',
|
|
34
|
+
payload: {
|
|
35
|
+
simulatePickupPoints: true,
|
|
36
|
+
validatedSession: {},
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
expect(next.pickupPointsSimulation).toMatchObject({
|
|
41
|
+
postalCode: null,
|
|
42
|
+
country: null,
|
|
43
|
+
geoCoordinates: null,
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
})
|