@faststore/core 4.7.0-dev.3 → 4.7.0-dev.5
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 +101 -92
- package/CHANGELOG.md +18 -0
- package/cms/faststore/components/cms_component__recommendationshelf.jsonc +1 -7
- package/cms/faststore/my-account/README.md +2 -2
- package/cms/faststore/schema.json +1 -7
- package/discovery.config.default.js +5 -0
- package/package.json +6 -6
- package/src/Layout.tsx +3 -3
- package/src/components/account/orders/OrderDetails/section.module.scss +44 -3
- package/src/components/cms/RenderSections.tsx +20 -2
- package/src/components/sections/RecommendationShelf/README.md +25 -15
- package/src/components/sections/RecommendationShelf/RecommendationShelf.tsx +0 -2
- package/src/components/sections/RecommendationShelf/RecommendationShelf.types.ts +0 -7
- package/src/pages/pvt/account/403.tsx +1 -0
- package/src/pages/pvt/account/404.tsx +5 -1
- package/src/pages/pvt/account/cards/index.tsx +1 -0
- package/src/pages/pvt/account/orders/[id].tsx +5 -1
- package/src/pages/pvt/account/orders/index.tsx +1 -0
- package/src/pages/pvt/account/profile.tsx +1 -0
- package/src/pages/pvt/account/security.tsx +1 -0
- package/src/pages/pvt/account/user-details.tsx +1 -0
- package/src/sdk/analytics/hooks/useStartRecommendationSession.ts +18 -13
- package/src/sdk/recommendations/useRecommendationShelf.ts +17 -15
- package/src/server/cms/myAccountDefaultSections.ts +8 -3
- package/test/components/RecommendationShelf/RecommendationShelf.browser.test.tsx +11 -32
- package/test/components/cms/RenderSections.browser.test.tsx +135 -0
- package/test/sdk/analytics/useStartRecommendationSession.test.ts +27 -58
- package/test/sdk/recommendations/useRecommendationShelf.browser.test.tsx +32 -34
- package/test/sdk/recommendations/useRecommendationShelf.sessionFallback.browser.test.tsx +110 -0
- package/test/server/cms/myAccountDefaultSections.test.ts +44 -0
- package/src/sdk/analytics/utils/hasEnabledRecommendationShelf.ts +0 -49
- package/test/sdk/analytics/hasEnabledRecommendationShelf.test.ts +0 -88
|
@@ -18,50 +18,21 @@ vi.mock('src/sdk/graphql/useLazyQuery', () => ({
|
|
|
18
18
|
const getCookie = vi.hoisted(() => vi.fn())
|
|
19
19
|
vi.mock('src/utils/getCookie', () => ({ getCookie }))
|
|
20
20
|
|
|
21
|
+
const storeConfigMock = vi.hoisted(() => ({
|
|
22
|
+
experimental: { enableRecommendations: true },
|
|
23
|
+
}))
|
|
24
|
+
vi.mock('discovery.config', () => ({ default: storeConfigMock }))
|
|
25
|
+
|
|
21
26
|
import {
|
|
22
27
|
resetStartRecommendationSessionLock,
|
|
23
28
|
useStartRecommendationSession,
|
|
24
29
|
} from 'src/sdk/analytics/hooks/useStartRecommendationSession'
|
|
25
30
|
|
|
26
|
-
const enabledPageProps = {
|
|
27
|
-
sections: [
|
|
28
|
-
{
|
|
29
|
-
name: 'RecommendationShelf',
|
|
30
|
-
data: { enableRecommendations: true },
|
|
31
|
-
},
|
|
32
|
-
],
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const multipleEnabledShelvesPageProps = {
|
|
36
|
-
sections: [
|
|
37
|
-
{
|
|
38
|
-
name: 'RecommendationShelf',
|
|
39
|
-
data: { enableRecommendations: true, campaignVrn: 'vrn:a' },
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
name: 'RecommendationShelf',
|
|
43
|
-
data: { enableRecommendations: true, campaignVrn: 'vrn:b' },
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: 'ProductShelf',
|
|
47
|
-
data: {},
|
|
48
|
-
},
|
|
49
|
-
],
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const disabledPageProps = {
|
|
53
|
-
sections: [
|
|
54
|
-
{
|
|
55
|
-
name: 'RecommendationShelf',
|
|
56
|
-
data: { enableRecommendations: false },
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
}
|
|
60
|
-
|
|
61
31
|
beforeEach(() => {
|
|
62
32
|
resetStartRecommendationSessionLock()
|
|
63
33
|
useLazyQueryMock.mockReturnValue([runStartRecommendationSession, {}])
|
|
64
34
|
runStartRecommendationSession.mockResolvedValue(true)
|
|
35
|
+
storeConfigMock.experimental.enableRecommendations = true
|
|
65
36
|
})
|
|
66
37
|
|
|
67
38
|
afterEach(() => {
|
|
@@ -69,18 +40,19 @@ afterEach(() => {
|
|
|
69
40
|
})
|
|
70
41
|
|
|
71
42
|
describe('useStartRecommendationSession', () => {
|
|
72
|
-
it('does not start a session when
|
|
43
|
+
it('does not start a session when experimental.enableRecommendations is false', async () => {
|
|
44
|
+
storeConfigMock.experimental.enableRecommendations = false
|
|
73
45
|
getCookie.mockReturnValue(undefined)
|
|
74
46
|
|
|
75
|
-
renderHook(() => useStartRecommendationSession(
|
|
47
|
+
renderHook(() => useStartRecommendationSession())
|
|
76
48
|
|
|
77
49
|
await waitFor(() => {
|
|
78
50
|
expect(runStartRecommendationSession).not.toHaveBeenCalled()
|
|
79
51
|
})
|
|
80
52
|
})
|
|
81
53
|
|
|
82
|
-
it('does not start a session when
|
|
83
|
-
getCookie.mockReturnValue(
|
|
54
|
+
it('does not start a session when the session cookie already exists', async () => {
|
|
55
|
+
getCookie.mockReturnValue('already-started')
|
|
84
56
|
|
|
85
57
|
renderHook(() => useStartRecommendationSession())
|
|
86
58
|
|
|
@@ -89,53 +61,50 @@ describe('useStartRecommendationSession', () => {
|
|
|
89
61
|
})
|
|
90
62
|
})
|
|
91
63
|
|
|
92
|
-
it('
|
|
93
|
-
getCookie.mockReturnValue(
|
|
64
|
+
it('starts a session when the feature flag is on and no session cookie is present', async () => {
|
|
65
|
+
getCookie.mockReturnValue(undefined)
|
|
94
66
|
|
|
95
|
-
renderHook(() => useStartRecommendationSession(
|
|
67
|
+
renderHook(() => useStartRecommendationSession())
|
|
96
68
|
|
|
97
69
|
await waitFor(() => {
|
|
98
|
-
expect(runStartRecommendationSession).
|
|
70
|
+
expect(runStartRecommendationSession).toHaveBeenCalledTimes(1)
|
|
99
71
|
})
|
|
100
72
|
})
|
|
101
73
|
|
|
102
|
-
it('starts a session when
|
|
74
|
+
it('starts a session when enabled override is true even if the feature flag is off', async () => {
|
|
75
|
+
storeConfigMock.experimental.enableRecommendations = false
|
|
103
76
|
getCookie.mockReturnValue(undefined)
|
|
104
77
|
|
|
105
|
-
renderHook(() => useStartRecommendationSession(
|
|
78
|
+
renderHook(() => useStartRecommendationSession(true))
|
|
106
79
|
|
|
107
80
|
await waitFor(() => {
|
|
108
81
|
expect(runStartRecommendationSession).toHaveBeenCalledTimes(1)
|
|
109
82
|
})
|
|
110
83
|
})
|
|
111
84
|
|
|
112
|
-
it('
|
|
85
|
+
it('does not start a session when enabled override is false even if the feature flag is on', async () => {
|
|
113
86
|
getCookie.mockReturnValue(undefined)
|
|
114
87
|
|
|
115
|
-
|
|
116
|
-
({ pageProps }) => useStartRecommendationSession(pageProps),
|
|
117
|
-
{ initialProps: { pageProps: multipleEnabledShelvesPageProps } }
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
// Simulate Layout re-renders caused by multiple shelves updating.
|
|
121
|
-
rerender({ pageProps: multipleEnabledShelvesPageProps })
|
|
122
|
-
rerender({ pageProps: multipleEnabledShelvesPageProps })
|
|
88
|
+
renderHook(() => useStartRecommendationSession(false))
|
|
123
89
|
|
|
124
90
|
await waitFor(() => {
|
|
125
|
-
expect(runStartRecommendationSession).
|
|
91
|
+
expect(runStartRecommendationSession).not.toHaveBeenCalled()
|
|
126
92
|
})
|
|
127
93
|
})
|
|
128
94
|
|
|
129
|
-
it('
|
|
95
|
+
it('starts a session only once across remounts / re-renders (in-memory lock)', async () => {
|
|
130
96
|
getCookie.mockReturnValue(undefined)
|
|
131
97
|
|
|
132
|
-
renderHook(() => useStartRecommendationSession(
|
|
98
|
+
const { rerender } = renderHook(() => useStartRecommendationSession())
|
|
99
|
+
|
|
100
|
+
rerender()
|
|
101
|
+
rerender()
|
|
133
102
|
|
|
134
103
|
await waitFor(() => {
|
|
135
104
|
expect(runStartRecommendationSession).toHaveBeenCalledTimes(1)
|
|
136
105
|
})
|
|
137
106
|
|
|
138
|
-
renderHook(() => useStartRecommendationSession(
|
|
107
|
+
renderHook(() => useStartRecommendationSession())
|
|
139
108
|
|
|
140
109
|
await waitFor(() => {
|
|
141
110
|
expect(runStartRecommendationSession).toHaveBeenCalledTimes(1)
|
|
@@ -17,6 +17,11 @@ vi.mock('src/sdk/recommendations/useRecommendationUserId', () => ({
|
|
|
17
17
|
useRecommendationUserId,
|
|
18
18
|
}))
|
|
19
19
|
|
|
20
|
+
const useStartRecommendationSession = vi.hoisted(() => vi.fn())
|
|
21
|
+
vi.mock('src/sdk/analytics/hooks/useStartRecommendationSession', () => ({
|
|
22
|
+
useStartRecommendationSession,
|
|
23
|
+
}))
|
|
24
|
+
|
|
20
25
|
import { useRecommendationShelf } from 'src/sdk/recommendations/useRecommendationShelf'
|
|
21
26
|
|
|
22
27
|
const TOP_ITEMS_VRN = 'vrn:recommendations:acc:rec-top-items-v2:campaign-1'
|
|
@@ -53,12 +58,9 @@ afterEach(() => {
|
|
|
53
58
|
})
|
|
54
59
|
|
|
55
60
|
describe('useRecommendationShelf', () => {
|
|
56
|
-
it('exposes the campaign payload
|
|
61
|
+
it('exposes the campaign payload', () => {
|
|
57
62
|
const { result } = renderHook(() =>
|
|
58
|
-
useRecommendationShelf({
|
|
59
|
-
campaignVrn: TOP_ITEMS_VRN,
|
|
60
|
-
enableRecommendations: true,
|
|
61
|
-
})
|
|
63
|
+
useRecommendationShelf({ campaignVrn: TOP_ITEMS_VRN })
|
|
62
64
|
)
|
|
63
65
|
|
|
64
66
|
expect(result.current.items).toHaveLength(2)
|
|
@@ -66,32 +68,40 @@ describe('useRecommendationShelf', () => {
|
|
|
66
68
|
expect(result.current.correlationId).toBe('corr-1')
|
|
67
69
|
})
|
|
68
70
|
|
|
69
|
-
it('never fetches
|
|
71
|
+
it('never fetches before a userId resolves', () => {
|
|
72
|
+
useRecommendationUserId.mockReturnValue(null)
|
|
73
|
+
|
|
70
74
|
renderHook(() => useRecommendationShelf({ campaignVrn: TOP_ITEMS_VRN }))
|
|
71
75
|
|
|
72
76
|
expect(lastArgs()).toBeNull()
|
|
73
77
|
})
|
|
74
78
|
|
|
75
|
-
it('
|
|
79
|
+
it('starts the recommendation session as a fallback when userId is missing', () => {
|
|
76
80
|
useRecommendationUserId.mockReturnValue(null)
|
|
77
81
|
|
|
78
|
-
renderHook(() =>
|
|
79
|
-
useRecommendationShelf({
|
|
80
|
-
campaignVrn: TOP_ITEMS_VRN,
|
|
81
|
-
enableRecommendations: true,
|
|
82
|
-
})
|
|
83
|
-
)
|
|
82
|
+
renderHook(() => useRecommendationShelf({ campaignVrn: TOP_ITEMS_VRN }))
|
|
84
83
|
|
|
85
|
-
expect(
|
|
84
|
+
expect(useStartRecommendationSession).toHaveBeenCalledWith(true)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('starts the recommendation session while userId is still resolving', () => {
|
|
88
|
+
useRecommendationUserId.mockReturnValue(undefined)
|
|
89
|
+
|
|
90
|
+
renderHook(() => useRecommendationShelf({ campaignVrn: TOP_ITEMS_VRN }))
|
|
91
|
+
|
|
92
|
+
expect(useStartRecommendationSession).toHaveBeenCalledWith(true)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('does not start the recommendation session when userId already exists', () => {
|
|
96
|
+
useRecommendationUserId.mockReturnValue('user-1')
|
|
97
|
+
|
|
98
|
+
renderHook(() => useRecommendationShelf({ campaignVrn: TOP_ITEMS_VRN }))
|
|
99
|
+
|
|
100
|
+
expect(useStartRecommendationSession).toHaveBeenCalledWith(false)
|
|
86
101
|
})
|
|
87
102
|
|
|
88
103
|
it('never fetches on a malformed campaign vrn', () => {
|
|
89
|
-
renderHook(() =>
|
|
90
|
-
useRecommendationShelf({
|
|
91
|
-
campaignVrn: 'not-a-vrn',
|
|
92
|
-
enableRecommendations: true,
|
|
93
|
-
})
|
|
94
|
-
)
|
|
104
|
+
renderHook(() => useRecommendationShelf({ campaignVrn: 'not-a-vrn' }))
|
|
95
105
|
|
|
96
106
|
expect(lastArgs()).toBeNull()
|
|
97
107
|
})
|
|
@@ -104,7 +114,6 @@ describe('useRecommendationShelf', () => {
|
|
|
104
114
|
renderHook(() =>
|
|
105
115
|
useRecommendationShelf({
|
|
106
116
|
campaignVrn: CROSS_SELL_VRN,
|
|
107
|
-
enableRecommendations: true,
|
|
108
117
|
itemsContext: 'CART',
|
|
109
118
|
})
|
|
110
119
|
)
|
|
@@ -120,7 +129,6 @@ describe('useRecommendationShelf', () => {
|
|
|
120
129
|
renderHook(() =>
|
|
121
130
|
useRecommendationShelf({
|
|
122
131
|
campaignVrn: CROSS_SELL_VRN,
|
|
123
|
-
enableRecommendations: true,
|
|
124
132
|
itemsContext: 'CART',
|
|
125
133
|
})
|
|
126
134
|
)
|
|
@@ -132,7 +140,6 @@ describe('useRecommendationShelf', () => {
|
|
|
132
140
|
renderHook(() =>
|
|
133
141
|
useRecommendationShelf({
|
|
134
142
|
campaignVrn: TOP_ITEMS_VRN,
|
|
135
|
-
enableRecommendations: true,
|
|
136
143
|
itemsContext: 'CART',
|
|
137
144
|
})
|
|
138
145
|
)
|
|
@@ -150,7 +157,6 @@ describe('useRecommendationShelf', () => {
|
|
|
150
157
|
const { rerender } = renderHook(() =>
|
|
151
158
|
useRecommendationShelf({
|
|
152
159
|
campaignVrn: CROSS_SELL_VRN,
|
|
153
|
-
enableRecommendations: true,
|
|
154
160
|
itemsContext: 'CART',
|
|
155
161
|
})
|
|
156
162
|
)
|
|
@@ -166,12 +172,7 @@ describe('useRecommendationShelf', () => {
|
|
|
166
172
|
data: { product: { isVariantOf: { productGroupID: 'pg-pdp' } } },
|
|
167
173
|
})
|
|
168
174
|
|
|
169
|
-
renderHook(() =>
|
|
170
|
-
useRecommendationShelf({
|
|
171
|
-
campaignVrn: CROSS_SELL_VRN,
|
|
172
|
-
enableRecommendations: true,
|
|
173
|
-
})
|
|
174
|
-
)
|
|
175
|
+
renderHook(() => useRecommendationShelf({ campaignVrn: CROSS_SELL_VRN }))
|
|
175
176
|
|
|
176
177
|
expect(lastArgs()?.products).toEqual(['pg-pdp'])
|
|
177
178
|
})
|
|
@@ -185,10 +186,7 @@ describe('useRecommendationShelf', () => {
|
|
|
185
186
|
})
|
|
186
187
|
|
|
187
188
|
const { result } = renderHook(() =>
|
|
188
|
-
useRecommendationShelf({
|
|
189
|
-
campaignVrn: TOP_ITEMS_VRN,
|
|
190
|
-
enableRecommendations: true,
|
|
191
|
-
})
|
|
189
|
+
useRecommendationShelf({ campaignVrn: TOP_ITEMS_VRN })
|
|
192
190
|
)
|
|
193
191
|
|
|
194
192
|
expect(result.current.items).toEqual([])
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { renderHook, waitFor } from '@testing-library/react'
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
const usePDP = vi.hoisted(() => vi.fn())
|
|
5
|
+
vi.mock('@faststore/core', () => ({ usePDP }))
|
|
6
|
+
|
|
7
|
+
vi.mock('@faststore/core/api', () => ({
|
|
8
|
+
gql: (s: string) => ({ __meta__: {}, query: s }),
|
|
9
|
+
}))
|
|
10
|
+
|
|
11
|
+
const useCart = vi.hoisted(() => vi.fn())
|
|
12
|
+
vi.mock('src/sdk/cart', () => ({ useCart }))
|
|
13
|
+
|
|
14
|
+
const useRecommendations = vi.hoisted(() => vi.fn())
|
|
15
|
+
vi.mock('src/sdk/recommendations/useRecommendations', () => ({
|
|
16
|
+
useRecommendations,
|
|
17
|
+
}))
|
|
18
|
+
|
|
19
|
+
const runStartRecommendationSession = vi.hoisted(() => vi.fn())
|
|
20
|
+
vi.mock('src/sdk/graphql/useLazyQuery', () => ({
|
|
21
|
+
useLazyQuery: () => [runStartRecommendationSession, {}],
|
|
22
|
+
}))
|
|
23
|
+
|
|
24
|
+
const cookies = vi.hoisted(() => ({
|
|
25
|
+
current: {} as Record<string, string | undefined>,
|
|
26
|
+
}))
|
|
27
|
+
vi.mock('src/utils/getCookie', () => ({
|
|
28
|
+
getCookie: (name: string) => cookies.current[name],
|
|
29
|
+
}))
|
|
30
|
+
|
|
31
|
+
const storeConfigMock = vi.hoisted(() => ({
|
|
32
|
+
experimental: { enableRecommendations: false },
|
|
33
|
+
}))
|
|
34
|
+
vi.mock('discovery.config', () => ({ default: storeConfigMock }))
|
|
35
|
+
|
|
36
|
+
// Keep retry semantics (loop until `until` / attempts) but skip wall-clock
|
|
37
|
+
// backoff so the cookie lookup can observe the session mutation in this test.
|
|
38
|
+
vi.mock('src/utils/retry', async (importOriginal) => {
|
|
39
|
+
const actual = await importOriginal<typeof import('src/utils/retry')>()
|
|
40
|
+
return {
|
|
41
|
+
retry: <T,>(
|
|
42
|
+
fn: () => T | Promise<T>,
|
|
43
|
+
options?: Parameters<typeof actual.retry>[1]
|
|
44
|
+
) => actual.retry(fn, { ...options, delayMs: 0, maxDelayMs: 0 }),
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
import { resetStartRecommendationSessionLock } from 'src/sdk/analytics/hooks/useStartRecommendationSession'
|
|
49
|
+
import { useRecommendationShelf } from 'src/sdk/recommendations/useRecommendationShelf'
|
|
50
|
+
|
|
51
|
+
const TOP_ITEMS_VRN = 'vrn:recommendations:acc:rec-top-items-v2:campaign-1'
|
|
52
|
+
|
|
53
|
+
const lastArgs = () => useRecommendations.mock.calls.at(-1)?.[0]
|
|
54
|
+
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
cookies.current = {}
|
|
57
|
+
resetStartRecommendationSessionLock()
|
|
58
|
+
storeConfigMock.experimental.enableRecommendations = false
|
|
59
|
+
usePDP.mockReturnValue({ data: undefined })
|
|
60
|
+
useCart.mockReturnValue({ items: [] })
|
|
61
|
+
useRecommendations.mockReturnValue({
|
|
62
|
+
data: undefined,
|
|
63
|
+
isLoading: false,
|
|
64
|
+
error: null,
|
|
65
|
+
})
|
|
66
|
+
runStartRecommendationSession.mockImplementation(async () => {
|
|
67
|
+
cookies.current['vtex-rec-user-id'] = 'user-from-session'
|
|
68
|
+
cookies.current['vtex-rec-user-start-session'] = '1'
|
|
69
|
+
return true
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
afterEach(() => {
|
|
74
|
+
vi.clearAllMocks()
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
describe('useRecommendationShelf session fallback race', () => {
|
|
78
|
+
it('starts the session in parallel with cookie retry so the first page can fetch', async () => {
|
|
79
|
+
renderHook(() => useRecommendationShelf({ campaignVrn: TOP_ITEMS_VRN }))
|
|
80
|
+
|
|
81
|
+
await waitFor(() => {
|
|
82
|
+
expect(runStartRecommendationSession).toHaveBeenCalledTimes(1)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
await waitFor(() => {
|
|
86
|
+
expect(lastArgs()).toEqual({
|
|
87
|
+
userId: 'user-from-session',
|
|
88
|
+
campaignVrn: TOP_ITEMS_VRN,
|
|
89
|
+
products: [],
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('does not start a session when the user id cookie is already present', async () => {
|
|
95
|
+
cookies.current['vtex-rec-user-id'] = 'existing-user'
|
|
96
|
+
cookies.current['vtex-rec-user-start-session'] = '1'
|
|
97
|
+
|
|
98
|
+
renderHook(() => useRecommendationShelf({ campaignVrn: TOP_ITEMS_VRN }))
|
|
99
|
+
|
|
100
|
+
await waitFor(() => {
|
|
101
|
+
expect(lastArgs()).toEqual({
|
|
102
|
+
userId: 'existing-user',
|
|
103
|
+
campaignVrn: TOP_ITEMS_VRN,
|
|
104
|
+
products: [],
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
expect(runStartRecommendationSession).not.toHaveBeenCalled()
|
|
109
|
+
})
|
|
110
|
+
})
|
|
@@ -20,6 +20,50 @@ describe('getDefaultMyAccountSections', () => {
|
|
|
20
20
|
it('returns an empty list for store (unknown) content-types', () => {
|
|
21
21
|
expect(getDefaultMyAccountSections('myAccountWishlist')).toEqual([])
|
|
22
22
|
})
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The order-details defaults must mirror the render order of the
|
|
26
|
+
* `@deprecated OrderDetails.tsx` component, which is the design reference
|
|
27
|
+
* available in the repo. The delivery option accordions travel with
|
|
28
|
+
* AccountOrderDelivery, which is the single known deviation.
|
|
29
|
+
*/
|
|
30
|
+
it('orders the myAccountOrderDetails defaults like the deprecated component', () => {
|
|
31
|
+
expect(
|
|
32
|
+
getDefaultMyAccountSections('myAccountOrderDetails').map(
|
|
33
|
+
(section) => section.$componentKey
|
|
34
|
+
)
|
|
35
|
+
).toEqual([
|
|
36
|
+
'AccountNavigation',
|
|
37
|
+
'AccountOrderDetails',
|
|
38
|
+
'AccountOrderOrderedBy',
|
|
39
|
+
'AccountOrderDelivery',
|
|
40
|
+
'AccountOrderStatus',
|
|
41
|
+
'AccountOrderPayment',
|
|
42
|
+
'AccountOrderSummary',
|
|
43
|
+
'AccountOrderMoreInfo',
|
|
44
|
+
'AccountOrderBudgets',
|
|
45
|
+
])
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('keeps the myAccountOrderDetails default section set unchanged', () => {
|
|
49
|
+
const keys = getDefaultMyAccountSections('myAccountOrderDetails').map(
|
|
50
|
+
(section) => section.$componentKey
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
expect(new Set(keys)).toEqual(
|
|
54
|
+
new Set([
|
|
55
|
+
'AccountNavigation',
|
|
56
|
+
'AccountOrderDetails',
|
|
57
|
+
'AccountOrderStatus',
|
|
58
|
+
'AccountOrderPayment',
|
|
59
|
+
'AccountOrderDelivery',
|
|
60
|
+
'AccountOrderSummary',
|
|
61
|
+
'AccountOrderOrderedBy',
|
|
62
|
+
'AccountOrderBudgets',
|
|
63
|
+
'AccountOrderMoreInfo',
|
|
64
|
+
])
|
|
65
|
+
)
|
|
66
|
+
})
|
|
23
67
|
})
|
|
24
68
|
|
|
25
69
|
describe('withDefaultMyAccountSections', () => {
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
type CmsSection = {
|
|
2
|
-
name?: string
|
|
3
|
-
$componentKey?: string
|
|
4
|
-
data?: {
|
|
5
|
-
enableRecommendations?: boolean
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function pushSections(target: CmsSection[], value: unknown) {
|
|
10
|
-
if (Array.isArray(value)) {
|
|
11
|
-
for (const item of value) {
|
|
12
|
-
if (item && typeof item === 'object') {
|
|
13
|
-
target.push(item as CmsSection)
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
return
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (value && typeof value === 'object') {
|
|
20
|
-
const sections = (value as { sections?: unknown }).sections
|
|
21
|
-
if (Array.isArray(sections)) {
|
|
22
|
-
pushSections(target, sections)
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Returns true when the current page CMS data includes at least one
|
|
29
|
-
* `RecommendationShelf` with `enableRecommendations: true`.
|
|
30
|
-
*/
|
|
31
|
-
export function hasEnabledRecommendationShelf(pageProps: unknown): boolean {
|
|
32
|
-
if (!pageProps || typeof pageProps !== 'object') {
|
|
33
|
-
return false
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const props = pageProps as Record<string, unknown>
|
|
37
|
-
const sections: CmsSection[] = []
|
|
38
|
-
|
|
39
|
-
pushSections(sections, props.sections)
|
|
40
|
-
pushSections(sections, props.globalSections)
|
|
41
|
-
pushSections(sections, props.page)
|
|
42
|
-
|
|
43
|
-
return sections.some(
|
|
44
|
-
(section) =>
|
|
45
|
-
(section.name === 'RecommendationShelf' ||
|
|
46
|
-
section.$componentKey === 'RecommendationShelf') &&
|
|
47
|
-
section.data?.enableRecommendations === true
|
|
48
|
-
)
|
|
49
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
|
|
3
|
-
import { hasEnabledRecommendationShelf } from 'src/sdk/analytics/utils/hasEnabledRecommendationShelf'
|
|
4
|
-
|
|
5
|
-
describe('hasEnabledRecommendationShelf', () => {
|
|
6
|
-
it('returns false for empty or unrelated page props', () => {
|
|
7
|
-
expect(hasEnabledRecommendationShelf(undefined)).toBe(false)
|
|
8
|
-
expect(hasEnabledRecommendationShelf({})).toBe(false)
|
|
9
|
-
expect(
|
|
10
|
-
hasEnabledRecommendationShelf({
|
|
11
|
-
sections: [{ name: 'ProductShelf', data: {} }],
|
|
12
|
-
})
|
|
13
|
-
).toBe(false)
|
|
14
|
-
})
|
|
15
|
-
|
|
16
|
-
it('returns true when a page section enables recommendations', () => {
|
|
17
|
-
expect(
|
|
18
|
-
hasEnabledRecommendationShelf({
|
|
19
|
-
sections: [
|
|
20
|
-
{
|
|
21
|
-
name: 'RecommendationShelf',
|
|
22
|
-
data: { enableRecommendations: true, campaignVrn: 'vrn:x' },
|
|
23
|
-
},
|
|
24
|
-
],
|
|
25
|
-
})
|
|
26
|
-
).toBe(true)
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('returns true when a global section enables recommendations', () => {
|
|
30
|
-
expect(
|
|
31
|
-
hasEnabledRecommendationShelf({
|
|
32
|
-
globalSections: {
|
|
33
|
-
sections: [
|
|
34
|
-
{
|
|
35
|
-
$componentKey: 'RecommendationShelf',
|
|
36
|
-
data: { enableRecommendations: true },
|
|
37
|
-
},
|
|
38
|
-
],
|
|
39
|
-
},
|
|
40
|
-
})
|
|
41
|
-
).toBe(true)
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
it('returns true when page.sections enables recommendations', () => {
|
|
45
|
-
expect(
|
|
46
|
-
hasEnabledRecommendationShelf({
|
|
47
|
-
page: {
|
|
48
|
-
sections: [
|
|
49
|
-
{
|
|
50
|
-
name: 'RecommendationShelf',
|
|
51
|
-
data: { enableRecommendations: true },
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
},
|
|
55
|
-
})
|
|
56
|
-
).toBe(true)
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('returns false when the shelf is present but enableRecommendations is false', () => {
|
|
60
|
-
expect(
|
|
61
|
-
hasEnabledRecommendationShelf({
|
|
62
|
-
sections: [
|
|
63
|
-
{
|
|
64
|
-
name: 'RecommendationShelf',
|
|
65
|
-
data: { enableRecommendations: false },
|
|
66
|
-
},
|
|
67
|
-
],
|
|
68
|
-
})
|
|
69
|
-
).toBe(false)
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
it('returns true when at least one of multiple shelves enables recommendations', () => {
|
|
73
|
-
expect(
|
|
74
|
-
hasEnabledRecommendationShelf({
|
|
75
|
-
sections: [
|
|
76
|
-
{
|
|
77
|
-
name: 'RecommendationShelf',
|
|
78
|
-
data: { enableRecommendations: false },
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
name: 'RecommendationShelf',
|
|
82
|
-
data: { enableRecommendations: true },
|
|
83
|
-
},
|
|
84
|
-
],
|
|
85
|
-
})
|
|
86
|
-
).toBe(true)
|
|
87
|
-
})
|
|
88
|
-
})
|