@faststore/core 4.5.0-dev.3 → 4.5.0-dev.4
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 +78 -70
- package/@generated/gql.ts +6 -6
- package/@generated/graphql.ts +46 -7
- package/@generated/persisted-documents.json +2 -2
- package/@generated/schema.graphql +44 -5
- package/CHANGELOG.md +6 -0
- package/package.json +6 -6
- package/src/components/sections/EmptyState/EmptyState.tsx +4 -3
- package/src/components/templates/ProductListingPage/ProductListingPage.tsx +43 -37
- package/src/components/ui/Breadcrumb/Breadcrumb.tsx +5 -12
- package/src/components/ui/LocalizationButton/LocalizationButton.tsx +5 -1
- package/src/experimental/index.ts +2 -0
- package/src/pages/[...slug].tsx +5 -0
- package/src/pages/[slug]/p.tsx +63 -11
- package/src/sdk/localization/LocalizedProductContext.tsx +65 -0
- package/src/sdk/localization/useBindingSelector.ts +148 -5
- package/src/server/index.ts +10 -1
- package/test/sdk/localization/useBindingSelector.test.tsx +144 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
buildLanguageOptions,
|
|
@@ -6,7 +6,47 @@ import {
|
|
|
6
6
|
isValidUrl,
|
|
7
7
|
resolveBinding,
|
|
8
8
|
} from '../../../src/sdk/localization/bindingSelector'
|
|
9
|
+
import type { LocalizedProductLocale } from '../../../src/sdk/localization/LocalizedProductContext'
|
|
9
10
|
import type { Locale } from '../../../src/sdk/localization/types'
|
|
11
|
+
import {
|
|
12
|
+
getSkuIdFromPdpPath,
|
|
13
|
+
persistOtherLocales,
|
|
14
|
+
recoverOtherLocales,
|
|
15
|
+
} from '../../../src/sdk/localization/useBindingSelector'
|
|
16
|
+
|
|
17
|
+
/** In-memory Storage stub matching the subset used by the helpers. */
|
|
18
|
+
function createFakeStorage() {
|
|
19
|
+
const map = new Map<string, string>()
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
getItem: (key: string) => (map.has(key) ? (map.get(key) as string) : null),
|
|
23
|
+
setItem: (key: string, value: string) => {
|
|
24
|
+
map.set(key, value)
|
|
25
|
+
},
|
|
26
|
+
removeItem: (key: string) => {
|
|
27
|
+
map.delete(key)
|
|
28
|
+
},
|
|
29
|
+
clear: () => map.clear(),
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Stubs a browser-like global scope with the given pathname and storage.
|
|
35
|
+
*
|
|
36
|
+
* The production helpers guard on `typeof window` but read `globalThis.location`
|
|
37
|
+
* / `globalThis.sessionStorage` (which coincide with `window` in a real browser).
|
|
38
|
+
* Under the node test environment those are distinct, so we stub all three: the
|
|
39
|
+
* `window` keeps the guard passing while `location`/`sessionStorage` back the reads.
|
|
40
|
+
* Pass an existing `sessionStorage` to simulate navigation while storage persists.
|
|
41
|
+
*/
|
|
42
|
+
function stubWindow(pathname: string, sessionStorage = createFakeStorage()) {
|
|
43
|
+
const location = { pathname }
|
|
44
|
+
vi.stubGlobal('window', { location, sessionStorage })
|
|
45
|
+
vi.stubGlobal('location', location)
|
|
46
|
+
vi.stubGlobal('sessionStorage', sessionStorage)
|
|
47
|
+
|
|
48
|
+
return sessionStorage
|
|
49
|
+
}
|
|
10
50
|
|
|
11
51
|
// Test data that mirrors what the hook would receive from discovery.config
|
|
12
52
|
const mockLocales: Record<string, Locale> = {
|
|
@@ -216,4 +256,107 @@ describe('useBindingSelector integration scenarios', () => {
|
|
|
216
256
|
expect(newCurrencies[0]).toBe('EUR')
|
|
217
257
|
})
|
|
218
258
|
})
|
|
259
|
+
|
|
260
|
+
describe('getSkuIdFromPdpPath', () => {
|
|
261
|
+
afterEach(() => {
|
|
262
|
+
vi.unstubAllGlobals()
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('extracts the SKU id from a locale-prefixed PDP path', () => {
|
|
266
|
+
expect(
|
|
267
|
+
getSkuIdFromPdpPath(
|
|
268
|
+
'/en-CA/adidas-mens-performance-polo-blast-blue-65/p'
|
|
269
|
+
)
|
|
270
|
+
).toBe('65')
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
it('extracts the SKU id from a default-locale (root) PDP path', () => {
|
|
274
|
+
expect(
|
|
275
|
+
getSkuIdFromPdpPath('/adidas-mens-performance-polo-blast-blue-65/p')
|
|
276
|
+
).toBe('65')
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it('extracts the SKU id from a custom-path binding PDP', () => {
|
|
280
|
+
expect(getSkuIdFromPdpPath('/europe/it/some-product-slug-12/p')).toBe(
|
|
281
|
+
'12'
|
|
282
|
+
)
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
it('handles a trailing slash after /p', () => {
|
|
286
|
+
expect(getSkuIdFromPdpPath('/pt-BR/some-slug-9/p/')).toBe('9')
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
it('returns null for non-PDP paths', () => {
|
|
290
|
+
expect(getSkuIdFromPdpPath('/en-CA')).toBeNull()
|
|
291
|
+
expect(getSkuIdFromPdpPath('/en-CA/office')).toBeNull()
|
|
292
|
+
expect(getSkuIdFromPdpPath('/')).toBeNull()
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
it('returns null when the slug has no numeric id', () => {
|
|
296
|
+
expect(getSkuIdFromPdpPath('/en-CA/no-numeric-id/p')).toBeNull()
|
|
297
|
+
})
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
describe('persist / recover otherLocales (sessionStorage)', () => {
|
|
301
|
+
afterEach(() => {
|
|
302
|
+
vi.unstubAllGlobals()
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
const otherLocales: LocalizedProductLocale[] = [
|
|
306
|
+
{ locale: 'en-US', slug: 'adidas-mens-performance-polo-blast-blue-65' },
|
|
307
|
+
{
|
|
308
|
+
locale: 'pt-BR',
|
|
309
|
+
slug: 'adidas-polo-performance-masculina-azul-blast-65',
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
locale: 'fr-CA',
|
|
313
|
+
slug: 'adidas-polo-performance-homme-bleu-blast-ca-65',
|
|
314
|
+
},
|
|
315
|
+
]
|
|
316
|
+
|
|
317
|
+
it('round-trips the map for the SKU referenced by the current path', () => {
|
|
318
|
+
// Persisted while on a working PDP (SKU 65).
|
|
319
|
+
const storage = stubWindow(
|
|
320
|
+
'/adidas-mens-performance-polo-blast-blue-65/p'
|
|
321
|
+
)
|
|
322
|
+
persistOtherLocales(otherLocales)
|
|
323
|
+
|
|
324
|
+
// Later, on a 404 for en-CA (same SKU 65, default-locale slug in the URL).
|
|
325
|
+
stubWindow('/en-CA/adidas-mens-performance-polo-blast-blue-65/p', storage)
|
|
326
|
+
|
|
327
|
+
expect(recoverOtherLocales()).toEqual(otherLocales)
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
it('returns null when the current path references a different SKU', () => {
|
|
331
|
+
const storage = stubWindow(
|
|
332
|
+
'/adidas-mens-performance-polo-blast-blue-65/p'
|
|
333
|
+
)
|
|
334
|
+
persistOtherLocales(otherLocales)
|
|
335
|
+
|
|
336
|
+
stubWindow('/en-CA/some-other-product-99/p', storage)
|
|
337
|
+
|
|
338
|
+
expect(recoverOtherLocales()).toBeNull()
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
it('returns null when the current path is not a PDP', () => {
|
|
342
|
+
const storage = stubWindow(
|
|
343
|
+
'/adidas-mens-performance-polo-blast-blue-65/p'
|
|
344
|
+
)
|
|
345
|
+
persistOtherLocales(otherLocales)
|
|
346
|
+
|
|
347
|
+
stubWindow('/en-CA', storage)
|
|
348
|
+
|
|
349
|
+
expect(recoverOtherLocales()).toBeNull()
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
it('does not persist when otherLocales is empty', () => {
|
|
353
|
+
const storage = stubWindow(
|
|
354
|
+
'/adidas-mens-performance-polo-blast-blue-65/p'
|
|
355
|
+
)
|
|
356
|
+
persistOtherLocales([])
|
|
357
|
+
|
|
358
|
+
expect(recoverOtherLocales()).toBeNull()
|
|
359
|
+
expect(storage.getItem('fs:otherLocales:65')).toBeNull()
|
|
360
|
+
})
|
|
361
|
+
})
|
|
219
362
|
})
|