@faststore/core 4.10.1-dev.6 → 4.10.1-dev.7

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.
Files changed (60) hide show
  1. package/.turbo/turbo-generate.log +3 -3
  2. package/.turbo/turbo-test.log +176 -167
  3. package/@generated/graphql.ts +1 -0
  4. package/@generated/schema.graphql +1 -1
  5. package/CHANGELOG.md +29 -0
  6. package/package.json +6 -6
  7. package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawer.tsx +3 -3
  8. package/src/components/account/Drawer/OrganizationDrawer/OrganizationDrawerBody.tsx +5 -7
  9. package/src/components/account/Drawer/OrganizationSignInButton/OrganizationSignInButton.tsx +4 -1
  10. package/src/components/account/Layout/Layout.tsx +5 -7
  11. package/src/components/account/Menu/Menu.tsx +1 -1
  12. package/src/components/navigation/Navbar/Navbar.tsx +3 -3
  13. package/src/components/navigation/Navbar/NavbarAccountArea.tsx +5 -5
  14. package/src/components/navigation/NavbarSlider/NavbarSlider.tsx +3 -3
  15. package/src/experimental/myAccountCmsServerSideProps.ts +3 -3
  16. package/src/experimental/myAccountServerSideProps.ts +4 -4
  17. package/src/pages/[slug]/p.tsx +11 -2
  18. package/src/pages/api/fs/logout.ts +18 -10
  19. package/src/pages/api/fs/switch-contract.ts +170 -21
  20. package/src/pages/api/graphql.ts +4 -3
  21. package/src/pages/pvt/account/cards/index.tsx +5 -5
  22. package/src/pages/pvt/account/orders/[id].tsx +5 -5
  23. package/src/pages/pvt/account/orders/index.tsx +5 -5
  24. package/src/pages/pvt/account/profile.tsx +5 -5
  25. package/src/pages/pvt/account/quotes/index.tsx +5 -5
  26. package/src/pages/pvt/account/security.tsx +5 -5
  27. package/src/pages/pvt/account/user-details.tsx +6 -6
  28. package/src/sdk/account/getB2BSessionClaims.ts +1 -1
  29. package/src/sdk/account/getIsB2BBuyer.ts +28 -0
  30. package/src/sdk/account/refreshToken.ts +89 -11
  31. package/src/sdk/account/refreshTokenPaths.ts +25 -0
  32. package/src/sdk/account/useRefreshToken.ts +10 -5
  33. package/src/sdk/product/isProductNotFound.ts +13 -0
  34. package/src/sdk/product/useProductNotFoundRedirect.ts +43 -0
  35. package/src/sdk/product/useProductQuery.ts +29 -0
  36. package/src/sdk/session/index.ts +4 -4
  37. package/src/utils/getCheckoutParentDomain.ts +53 -0
  38. package/src/utils/trustedForwardedHost.ts +18 -0
  39. package/test/components/account/Layout.test.tsx +1 -1
  40. package/test/components/account/Menu.browser.test.tsx +7 -5
  41. package/test/components/account/OrganizationDrawer.browser.test.tsx +13 -13
  42. package/test/components/account/OrganizationDrawerBody.test.tsx +3 -3
  43. package/test/components/account/OrganizationSignInButton.browser.test.tsx +19 -4
  44. package/test/components/navigation/Navbar.browser.test.tsx +3 -3
  45. package/test/components/navigation/NavbarAccountArea.browser.test.tsx +5 -5
  46. package/test/components/navigation/NavbarSlider.browser.test.tsx +3 -3
  47. package/test/experimental/myAccountCmsServerSideProps.test.ts +5 -5
  48. package/test/pages/api/logout.test.ts +106 -0
  49. package/test/pages/api/switch-contract.test.ts +443 -15
  50. package/test/pages/pvt/account/cards/index.test.ts +3 -3
  51. package/test/sdk/account/getIsB2BBuyer.test.ts +52 -0
  52. package/test/sdk/account/refreshToken.browser.test.ts +10 -1
  53. package/test/sdk/account/refreshToken.test.ts +150 -18
  54. package/test/sdk/account/useRefreshToken.browser.test.ts +4 -1
  55. package/test/sdk/product/useProductNotFoundRedirect.browser.test.ts +83 -0
  56. package/test/sdk/product/useProductQuery.browser.test.ts +154 -0
  57. package/test/sdk/session/refreshTokenRoute.browser.test.ts +106 -0
  58. package/test/sdk/session/validateSession.browser.test.ts +4 -1
  59. package/test/utils/trustedForwardedHost.test.ts +21 -0
  60. package/src/sdk/account/getIsRepresentative.ts +0 -20
@@ -11,17 +11,17 @@ vi.mock(
11
11
  () => ({
12
12
  OrganizationSignInButton: ({
13
13
  icon,
14
- isRepresentative,
14
+ isB2BBuyer,
15
15
  }: {
16
16
  icon: { alt: string; icon: string }
17
- isRepresentative?: boolean
17
+ isB2BBuyer?: boolean
18
18
  }) => (
19
19
  <button
20
20
  type="button"
21
21
  data-testid="organization-signin"
22
22
  data-icon={icon.alt}
23
23
  >
24
- {isRepresentative ? 'Representative' : 'Not representative'}
24
+ {isB2BBuyer ? 'B2B buyer' : 'Not a B2B buyer'}
25
25
  </button>
26
26
  ),
27
27
  })
@@ -85,7 +85,7 @@ describe('NavbarAccountArea', () => {
85
85
  <NavbarAccountArea
86
86
  isSignInResolved
87
87
  isOrganizationEnabled
88
- isRepresentative
88
+ isB2BBuyer
89
89
  signInButton={signInButton}
90
90
  ButtonSignIn={ButtonSignInStub}
91
91
  skeletonAttr="data-fs-navbar-signin-skeleton"
@@ -93,7 +93,7 @@ describe('NavbarAccountArea', () => {
93
93
  )
94
94
 
95
95
  expect(screen.getByTestId('organization-signin')).toHaveTextContent(
96
- 'Representative'
96
+ 'B2B buyer'
97
97
  )
98
98
  expect(screen.queryByTestId('storefront-signin')).toBeNull()
99
99
  })
@@ -120,10 +120,10 @@ describe('NavbarSlider', () => {
120
120
  )
121
121
  })
122
122
 
123
- it('forwards isSignInResolved=true and isOrganizationEnabled once the B2B representative session resolves', () => {
123
+ it('forwards isSignInResolved=true and isOrganizationEnabled once the B2B session resolves', () => {
124
124
  mockStoreConfig.experimental.enableFaststoreMyAccount = true
125
125
  mockUseSession.mockReturnValue({
126
- b2b: { isRepresentative: true },
126
+ b2b: { unitId: 'unit-1', customerId: 'contract-1' },
127
127
  isSessionReady: true,
128
128
  hasValidated: true,
129
129
  })
@@ -134,7 +134,7 @@ describe('NavbarSlider', () => {
134
134
  expect.objectContaining({
135
135
  isSignInResolved: true,
136
136
  isOrganizationEnabled: true,
137
- isRepresentative: true,
137
+ isB2BBuyer: true,
138
138
  })
139
139
  )
140
140
 
@@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
2
 
3
3
  const mockValidateUser = vi.fn()
4
4
  const mockGetMyAccountRedirect = vi.fn()
5
- const mockGetIsRepresentative = vi.fn()
5
+ const mockGetIsB2BBuyer = vi.fn()
6
6
  const mockFetchMyAccountPageContent = vi.fn()
7
7
  const mockGetGlobalSectionsData = vi.fn()
8
8
  const mockExecute = vi.fn()
@@ -20,8 +20,8 @@ vi.mock('src/utils/myAccountRedirect', () => ({
20
20
  mockGetMyAccountRedirect(...args),
21
21
  }))
22
22
 
23
- vi.mock('src/sdk/account/getIsRepresentative', () => ({
24
- getIsRepresentative: (...args: unknown[]) => mockGetIsRepresentative(...args),
23
+ vi.mock('src/sdk/account/getIsB2BBuyer', () => ({
24
+ getIsB2BBuyer: (...args: unknown[]) => mockGetIsB2BBuyer(...args),
25
25
  }))
26
26
 
27
27
  vi.mock('src/server/cms/fetchMyAccountPageContent', () => ({
@@ -78,7 +78,7 @@ function buildContext(overrides: Record<string, unknown> = {}) {
78
78
  describe('myAccountCmsServerSideProps', () => {
79
79
  beforeEach(() => {
80
80
  vi.clearAllMocks()
81
- mockGetIsRepresentative.mockReturnValue(false)
81
+ mockGetIsB2BBuyer.mockReturnValue(false)
82
82
  mockGetGlobalSectionsData.mockReturnValue([
83
83
  Promise.resolve({ sections: [], settings: {} }),
84
84
  Promise.resolve({ sections: [] }),
@@ -176,7 +176,7 @@ describe('myAccountCmsServerSideProps', () => {
176
176
  expect(result).toMatchObject({
177
177
  props: {
178
178
  accountName: 'Ada',
179
- isRepresentative: false,
179
+ isB2BBuyer: false,
180
180
  navigationLabels: { profileLabel: 'Profile' },
181
181
  pageSections: [
182
182
  {
@@ -0,0 +1,106 @@
1
+ import type { NextApiRequest, NextApiResponse } from 'next'
2
+ import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest'
3
+
4
+ const mockConfig = vi.hoisted(() => ({
5
+ default: {
6
+ experimental: { refreshToken: true } as Record<string, unknown>,
7
+ } as { experimental: Record<string, unknown>; secureSubdomain?: string },
8
+ }))
9
+
10
+ vi.mock('discovery.config', () => ({ __esModule: true, ...mockConfig }))
11
+
12
+ function createReqRes(cookie = 'VtexIdclientAutCookie_test-store=token') {
13
+ const res = {
14
+ status: vi.fn().mockReturnThis(),
15
+ json: vi.fn(),
16
+ end: vi.fn(),
17
+ setHeader: vi.fn(),
18
+ }
19
+
20
+ return {
21
+ req: {
22
+ method: 'POST',
23
+ headers: { host: 'store.example.com', cookie },
24
+ } as unknown as NextApiRequest,
25
+ res: res as unknown as NextApiResponse & { setHeader: Mock },
26
+ }
27
+ }
28
+
29
+ const setCookiesFrom = (res: { setHeader: Mock }): string[] =>
30
+ (res.setHeader.mock.calls.find(
31
+ ([name]) => String(name).toLowerCase() === 'set-cookie'
32
+ )?.[1] as string[] | undefined) ?? []
33
+
34
+ async function getHandler() {
35
+ const mod = await import('../../../src/pages/api/fs/logout')
36
+ return mod.default
37
+ }
38
+
39
+ describe('/api/fs/logout', () => {
40
+ beforeEach(() => {
41
+ vi.resetModules()
42
+ mockConfig.default.experimental = { refreshToken: true }
43
+ mockConfig.default.secureSubdomain = undefined
44
+ })
45
+
46
+ it('expires vid_rt on every path it can be scoped to, even though the request never carries it', async () => {
47
+ const handler = await getHandler()
48
+ const { req, res } = createReqRes()
49
+
50
+ await handler(req, res)
51
+
52
+ const vidRt = setCookiesFrom(res).filter((cookie) =>
53
+ cookie.startsWith('vid_rt=;')
54
+ )
55
+
56
+ for (const path of [
57
+ '/api/vtexid/refreshtoken/webstore',
58
+ '/api/authenticator/refreshtoken/webstore',
59
+ '/api/authenticator/v1/refreshtoken/webstore',
60
+ ]) {
61
+ expect(vidRt.some((cookie) => cookie.includes(`path=${path}`))).toBe(true)
62
+ }
63
+ })
64
+
65
+ it('does not touch vid_rt when refresh token is disabled', async () => {
66
+ mockConfig.default.experimental = {}
67
+ const handler = await getHandler()
68
+ const { req, res } = createReqRes()
69
+
70
+ await handler(req, res)
71
+
72
+ expect(
73
+ setCookiesFrom(res).some((cookie) => cookie.startsWith('vid_rt='))
74
+ ).toBe(false)
75
+ })
76
+
77
+ it('expires the auth cookie on the parent shared with a sibling checkout host', async () => {
78
+ mockConfig.default.secureSubdomain = 'https://secure.example.com'
79
+ const handler = await getHandler()
80
+ const { req, res } = createReqRes()
81
+
82
+ await handler(req, res)
83
+
84
+ expect(
85
+ setCookiesFrom(res).filter((cookie) =>
86
+ cookie.startsWith('VtexIdclientAutCookie_test-store=;')
87
+ )
88
+ ).toEqual(
89
+ expect.arrayContaining([expect.stringContaining('; domain=example.com;')])
90
+ )
91
+ })
92
+
93
+ it('does not touch the parent domain when checkout runs on the store host', async () => {
94
+ mockConfig.default.secureSubdomain = 'https://store.example.com'
95
+ const handler = await getHandler()
96
+ const { req, res } = createReqRes()
97
+
98
+ await handler(req, res)
99
+
100
+ expect(
101
+ setCookiesFrom(res).some((cookie) =>
102
+ cookie.includes('; domain=example.com;')
103
+ )
104
+ ).toBe(false)
105
+ })
106
+ })