@faststore/core 4.7.0-dev.5 → 4.7.0-dev.6

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.
@@ -1,9 +1,9 @@
1
1
 
2
- > @faststore/core@4.7.0-dev.4 generate /home/runner/work/faststore/faststore/packages/core
2
+ > @faststore/core@4.7.0-dev.5 generate /home/runner/work/faststore/faststore/packages/core
3
3
  > pnpm run gen-types && pnpm run cache-graphql
4
4
 
5
5
 
6
- > @faststore/core@4.7.0-dev.4 gen-types /home/runner/work/faststore/faststore/packages/core
6
+ > @faststore/core@4.7.0-dev.5 gen-types /home/runner/work/faststore/faststore/packages/core
7
7
  > node ../cli/bin/run generate-types .
8
8
 
9
9
  [STARTED] Parse Configuration
@@ -19,7 +19,7 @@
19
19
  [COMPLETED] Generate to /home/runner/work/faststore/faststore/packages/core/@generated/
20
20
  [COMPLETED] Generate outputs
21
21
 
22
- > @faststore/core@4.7.0-dev.4 cache-graphql /home/runner/work/faststore/faststore/packages/core
22
+ > @faststore/core@4.7.0-dev.5 cache-graphql /home/runner/work/faststore/faststore/packages/core
23
23
  > node ../cli/bin/run cache-graphql --config=./discovery.config.default.js --queries=./@generated/persisted-documents.json
24
24
 
25
25
  [Info] - Config file location: /home/runner/work/faststore/faststore/packages/core/discovery.config.default.js
@@ -1,5 +1,5 @@
1
1
 
2
- > @faststore/core@4.7.0-dev.4 test /home/runner/work/faststore/faststore/packages/core
2
+ > @faststore/core@4.7.0-dev.5 test /home/runner/work/faststore/faststore/packages/core
3
3
  > vitest run
4
4
 
5
5
 
@@ -7,20 +7,19 @@
7
7
 
8
8
  Not implemented: navigation to another Document
9
9
  Not implemented: navigation to another Document
10
- ✓  browser  test/sdk/ui/useScrollRestoration.browser.test.ts (43 tests) 1325ms
10
+ ✓  browser  test/sdk/ui/useScrollRestoration.browser.test.ts (43 tests) 831ms
11
11
  Browserslist: browsers data (caniuse-lite) is 6 months old. Please run:
12
12
  npx update-browserslist-db@latest
13
13
  Why you should do it regularly: https://github.com/browserslist/update-db#readme
14
- ✓  browser  test/components/ui/ProductGallery/GalleryPageHeightLock.browser.test.tsx (8 tests) 260ms
15
- ✓  browser  test/components/account/ContractSwitcher.browser.test.tsx (13 tests) 2965ms
16
- ✓ shows an error state when the list fails to load  863ms
17
- ✓ requires selecting a contract before Confirm is enabled, then switches (REQ-03)  427ms
18
- ✓ keeps Confirm disabled when switching is unavailable  372ms
19
- ✓  browser  test/components/account/cards/MyAccountListCards.browser.test.tsx (14 tests) 2735ms
20
- ✓ shows the tab bar and renders Personal cards by default  1165ms
21
- ✓ paginates when there are more than 25 cards  380ms
22
- ✓  browser  test/sdk/session/reconcileSessionLocale.browser.test.ts (5 tests) 30ms
23
- ✓  browser  test/sdk/recommendations/useRecommendationShelf.browser.test.tsx (12 tests) 218ms
14
+ ✓  browser  test/components/account/ContractSwitcher.browser.test.tsx (13 tests) 3134ms
15
+ ✓ shows an error state when the list fails to load  839ms
16
+ ✓ requires selecting a contract before Confirm is enabled, then switches (REQ-03)  541ms
17
+ ✓  browser  test/components/account/cards/MyAccountListCards.browser.test.tsx (14 tests) 2789ms
18
+ ✓ shows the tab bar and renders Personal cards by default  1093ms
19
+ ✓ paginates when there are more than 25 cards  498ms
20
+ ✓  browser  test/components/ui/ProductGallery/GalleryPageHeightLock.browser.test.tsx (8 tests) 200ms
21
+ ✓  browser  test/sdk/session/reconcileSessionLocale.browser.test.ts (5 tests) 41ms
22
+ ✓  browser  test/sdk/recommendations/useRecommendationShelf.browser.test.tsx (12 tests) 166ms
24
23
  stderr | test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx
25
24
  Error in optimistic validation: TypeError: Failed to parse URL from /api/graphql?operationName=ValidateCartMutation&operationHash=3c71b0d3fc2bade14df68abfa271858beb361b12
26
25
   at node:internal/deps/undici/undici:16416:13
@@ -50,7 +49,8 @@ Browserslist: browsers data (caniuse-lite) is 6 months old. Please run:
50
49
  stderr | test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx > RecommendationShelf > renders nothing when there is an error
51
50
  Error fetching recommendations undefined boom for campaign vrn:recommendations:acc:rec-top-items-v2:campaign-1
52
51
 
53
- ✓  browser  test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx (7 tests) 292ms
52
+ ✓  browser  test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx (7 tests) 258ms
53
+ ✓  browser  test/sdk/session/getInitialSession.browser.test.ts (6 tests) 40ms
54
54
  stderr | test/pages/password-protection.browser.test.tsx > PasswordProtectionLogin > posts the password and redirects to the requested return path
55
55
  Warning: An update to PasswordProtectionLogin inside a test was not wrapped in act(...).
56
56
 
@@ -184,7 +184,6 @@ act(() => {
184
184
  This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
185
185
  at PasswordProtectionLogin (/home/runner/work/faststore/faststore/packages/core/src/pages/password-protection/index.tsx:19:69)
186
186
 
187
- ✓  browser  test/sdk/session/getInitialSession.browser.test.ts (6 tests) 31ms
188
187
  stderr | test/pages/password-protection.browser.test.tsx > PasswordProtectionLogin > shows service unavailable when the response is not a login payload
189
188
  Warning: An update to PasswordProtectionLogin inside a test was not wrapped in act(...).
190
189
 
@@ -233,6 +232,9 @@ act(() => {
233
232
  This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
234
233
  at PasswordProtectionLogin (/home/runner/work/faststore/faststore/packages/core/src/pages/password-protection/index.tsx:19:69)
235
234
 
235
+ ✓  browser  test/components/account/OrganizationDrawer.browser.test.tsx (5 tests) 1967ms
236
+ ✓ renders the menu view with contract details and manage footer  987ms
237
+ ✓ opens the contract switcher and returns to the menu on back  418ms
236
238
  stderr | test/pages/password-protection.browser.test.tsx > PasswordProtectionLogin > shows service unavailable when the login request fails
237
239
  Warning: An update to PasswordProtectionLogin inside a test was not wrapped in act(...).
238
240
 
@@ -281,12 +283,10 @@ act(() => {
281
283
  This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
282
284
  at PasswordProtectionLogin (/home/runner/work/faststore/faststore/packages/core/src/pages/password-protection/index.tsx:19:69)
283
285
 
284
- ✓  browser  test/pages/password-protection.browser.test.tsx (6 tests) 1454ms
285
- ✓ renders the password protection form  616ms
286
- ✓  browser  test/components/account/OrganizationDrawer.browser.test.tsx (5 tests) 1396ms
287
- ✓ renders the menu view with contract details and manage footer  889ms
286
+ ✓  browser  test/pages/password-protection.browser.test.tsx (6 tests) 1685ms
287
+ ✓ renders the password protection form  940ms
288
288
  ✓  browser  test/sdk/account/changeContractToken.browser.test.ts (9 tests) 24ms
289
- stderr | test/components/cms/RenderSections.browser.test.tsx
289
+ stderr | test/sdk/localization/store-url.browser.test.ts
290
290
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
291
291
  at waitForSessionValidated (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/waitForSessionValidated.ts:16:32)
292
292
  at validateCart (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/index.ts:120:9)
@@ -294,7 +294,8 @@ This ensures that you're testing the behavior the user would see in the browser.
294
294
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:23)
295
295
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
296
296
 
297
- stderr | test/sdk/localization/store-url.browser.test.ts
297
+ ✓  browser  test/sdk/localization/store-url.browser.test.ts (3 tests) 6ms
298
+ stderr | test/components/cms/RenderSections.browser.test.tsx
298
299
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
299
300
  at waitForSessionValidated (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/waitForSessionValidated.ts:16:32)
300
301
  at validateCart (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/index.ts:120:9)
@@ -302,12 +303,11 @@ This ensures that you're testing the behavior the user would see in the browser.
302
303
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:23)
303
304
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
304
305
 
305
- ✓  browser  test/sdk/localization/store-url.browser.test.ts (3 tests) 6ms
306
- ✓  browser  test/components/cms/RenderSections.browser.test.tsx (5 tests) 81ms
307
- ✓  browser  test/sdk/account/useAvailableContracts.browser.test.ts (6 tests) 34ms
308
- ✓  browser  test/components/templates/SearchPage/SearchWrapper.browser.test.tsx (3 tests) 35ms
309
- ✓  browser  test/sdk/account/useSwitchContract.browser.test.ts (4 tests) 42ms
310
- ✓  browser  test/sdk/recommendations/useRecommendationShelf.sessionFallback.browser.test.tsx (2 tests) 137ms
306
+ ✓  browser  test/sdk/account/useAvailableContracts.browser.test.ts (6 tests) 40ms
307
+ ✓  browser  test/components/cms/RenderSections.browser.test.tsx (5 tests) 97ms
308
+ ✓  browser  test/components/templates/SearchPage/SearchWrapper.browser.test.tsx (3 tests) 44ms
309
+ ✓  browser  test/sdk/recommendations/useRecommendationShelf.sessionFallback.browser.test.tsx (2 tests) 134ms
310
+ ✓  browser  test/sdk/account/useSwitchContract.browser.test.ts (4 tests) 47ms
311
311
  stderr | test/components/account/doLogout.browser.test.ts
312
312
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
313
313
  at waitForSessionValidated (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/waitForSessionValidated.ts:16:32)
@@ -316,9 +316,10 @@ This ensures that you're testing the behavior the user would see in the browser.
316
316
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:23)
317
317
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
318
318
 
319
- ✓  browser  test/components/account/doLogout.browser.test.ts (1 test) 20ms
320
- ✓  browser  test/components/sections/Account/AccountListCards.browser.test.tsx (3 tests) 86ms
321
- ✓  browser  test/components/account/OrganizationDrawerHeader.browser.test.tsx (3 tests) 317ms
319
+ ✓  browser  test/components/account/doLogout.browser.test.ts (1 test) 24ms
320
+ ✓  browser  test/components/sections/Account/AccountListCards.browser.test.tsx (3 tests) 91ms
321
+ ✓  browser  test/components/account/OrganizationDrawerHeader.browser.test.tsx (3 tests) 286ms
322
+ ✓  browser  test/components/account/OrganizationDrawerFooter.browser.test.tsx (3 tests) 338ms
322
323
  stderr | test/sdk/account/clearPersistedSessionState.browser.test.ts
323
324
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
324
325
  at waitForSessionValidated (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/waitForSessionValidated.ts:16:32)
@@ -327,10 +328,11 @@ This ensures that you're testing the behavior the user would see in the browser.
327
328
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:23)
328
329
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
329
330
 
330
- ✓  browser  test/sdk/account/clearPersistedSessionState.browser.test.ts (1 test) 13ms
331
- ✓  browser  test/utils/isLocalHost.browser.test.ts (3 tests) 6ms
332
- ✓  browser  test/components/account/OrganizationDrawerFooter.browser.test.tsx (3 tests) 322ms
331
+ ✓  browser  test/sdk/account/clearPersistedSessionState.browser.test.ts (1 test) 8ms
332
+ ✓  browser  test/utils/isLocalHost.browser.test.ts (3 tests) 4ms
333
333
  ✓  browser  test/sdk/session/reconcileSessionLocale.disabled.browser.test.ts (1 test) 4ms
334
+ ✓  node  test/utils/localization/bindingPaths.test.ts (71 tests) 20ms
335
+ ✓  node  test/server/password-protection-service.test.ts (22 tests) 42ms
334
336
  stderr | test/sdk/account/refreshToken.browser.test.ts
335
337
  Error in optimistic validation: TypeError: Cannot read properties of undefined (reading 'read')
336
338
  at waitForSessionValidated (/home/runner/work/faststore/faststore/packages/core/src/sdk/cart/waitForSessionValidated.ts:16:32)
@@ -339,13 +341,11 @@ This ensures that you're testing the behavior the user would see in the browser.
339
341
  at a (/home/runner/work/faststore/faststore/packages/sdk/dist/es/index.mjs:354:23)
340
342
   at processTicksAndRejections (node:internal/process/task_queues:103:5)
341
343
 
342
- ✓  browser  test/sdk/account/refreshToken.browser.test.ts (1 test) 5ms
343
- ✓  node  test/utils/localization/bindingPaths.test.ts (71 tests) 21ms
344
- ✓  browser  test/components/skeletons/ProductGridSkeleton.browser.test.tsx (2 tests) 40ms
345
- ✓  node  test/utils/match-url.test.ts (10 tests) 12ms
346
- ✓  node  test/server/password-protection-service.test.ts (22 tests) 38ms
347
- ✓  node  test/pages/api/unlock.test.ts (15 tests) 260ms
348
- ✓  node  test/sdk/localization/useBindingSelector.test.tsx (22 tests) 21ms
344
+ ✓  browser  test/components/skeletons/ProductGridSkeleton.browser.test.tsx (2 tests) 39ms
345
+ ✓  browser  test/sdk/account/refreshToken.browser.test.ts (1 test) 8ms
346
+ ✓  node  test/utils/match-url.test.ts (10 tests) 24ms
347
+ ✓  node  test/sdk/localization/useBindingSelector.test.tsx (22 tests) 17ms
348
+ ✓  node  test/pages/api/unlock.test.ts (15 tests) 169ms
349
349
  ✓  node  test/sdk/localization/bindingSelector.test.ts (18 tests) 10ms
350
350
  stderr | test/components/search/SearchInputComponent.test.tsx
351
351
  Warning: forwardRef render functions accept exactly two parameters: props and ref. Did you forget to use the ref parameter?
@@ -410,19 +410,19 @@ Warning: React does not recognize the `providerProps` prop on a DOM element. If
410
410
  at Suspense
411
411
  at Wrapper (/home/runner/work/faststore/faststore/packages/core/test/components/search/SearchInputComponent.test.tsx:106:20)
412
412
 
413
- ✓  node  test/components/search/SearchInputComponent.test.tsx (6 tests) 73ms
414
- ✓  node  test/sdk/search/useSearchHistory.test.ts (13 tests) 53ms
415
- ✓  node  test/utils/cookieCacheBusting.test.ts (10 tests) 17ms
413
+ ✓  node  test/components/search/SearchInputComponent.test.tsx (6 tests) 83ms
414
+ ✓  node  test/sdk/search/useSearchHistory.test.ts (13 tests) 52ms
415
+ ✓  node  test/utils/cookieCacheBusting.test.ts (10 tests) 19ms
416
416
  stderr | test/pages/pvt/account/cards/index.test.ts > Cards page getServerSideProps > redirects to /pvt/account/403 when the query fails with 401/403
417
417
  { extensions: { status: 401 } }
418
418
 
419
419
  stderr | test/pages/pvt/account/cards/index.test.ts > Cards page getServerSideProps > renders the error state (no redirect) when the query fails with a non-auth status
420
420
  { extensions: { status: 502 } }
421
421
 
422
- ✓  node  test/pages/pvt/account/cards/index.test.ts (8 tests) 14ms
422
+ ✓  node  test/pages/pvt/account/cards/index.test.ts (8 tests) 12ms
423
423
  ✓  node  test/utils/clearCookies.test.ts (20 tests) 28ms
424
424
  ✓  node  test/experimental/myAccountCmsServerSideProps.test.ts (5 tests) 11ms
425
- ✓  node  test/components/quotes/MyAccountListQuotesTable.test.tsx (12 tests) 441ms
425
+ ✓  node  test/components/quotes/MyAccountListQuotesTable.test.tsx (12 tests) 445ms
426
426
  stderr | test/pages/api/graphql.test.ts > /api/graphql error status propagation > propagates the upstream 400 from a wrapped BadRequestError instead of 500
427
427
  Graphql execution returned with error: [
428
428
  {
@@ -515,64 +515,67 @@ Warning: React does not recognize the `providerProps` prop on a DOM element. If
515
515
  at file:///home/runner/work/faststore/faststore/node_modules/.pnpm/@vitest+runner@4.0.18/node_modules/@vitest/runner/dist/index.js:1653:37
516
516
  at Traces.$ (file:///home/runner/work/faststore/faststore/node_modules/.pnpm/vitest@4.0.18_@opentelemetry+api@1.9.0_@types+node@20.19.13_jiti@2.6.1_jsdom@27.4.0_sas_13be941c57d55f08f06eeaf1c180ddfe/node_modules/vitest/dist/chunks/traces.CCmnQaNT.js:142:27)
517
517
 
518
- ✓  node  test/pages/api/graphql.test.ts (17 tests) 24ms
519
- ✓  node  test/components/quotes/MyAccountListQuotes.test.tsx (9 tests) 851ms
520
- ✓ navigates with the search term on blur  362ms
521
- ✓ navigates without a label param when the search is cleared  361ms
518
+ ✓  node  test/pages/api/graphql.test.ts (17 tests) 29ms
519
+ ✓  node  test/components/quotes/MyAccountListQuotes.test.tsx (9 tests) 848ms
520
+ ✓ navigates with the search term on blur  365ms
521
+ ✓ navigates without a label param when the search is cleared  362ms
522
522
  stderr | test/sdk/account/useSetPassword.test.ts > useSetPassword > maps wrongcredentials error responses to the existing message
523
523
  Set password request failed: Bad Request
524
524
 
525
- ✓  node  test/sdk/account/useSetPassword.test.ts (6 tests) 46ms
526
- ✓  node  test/utils/multipleTemplates.test.ts (8 tests) 8ms
527
- ✓  node  test/sdk/orderEntry/useOrderEntryOperation.test.ts (8 tests) 37ms
528
- ✓  node  test/components/quotes/quoteFilters.test.ts (23 tests) 39ms
529
- ✓  node  test/utils/localization/localizeRedirectDestination.test.ts (11 tests) 7ms
530
- ✓  node  test/sdk/account/getMyAccountRoutes.test.ts (12 tests) 9ms
531
- ✓  node  test/sdk/orderEntry/useOrderEntryUpload.test.ts (8 tests) 65ms
532
- ✓  node  test/components/quotes/MyAccountQuotesFilterSlider.test.tsx (5 tests) 169ms
533
- ✓  node  test/proxy.localization.test.ts (5 tests) 53ms
534
- ✓  node  test/sdk/orderEntry/useOrderEntry.test.ts (8 tests) 44ms
535
- ✓  node  test/server/index.test.ts (7 tests) 945ms
536
- ✓ should handle options and execute  383ms
537
- ✓  node  test/sdk/session/syncSalesChannelFromOrderForm.test.ts (6 tests) 7ms
538
- ✓  node  test/components/quotes/MyAccountQuotesSelectedTags.test.tsx (8 tests) 113ms
539
- ✓  node  test/sdk/analytics/useStartRecommendationSession.test.ts (6 tests) 48ms
540
- ✓  node  test/proxy.test.ts (4 tests) 18ms
541
- ✓  node  test/server/webops-api.test.ts (9 tests) 24ms
525
+ ✓  node  test/sdk/account/useSetPassword.test.ts (6 tests) 59ms
526
+ ✓  node  test/components/quotes/quoteFilters.test.ts (23 tests) 40ms
527
+ ✓  node  test/sdk/orderEntry/useOrderEntryOperation.test.ts (8 tests) 44ms
528
+ ✓  node  test/utils/multipleTemplates.test.ts (8 tests) 7ms
529
+ ✓  node  test/utils/localization/localizeRedirectDestination.test.ts (11 tests) 8ms
530
+ ✓  node  test/sdk/account/getMyAccountRoutes.test.ts (12 tests) 10ms
531
+ ✓  node  test/sdk/orderEntry/useOrderEntryUpload.test.ts (8 tests) 51ms
532
+ ✓  node  test/components/quotes/MyAccountQuotesFilterSlider.test.tsx (5 tests) 166ms
533
+ ✓  node  test/utils/productJsonLd.test.ts (17 tests) 10ms
534
+ ✓  node  test/sdk/orderEntry/useOrderEntry.test.ts (8 tests) 47ms
535
+ ✓  node  test/proxy.localization.test.ts (5 tests) 56ms
536
+ ✓  node  test/server/index.test.ts (7 tests) 964ms
537
+ ✓ should return a valid merged GraphQL schema  355ms
538
+ ✓ should handle options and execute  303ms
539
+ ✓  node  test/sdk/session/syncSalesChannelFromOrderForm.test.ts (6 tests) 8ms
540
+ ✓  node  test/sdk/analytics/useStartRecommendationSession.test.ts (6 tests) 54ms
541
+ ✓  node  test/components/quotes/MyAccountQuotesSelectedTags.test.tsx (8 tests) 122ms
542
+ ✓  node  test/proxy.test.ts (4 tests) 22ms
543
+ ✓  node  test/server/webops-api.test.ts (9 tests) 14ms
544
+ ✓  node  test/sdk/cart/waitForSessionValidated.test.ts (5 tests) 17ms
542
545
  ✓  node  test/sdk/orderEntry/useOrderFormItems.test.ts (7 tests) 39ms
543
- ✓  node  test/sdk/cart/waitForSessionValidated.test.ts (5 tests) 15ms
544
- ✓  node  test/server/cms/myAccountDefaultSections.test.ts (7 tests) 7ms
545
546
  ✓  node  test/server/content/service.test.ts (5 tests) 6ms
547
+ ✓  node  test/server/cms/myAccountDefaultSections.test.ts (7 tests) 6ms
548
+ ✓  node  test/utils/productJsonLdRendering.test.tsx (4 tests) 15ms
546
549
  ✓  node  test/sdk/account/refreshToken.test.ts (5 tests) 9ms
547
550
  ✓  node  test/server/cms/global.test.ts (3 tests) 6ms
548
- ✓  node  test/sdk/localization/store-url.test.ts (1 test) 4ms
551
+ ✓  node  test/sdk/localization/store-url.test.ts (1 test) 3ms
549
552
  ✓  node  test/sdk/auth/useAuth.test.ts (5 tests) 34ms
550
- ✓  node  test/components/quotes/MyAccountQuotesStatusSelector.test.tsx (6 tests) 147ms
551
- ✓  node  test/utils/getRequestHostname.test.ts (12 tests) 7ms
552
- ✓  node  test/pages/api/preview.test.ts (2 tests) 7ms
553
- ✓  node  test/server/rootSpan.test.ts (4 tests) 44ms
553
+ ✓  node  test/components/quotes/MyAccountQuotesStatusSelector.test.tsx (6 tests) 144ms
554
+ ✓  node  test/utils/getRequestHostname.test.ts (12 tests) 8ms
555
+ ✓  node  test/server/rootSpan.test.ts (4 tests) 72ms
556
+ ✓  node  test/pages/api/preview.test.ts (2 tests) 12ms
557
+ ✓  node  test/components/account/Layout.test.tsx (6 tests) 80ms
554
558
  ✓  node  test/utils/validateSessionRefreshToken.test.ts (6 tests) 5ms
555
- ✓  node  test/components/account/Layout.test.tsx (6 tests) 69ms
556
- ✓  node  test/components/search/SearchInput.test.ts (6 tests) 7ms
557
- ✓  node  test/sdk/recommendations/vrn.test.ts (5 tests) 5ms
558
- ✓  node  test/utils/retry.test.ts (5 tests) 14ms
559
+ ✓  node  test/components/search/SearchInput.test.ts (6 tests) 17ms
560
+ ✓  node  test/sdk/recommendations/vrn.test.ts (5 tests) 6ms
561
+ ✓  node  test/utils/retry.test.ts (5 tests) 15ms
559
562
  ✓  node  test/server/cms/index.test.ts (2 tests) 6ms
560
563
  ✓  node  test/sdk/account/getB2BSessionClaims.test.ts (4 tests) 6ms
561
- ✓  node  test/utils/unlockResponse.test.ts (11 tests) 6ms
562
- ✓  node  test/sdk/recommendations/useRecommendations.test.ts (2 tests) 6ms
563
- ✓  node  test/sdk/recommendations/useRecommendationUserId.test.ts (4 tests) 184ms
564
+ ✓  node  test/utils/unlockResponse.test.ts (11 tests) 5ms
565
+ ✓  node  test/sdk/recommendations/useRecommendations.test.ts (2 tests) 4ms
566
+ ✓  node  test/sdk/recommendations/useRecommendationUserId.test.ts (4 tests) 190ms
564
567
  ✓  node  test/utils/isLocalHost.test.ts (7 tests) 6ms
565
568
  ✓  node  test/components/account/OrganizationDrawerBody.test.tsx (3 tests) 57ms
566
- ✓  node  test/utils/userOrderStatus.test.ts (3 tests) 4ms
567
- ✓  node  test/utils/getCookie.test.ts (4 tests) 5ms
569
+ ✓  node  test/utils/userOrderStatus.test.ts (3 tests) 5ms
570
+ ✓  node  test/utils/getCookie.test.ts (4 tests) 6ms
568
571
  ✓  node  test/sdk/analytics/utils.test.ts (2 tests) 5ms
569
- ✓  node  test/components/auth/ProfileChallenge.test.tsx (3 tests) 30ms
570
- ✓  node  test/instrumentation.test.ts (3 tests) 6ms
571
- ✓  node  test/sdk/account/clearPersistedSessionState.test.ts (1 test) 8ms
572
- ✓  node  test/components/quotes/MyAccountQuoteStatusBadge.test.tsx (2 tests) 25ms
573
-
574
-  Test Files  88 passed (88)
575
-  Tests  702 passed (702)
576
-  Start at  21:33:38
577
-  Duration  45.95s (transform 12.76s, setup 0ms, import 41.15s, tests 16.16s, environment 54.47s)
572
+ ✓  node  test/instrumentation.test.ts (3 tests) 5ms
573
+ ✓  node  test/components/auth/ProfileChallenge.test.tsx (3 tests) 42ms
574
+ ✓  node  test/sdk/account/clearPersistedSessionState.test.ts (1 test) 13ms
575
+ ✓  node  test/components/quotes/MyAccountQuoteStatusBadge.test.tsx (2 tests) 32ms
576
+
577
+  Test Files  90 passed (90)
578
+  Tests  723 passed (723)
579
+  Start at  14:52:14
580
+  Duration  46.99s (transform 11.84s, setup 0ms, import 41.77s, tests 16.67s, environment 57.05s)
578
581
 
package/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [4.7.0-dev.6](https://github.com/vtex/faststore/compare/v4.7.0-dev.5...v4.7.0-dev.6) (2026-08-26)
7
+
8
+ ### Bug Fixes
9
+
10
+ - make PDP product JSON-LD Schema.org compliant ([#3465](https://github.com/vtex/faststore/issues/3465)) ([0c09f15](https://github.com/vtex/faststore/commit/0c09f15456d3490602970ce0b4dff6b3c76a4f9c)), closes [#1449246](https://github.com/vtex/faststore/issues/1449246)
11
+
6
12
  # [4.7.0-dev.5](https://github.com/vtex/faststore/compare/v4.7.0-dev.4...v4.7.0-dev.5) (2026-08-25)
7
13
 
8
14
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faststore/core",
3
- "version": "4.7.0-dev.5",
3
+ "version": "4.7.0-dev.6",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -71,11 +71,11 @@
71
71
  "style-loader": "^3.3.1",
72
72
  "swr": "^2.2.5",
73
73
  "use-sync-external-store": "^1.6.0",
74
- "@faststore/api": "4.7.0-dev.5",
75
- "@faststore/diagnostics": "4.7.0-dev.5",
76
- "@faststore/lighthouse": "4.7.0-dev.5",
77
- "@faststore/sdk": "4.7.0-dev.5",
78
- "@faststore/ui": "4.7.0-dev.5"
74
+ "@faststore/api": "4.7.0-dev.6",
75
+ "@faststore/lighthouse": "4.7.0-dev.6",
76
+ "@faststore/sdk": "4.7.0-dev.6",
77
+ "@faststore/ui": "4.7.0-dev.6",
78
+ "@faststore/diagnostics": "4.7.0-dev.6"
79
79
  },
80
80
  "devDependencies": {
81
81
  "@cypress/code-coverage": "^3.12.1",
@@ -28,6 +28,7 @@ import { useSession } from 'src/sdk/session'
28
28
  import { execute } from 'src/server'
29
29
  import { getComponentKey } from 'src/utils/cms'
30
30
  import { getChannelForLocale } from 'src/utils/localization/bindingPaths'
31
+ import { toProductJsonLdOffer } from 'src/utils/productJsonLd'
31
32
 
32
33
  import storeConfig from 'discovery.config'
33
34
  import {
@@ -328,11 +329,13 @@ function Page({
328
329
  description={description}
329
330
  brand={product.brand.name}
330
331
  sku={product.sku}
331
- gtin={product.gtin}
332
- mpn={product.mpn}
333
- releaseDate={product.releaseDate}
332
+ // Spread conditionally so an unregistered identifier is omitted rather
333
+ // than published as "": next-seo passes these through untouched.
334
+ {...(product.gtin && { gtin: product.gtin })}
335
+ {...(product.mpn && { mpn: product.mpn })}
336
+ {...(product.releaseDate && { releaseDate: product.releaseDate })}
334
337
  images={product.image.map((img) => img.url)} // Somehow, Google does not understand this valid Schema.org schema, so we need to do conversions
335
- offers={offers}
338
+ {...(offers && { offers })}
336
339
  {...(itemListElements.length !== 0 && {
337
340
  category: itemListElements[0].name,
338
341
  })}
@@ -508,19 +511,10 @@ export const getStaticProps: GetStaticProps<
508
511
 
509
512
  const meta = { title, description, canonical }
510
513
 
511
- let offer = {}
512
-
513
- if (data.product.offers.offers.length > 0) {
514
- const { listPrice, ...offerData } = data.product.offers.offers[0]
515
-
516
- offer = offerData
517
- }
518
-
519
- const offers = {
520
- ...offer,
514
+ const offers = toProductJsonLdOffer(data.product.offers.offers[0], {
521
515
  priceCurrency: data.product.offers.priceCurrency,
522
516
  url: canonical,
523
- }
517
+ })
524
518
 
525
519
  const globalSectionsResult = injectGlobalSections({
526
520
  globalSections,
@@ -0,0 +1,71 @@
1
+ import type { ServerProductQueryQuery } from '@generated/graphql'
2
+
3
+ type ServerOffers = ServerProductQueryQuery['product']['offers']
4
+ type ServerOffer = ServerOffers['offers'][number]
5
+
6
+ /**
7
+ * Schema.org properties the PDP JSON-LD is allowed to publish on an Offer.
8
+ *
9
+ * This is an allowlist on purpose. The PDP's server query and
10
+ * `ProductDetailsFragment_product` both select `offers.offers`, and GraphQL
11
+ * merges the two selection sets — so the offer object reaching this module
12
+ * carries UI-only fields (`priceWithTaxes`, `listPrice`, `listPriceWithTaxes`,
13
+ * `quantity`, `priceToken`) that have no meaning to Schema.org consumers.
14
+ * Excluding them by name would mean tracking a fragment this module does not
15
+ * own, which is exactly how they leaked into public markup in the first place.
16
+ */
17
+ const SCHEMA_ORG_OFFER_FIELDS = [
18
+ 'availability',
19
+ 'itemCondition',
20
+ 'price',
21
+ 'priceValidUntil',
22
+ ] as const satisfies ReadonlyArray<keyof ServerOffer>
23
+
24
+ // Emptiness, not falsiness: `price` is numeric and `0` is a real price, so a
25
+ // falsy check would strip it and leave an Offer with no price at all.
26
+ const isEmpty = (value: unknown) =>
27
+ value === null || value === undefined || value === ''
28
+
29
+ type ObjectLevelFields = {
30
+ priceCurrency: ServerOffers['priceCurrency']
31
+ url: string
32
+ }
33
+
34
+ /**
35
+ * Builds the `offers` object for the PDP's `ProductJsonLd`, or `undefined` when
36
+ * the product has no offer to describe.
37
+ *
38
+ * `undefined` matters: an `Offer` carrying only a currency and a URL is invalid
39
+ * Schema.org, so omitting the property entirely beats emitting a priceless one.
40
+ * Every key is dropped when its value is empty — `priceValidUntil` and
41
+ * `availability` are both nullable upstream.
42
+ */
43
+ export const toProductJsonLdOffer = (
44
+ serverOffer: ServerOffer | undefined,
45
+ { priceCurrency, url }: ObjectLevelFields
46
+ ) => {
47
+ if (!serverOffer) {
48
+ return undefined
49
+ }
50
+
51
+ const offer: Record<string, unknown> = { priceCurrency, url }
52
+
53
+ for (const field of SCHEMA_ORG_OFFER_FIELDS) {
54
+ offer[field] = serverOffer[field]
55
+ }
56
+
57
+ for (const [key, value] of Object.entries(offer)) {
58
+ if (isEmpty(value)) {
59
+ delete offer[key]
60
+ }
61
+ }
62
+
63
+ // `price` resolves to null outside the search and order-form roots. Dropping
64
+ // the key alone would leave a priceless Offer — the same invalid markup the
65
+ // no-offer case above avoids, so it gets the same treatment.
66
+ if (isEmpty(offer.price)) {
67
+ return undefined
68
+ }
69
+
70
+ return offer
71
+ }
@@ -0,0 +1,126 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { toProductJsonLdOffer } from '../../src/utils/productJsonLd'
3
+
4
+ const OBJECT_LEVEL = { priceCurrency: 'USD', url: 'https://store.example/p' }
5
+
6
+ // Mirrors what actually reaches getStaticProps: the PDP query's Schema.org
7
+ // fields merged with the UI-only fields ProductDetailsFragment_product selects
8
+ // on the same `offers.offers` field.
9
+ const makeServerOffer = (overrides: Record<string, unknown> = {}) =>
10
+ ({
11
+ availability: 'https://schema.org/InStock',
12
+ itemCondition: 'https://schema.org/NewCondition',
13
+ price: 12.5,
14
+ priceValidUntil: '2027-08-24T19:48:25Z',
15
+ // UI-only fields that must never reach the markup
16
+ listPrice: 20,
17
+ listPriceWithTaxes: 22,
18
+ priceWithTaxes: 13.75,
19
+ quantity: 10000,
20
+ priceToken: 'a-short-lived-pricing-token',
21
+ seller: { identifier: '1' },
22
+ ...overrides,
23
+ }) as any
24
+
25
+ describe('toProductJsonLdOffer', () => {
26
+ it('emits exactly the allowed Schema.org keys', () => {
27
+ const offer = toProductJsonLdOffer(makeServerOffer(), OBJECT_LEVEL)
28
+
29
+ expect(Object.keys(offer!).sort()).toEqual([
30
+ 'availability',
31
+ 'itemCondition',
32
+ 'price',
33
+ 'priceCurrency',
34
+ 'priceValidUntil',
35
+ 'url',
36
+ ])
37
+ })
38
+
39
+ it.each([
40
+ 'listPrice',
41
+ 'listPriceWithTaxes',
42
+ 'priceWithTaxes',
43
+ 'quantity',
44
+ 'priceToken',
45
+ 'seller',
46
+ ])('does not leak %s into the markup', (field) => {
47
+ const offer = toProductJsonLdOffer(makeServerOffer(), OBJECT_LEVEL)
48
+
49
+ expect(offer).not.toHaveProperty(field)
50
+ })
51
+
52
+ // The regression guarantee: the mapper picks by allowlist, so a field added
53
+ // to any fragment selecting offers.offers cannot reach public markup.
54
+ it('ignores a field a future fragment might add', () => {
55
+ const offer = toProductJsonLdOffer(
56
+ makeServerOffer({ someFutureInternalField: 'leaked' }),
57
+ OBJECT_LEVEL
58
+ )
59
+
60
+ expect(offer).not.toHaveProperty('someFutureInternalField')
61
+ })
62
+
63
+ it('carries the allowed values through unchanged', () => {
64
+ const offer = toProductJsonLdOffer(makeServerOffer(), OBJECT_LEVEL)
65
+
66
+ expect(offer).toEqual({
67
+ availability: 'https://schema.org/InStock',
68
+ itemCondition: 'https://schema.org/NewCondition',
69
+ price: 12.5,
70
+ priceValidUntil: '2027-08-24T19:48:25Z',
71
+ priceCurrency: 'USD',
72
+ url: 'https://store.example/p',
73
+ })
74
+ })
75
+
76
+ it.each([
77
+ ['priceValidUntil', ''],
78
+ ['priceValidUntil', null],
79
+ ['availability', null],
80
+ ])('drops %s when it resolves to %p', (field, value) => {
81
+ const offer = toProductJsonLdOffer(
82
+ makeServerOffer({ [field]: value }),
83
+ OBJECT_LEVEL
84
+ )
85
+
86
+ expect(offer).not.toHaveProperty(field)
87
+ expect(offer).toHaveProperty('price')
88
+ })
89
+
90
+ // Emptiness, not falsiness — a free item still has a price.
91
+ it('keeps a price of 0', () => {
92
+ const offer = toProductJsonLdOffer(
93
+ makeServerOffer({ price: 0 }),
94
+ OBJECT_LEVEL
95
+ )
96
+
97
+ expect(offer).toHaveProperty('price', 0)
98
+ })
99
+
100
+ it('drops an empty priceCurrency', () => {
101
+ const offer = toProductJsonLdOffer(makeServerOffer(), {
102
+ ...OBJECT_LEVEL,
103
+ priceCurrency: '',
104
+ })
105
+
106
+ expect(offer).not.toHaveProperty('priceCurrency')
107
+ })
108
+
109
+ // An Offer with only a currency and a URL is invalid Schema.org, so the
110
+ // property is omitted entirely rather than emitted priceless.
111
+ it('returns undefined when the product has no offer', () => {
112
+ expect(toProductJsonLdOffer(undefined, OBJECT_LEVEL)).toBeUndefined()
113
+ })
114
+
115
+ // StoreOffer.price resolves to null outside the search and order-form roots.
116
+ // Dropping the key alone would leave a priceless Offer, which is exactly the
117
+ // invalid markup the no-offer case avoids.
118
+ it.each([
119
+ ['null', null],
120
+ ['an empty string', ''],
121
+ ])('returns undefined when the price is %s', (_label, price) => {
122
+ expect(
123
+ toProductJsonLdOffer(makeServerOffer({ price }), OBJECT_LEVEL)
124
+ ).toBeUndefined()
125
+ })
126
+ })
@@ -0,0 +1,74 @@
1
+ import { ProductJsonLd } from 'next-seo'
2
+ import { renderToStaticMarkup } from 'react-dom/server'
3
+ import { describe, expect, it } from 'vitest'
4
+ import { toProductJsonLdOffer } from '../../src/utils/productJsonLd'
5
+
6
+ /**
7
+ * Pins the next-seo behaviour the PDP's JSON-LD depends on.
8
+ *
9
+ * `p.tsx` omits `mpn`, `gtin` and `releaseDate` by conditional spread rather
10
+ * than passing empty strings, and drops `seller` from the offer allowlist. Both
11
+ * choices only work because of how next-seo builds the script — it forwards
12
+ * unknown props untouched (so `""` would be published) and turns any truthy
13
+ * `seller` into an `Organization`, even when it has no name. A next-seo upgrade
14
+ * that changed either would silently reintroduce the defects from ticket
15
+ * #1449246, so the behaviour is asserted here instead of assumed.
16
+ */
17
+
18
+ // `useAppDir` renders the script directly; without it next-seo emits through
19
+ // next/head, which produces no markup outside a Next document.
20
+ const renderJsonLd = (props: Record<string, unknown>) => {
21
+ const html = renderToStaticMarkup(
22
+ <ProductJsonLd useAppDir productName="Product" images={[]} {...props} />
23
+ )
24
+
25
+ return JSON.parse(html.replace(/^[\s\S]*?>([\s\S]*)<\/script>[\s\S]*$/, '$1'))
26
+ }
27
+
28
+ describe('next-seo ProductJsonLd contract', () => {
29
+ it('publishes an empty string when a prop is passed empty', () => {
30
+ expect(renderJsonLd({ mpn: '' })).toHaveProperty('mpn', '')
31
+ })
32
+
33
+ it('omits the key when the prop is absent — what the conditional spread relies on', () => {
34
+ expect(renderJsonLd({})).not.toHaveProperty('mpn')
35
+ })
36
+
37
+ it('emits a nameless Organization for a seller that has no name', () => {
38
+ const jsonLd = renderJsonLd({
39
+ offers: { price: 1, seller: { identifier: '1' } },
40
+ })
41
+
42
+ expect(jsonLd.offers.seller).toEqual({ '@type': 'Organization' })
43
+ })
44
+
45
+ it('leaves the mapper output intact, adding only @type', () => {
46
+ const offers = toProductJsonLdOffer(
47
+ {
48
+ availability: 'https://schema.org/InStock',
49
+ itemCondition: 'https://schema.org/NewCondition',
50
+ price: 0.2,
51
+ priceValidUntil: '2027-08-24T19:48:25Z',
52
+ priceWithTaxes: 0.2,
53
+ listPrice: 0.2,
54
+ listPriceWithTaxes: 0.2,
55
+ quantity: 10000,
56
+ priceToken: 'a-short-lived-pricing-token',
57
+ seller: { identifier: '1' },
58
+ } as any,
59
+ { priceCurrency: 'USD', url: 'https://store.example/p' }
60
+ )
61
+
62
+ const jsonLd = renderJsonLd({ offers })
63
+
64
+ expect(jsonLd.offers).toEqual({
65
+ '@type': 'Offer',
66
+ availability: 'https://schema.org/InStock',
67
+ itemCondition: 'https://schema.org/NewCondition',
68
+ price: 0.2,
69
+ priceCurrency: 'USD',
70
+ priceValidUntil: '2027-08-24T19:48:25Z',
71
+ url: 'https://store.example/p',
72
+ })
73
+ })
74
+ })