@salesforce/retail-react-app 6.0.0 → 6.1.0-dev
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/CHANGELOG.md +10 -0
- package/app/components/_app/index.jsx +13 -2
- package/app/components/_app/index.test.js +73 -29
- package/app/components/breadcrumb/index.jsx +2 -2
- package/app/components/links-list/index.test.js +0 -2
- package/app/components/product-view-modal/bundle.test.js +6 -1
- package/app/components/recommended-products/index.jsx +9 -0
- package/app/components/store-locator-modal/store-locator-content.test.jsx +0 -1
- package/app/hooks/use-datacloud.js +481 -0
- package/app/hooks/use-datacloud.test.js +164 -0
- package/app/mocks/datacloud-mock-data.js +404 -0
- package/app/pages/account/index.jsx +3 -0
- package/app/pages/account/index.test.js +36 -36
- package/app/pages/home/index.jsx +3 -0
- package/app/pages/login/index.jsx +3 -0
- package/app/pages/product-detail/index.jsx +16 -2
- package/app/pages/product-list/index.jsx +15 -1
- package/app/pages/registration/index.jsx +3 -0
- package/app/pages/reset-password/index.jsx +3 -0
- package/app/ssr.js +3 -1
- package/config/default.js +4 -0
- package/config/mocks/default.js +4 -0
- package/jest-setup.js +10 -0
- package/jest.config.js +5 -1
- package/package.json +15 -9
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025, salesforce.com, inc.
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
|
+
*/
|
|
7
|
+
import {expect} from '@jest/globals'
|
|
8
|
+
|
|
9
|
+
export const mockLoginViewPageEvent = {
|
|
10
|
+
events: [
|
|
11
|
+
expect.objectContaining({
|
|
12
|
+
guestId: 'guest-usid',
|
|
13
|
+
siteId: 'RefArch',
|
|
14
|
+
sessionId: expect.any(String),
|
|
15
|
+
deviceId: expect.any(String),
|
|
16
|
+
dateTime: expect.any(String),
|
|
17
|
+
customerId: 1234567890,
|
|
18
|
+
eventId: expect.any(String),
|
|
19
|
+
eventType: 'identity',
|
|
20
|
+
category: 'Profile',
|
|
21
|
+
isAnonymous: 0,
|
|
22
|
+
firstName: 'John',
|
|
23
|
+
lastName: 'Smith',
|
|
24
|
+
sourceUrl: '/login'
|
|
25
|
+
}),
|
|
26
|
+
expect.objectContaining({
|
|
27
|
+
guestId: 'guest-usid',
|
|
28
|
+
siteId: 'RefArch',
|
|
29
|
+
sessionId: expect.any(String),
|
|
30
|
+
deviceId: expect.any(String),
|
|
31
|
+
dateTime: expect.any(String),
|
|
32
|
+
customerId: 1234567890,
|
|
33
|
+
eventId: expect.any(String),
|
|
34
|
+
eventType: 'userEngagement',
|
|
35
|
+
category: 'Engagement',
|
|
36
|
+
interactionName: 'page-view',
|
|
37
|
+
sourceUrl: '/login'
|
|
38
|
+
})
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const mockLoginViewPageEventDNT = {
|
|
43
|
+
events: [
|
|
44
|
+
expect.objectContaining({
|
|
45
|
+
guestId: '__DNT__',
|
|
46
|
+
siteId: 'RefArch',
|
|
47
|
+
sessionId: '__DNT__',
|
|
48
|
+
deviceId: '__DNT__',
|
|
49
|
+
dateTime: expect.any(String),
|
|
50
|
+
customerId: '__DNT__',
|
|
51
|
+
customerNo: '__DNT__',
|
|
52
|
+
eventId: expect.any(String),
|
|
53
|
+
eventType: 'userEngagement',
|
|
54
|
+
category: 'Engagement',
|
|
55
|
+
interactionName: 'page-view',
|
|
56
|
+
sourceUrl: '/login'
|
|
57
|
+
})
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const mockViewProductEvent = {
|
|
62
|
+
events: [
|
|
63
|
+
expect.objectContaining({
|
|
64
|
+
guestId: 'guest-usid',
|
|
65
|
+
siteId: 'RefArch',
|
|
66
|
+
sessionId: expect.any(String),
|
|
67
|
+
deviceId: expect.any(String),
|
|
68
|
+
dateTime: expect.any(String),
|
|
69
|
+
customerId: 1234567890,
|
|
70
|
+
eventId: expect.any(String),
|
|
71
|
+
eventType: 'identity',
|
|
72
|
+
category: 'Profile',
|
|
73
|
+
isAnonymous: 0,
|
|
74
|
+
firstName: 'John',
|
|
75
|
+
lastName: 'Smith'
|
|
76
|
+
}),
|
|
77
|
+
expect.objectContaining({
|
|
78
|
+
guestId: 'guest-usid',
|
|
79
|
+
siteId: 'RefArch',
|
|
80
|
+
sessionId: expect.any(String),
|
|
81
|
+
deviceId: expect.any(String),
|
|
82
|
+
dateTime: expect.any(String),
|
|
83
|
+
customerId: 1234567890,
|
|
84
|
+
eventId: expect.any(String),
|
|
85
|
+
eventType: 'contactPointEmail',
|
|
86
|
+
category: 'Profile',
|
|
87
|
+
email: 'johnsmith@salesforce.com'
|
|
88
|
+
}),
|
|
89
|
+
expect.objectContaining({
|
|
90
|
+
guestId: 'guest-usid',
|
|
91
|
+
siteId: 'RefArch',
|
|
92
|
+
sessionId: expect.any(String),
|
|
93
|
+
deviceId: expect.any(String),
|
|
94
|
+
dateTime: expect.any(String),
|
|
95
|
+
customerId: 1234567890,
|
|
96
|
+
eventId: expect.any(String),
|
|
97
|
+
eventType: 'catalog',
|
|
98
|
+
category: 'Engagement',
|
|
99
|
+
id: '56736828M',
|
|
100
|
+
type: 'Product',
|
|
101
|
+
webStoreId: 'pwa',
|
|
102
|
+
interactionName: 'catalog-object-view-start'
|
|
103
|
+
})
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const mockCategorySearchParams = {
|
|
108
|
+
limit: 25,
|
|
109
|
+
offset: 0,
|
|
110
|
+
sort: 'best-matches'
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const mockViewCategoryEvent = {
|
|
114
|
+
events: [
|
|
115
|
+
expect.objectContaining({
|
|
116
|
+
guestId: 'guest-usid',
|
|
117
|
+
siteId: 'RefArch',
|
|
118
|
+
sessionId: expect.any(String),
|
|
119
|
+
deviceId: expect.any(String),
|
|
120
|
+
dateTime: expect.any(String),
|
|
121
|
+
customerId: 1234567890,
|
|
122
|
+
eventId: expect.any(String),
|
|
123
|
+
eventType: 'identity',
|
|
124
|
+
category: 'Profile',
|
|
125
|
+
isAnonymous: 0,
|
|
126
|
+
firstName: 'John',
|
|
127
|
+
lastName: 'Smith'
|
|
128
|
+
}),
|
|
129
|
+
expect.objectContaining({
|
|
130
|
+
guestId: 'guest-usid',
|
|
131
|
+
siteId: 'RefArch',
|
|
132
|
+
sessionId: expect.any(String),
|
|
133
|
+
deviceId: expect.any(String),
|
|
134
|
+
dateTime: expect.any(String),
|
|
135
|
+
customerId: 1234567890,
|
|
136
|
+
eventId: expect.any(String),
|
|
137
|
+
eventType: 'catalog',
|
|
138
|
+
category: 'Engagement',
|
|
139
|
+
searchResultTitle: undefined,
|
|
140
|
+
searchResultPosition: 0,
|
|
141
|
+
searchResultPageNumber: 1,
|
|
142
|
+
id: '25752986M',
|
|
143
|
+
type: 'Product',
|
|
144
|
+
webStoreId: 'pwa',
|
|
145
|
+
categoryId: 'mens-accessories-ties',
|
|
146
|
+
interactionName: 'catalog-object-impression'
|
|
147
|
+
}),
|
|
148
|
+
expect.objectContaining({
|
|
149
|
+
guestId: 'guest-usid',
|
|
150
|
+
siteId: 'RefArch',
|
|
151
|
+
sessionId: expect.any(String),
|
|
152
|
+
deviceId: expect.any(String),
|
|
153
|
+
dateTime: expect.any(String),
|
|
154
|
+
customerId: 1234567890,
|
|
155
|
+
eventId: expect.any(String),
|
|
156
|
+
eventType: 'catalog',
|
|
157
|
+
category: 'Engagement',
|
|
158
|
+
searchResultTitle: undefined,
|
|
159
|
+
searchResultPosition: 0,
|
|
160
|
+
searchResultPageNumber: 1,
|
|
161
|
+
id: '25752235M',
|
|
162
|
+
type: 'Product',
|
|
163
|
+
webStoreId: 'pwa',
|
|
164
|
+
categoryId: 'mens-accessories-ties',
|
|
165
|
+
interactionName: 'catalog-object-impression'
|
|
166
|
+
}),
|
|
167
|
+
expect.objectContaining({
|
|
168
|
+
guestId: 'guest-usid',
|
|
169
|
+
siteId: 'RefArch',
|
|
170
|
+
sessionId: expect.any(String),
|
|
171
|
+
deviceId: expect.any(String),
|
|
172
|
+
dateTime: expect.any(String),
|
|
173
|
+
customerId: 1234567890,
|
|
174
|
+
eventId: expect.any(String),
|
|
175
|
+
eventType: 'catalog',
|
|
176
|
+
category: 'Engagement',
|
|
177
|
+
searchResultTitle: undefined,
|
|
178
|
+
searchResultPosition: 0,
|
|
179
|
+
searchResultPageNumber: 1,
|
|
180
|
+
id: '25752218M',
|
|
181
|
+
type: 'Product',
|
|
182
|
+
webStoreId: 'pwa',
|
|
183
|
+
categoryId: 'mens-accessories-ties',
|
|
184
|
+
interactionName: 'catalog-object-impression'
|
|
185
|
+
}),
|
|
186
|
+
expect.objectContaining({
|
|
187
|
+
guestId: 'guest-usid',
|
|
188
|
+
siteId: 'RefArch',
|
|
189
|
+
sessionId: expect.any(String),
|
|
190
|
+
deviceId: expect.any(String),
|
|
191
|
+
dateTime: expect.any(String),
|
|
192
|
+
customerId: 1234567890,
|
|
193
|
+
eventId: expect.any(String),
|
|
194
|
+
eventType: 'catalog',
|
|
195
|
+
category: 'Engagement',
|
|
196
|
+
searchResultTitle: undefined,
|
|
197
|
+
searchResultPosition: 0,
|
|
198
|
+
searchResultPageNumber: 1,
|
|
199
|
+
id: '25752981M',
|
|
200
|
+
type: 'Product',
|
|
201
|
+
webStoreId: 'pwa',
|
|
202
|
+
categoryId: 'mens-accessories-ties',
|
|
203
|
+
interactionName: 'catalog-object-impression'
|
|
204
|
+
})
|
|
205
|
+
]
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export const mockSearchParam = {
|
|
209
|
+
limit: 25,
|
|
210
|
+
offset: 0,
|
|
211
|
+
q: 'oxford glove',
|
|
212
|
+
sort: 'best-matches'
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export const mockGloveSearchResult = {
|
|
216
|
+
limit: 1,
|
|
217
|
+
hits: [
|
|
218
|
+
{
|
|
219
|
+
currency: 'GBP',
|
|
220
|
+
hitType: 'master',
|
|
221
|
+
image: {
|
|
222
|
+
alt: "Men's Oxford Gloves, , large",
|
|
223
|
+
disBaseLink:
|
|
224
|
+
'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwb69853b8/images/large/TG250_206.jpg',
|
|
225
|
+
link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dwb69853b8/images/large/TG250_206.jpg',
|
|
226
|
+
title: "Men's Oxford Gloves, "
|
|
227
|
+
},
|
|
228
|
+
price: 63.99,
|
|
229
|
+
pricePerUnit: 63.99,
|
|
230
|
+
priceRanges: [
|
|
231
|
+
{
|
|
232
|
+
maxPrice: 63.99,
|
|
233
|
+
minPrice: 63.99,
|
|
234
|
+
pricebook: 'gbp-m-list-prices'
|
|
235
|
+
}
|
|
236
|
+
],
|
|
237
|
+
productId: 'TG250M',
|
|
238
|
+
productName: "Men's Oxford Gloves",
|
|
239
|
+
productType: {
|
|
240
|
+
master: true
|
|
241
|
+
},
|
|
242
|
+
c_productUrl: 'https://pwa-kit.mobify-storefront.com/global/en-GB/product/TG250M'
|
|
243
|
+
}
|
|
244
|
+
],
|
|
245
|
+
query: 'oxford glove',
|
|
246
|
+
refinements: [
|
|
247
|
+
{
|
|
248
|
+
attributeId: 'cgid',
|
|
249
|
+
label: 'Category',
|
|
250
|
+
values: [
|
|
251
|
+
{
|
|
252
|
+
hitCount: 1,
|
|
253
|
+
label: 'Mens',
|
|
254
|
+
value: 'mens',
|
|
255
|
+
values: [
|
|
256
|
+
{
|
|
257
|
+
hitCount: 1,
|
|
258
|
+
label: 'Accessories',
|
|
259
|
+
value: 'mens-accessories',
|
|
260
|
+
values: [
|
|
261
|
+
{
|
|
262
|
+
hitCount: 1,
|
|
263
|
+
label: 'Gloves',
|
|
264
|
+
value: 'mens-accessories-gloves'
|
|
265
|
+
}
|
|
266
|
+
]
|
|
267
|
+
}
|
|
268
|
+
]
|
|
269
|
+
}
|
|
270
|
+
]
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
attributeId: 'c_refinementColor',
|
|
274
|
+
label: 'Colour',
|
|
275
|
+
values: [
|
|
276
|
+
{
|
|
277
|
+
hitCount: 0,
|
|
278
|
+
label: 'Beige',
|
|
279
|
+
presentationId: 'beige',
|
|
280
|
+
value: 'Beige'
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
hitCount: 0,
|
|
284
|
+
label: 'Black',
|
|
285
|
+
presentationId: 'black',
|
|
286
|
+
value: 'Black'
|
|
287
|
+
}
|
|
288
|
+
]
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
attributeId: 'price',
|
|
292
|
+
label: 'Price',
|
|
293
|
+
values: [
|
|
294
|
+
{
|
|
295
|
+
hitCount: 1,
|
|
296
|
+
label: '£50 - £99.99',
|
|
297
|
+
value: '(50..100)'
|
|
298
|
+
}
|
|
299
|
+
]
|
|
300
|
+
}
|
|
301
|
+
],
|
|
302
|
+
selectedSortingOption: 'best-matches',
|
|
303
|
+
sortingOptions: [
|
|
304
|
+
{
|
|
305
|
+
id: 'best-matches',
|
|
306
|
+
label: 'Best Matches'
|
|
307
|
+
}
|
|
308
|
+
],
|
|
309
|
+
offset: 0,
|
|
310
|
+
total: 1
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export const mockViewSearchResultsEvent = {
|
|
314
|
+
events: [
|
|
315
|
+
expect.objectContaining({
|
|
316
|
+
guestId: 'guest-usid',
|
|
317
|
+
siteId: 'RefArch',
|
|
318
|
+
sessionId: expect.any(String),
|
|
319
|
+
deviceId: expect.any(String),
|
|
320
|
+
dateTime: expect.any(String),
|
|
321
|
+
customerId: 1234567890,
|
|
322
|
+
eventId: expect.any(String),
|
|
323
|
+
eventType: 'identity',
|
|
324
|
+
category: 'Profile',
|
|
325
|
+
isAnonymous: 0,
|
|
326
|
+
firstName: 'John',
|
|
327
|
+
lastName: 'Smith'
|
|
328
|
+
}),
|
|
329
|
+
expect.objectContaining({
|
|
330
|
+
guestId: 'guest-usid',
|
|
331
|
+
siteId: 'RefArch',
|
|
332
|
+
sessionId: expect.any(String),
|
|
333
|
+
deviceId: expect.any(String),
|
|
334
|
+
dateTime: expect.any(String),
|
|
335
|
+
customerId: 1234567890,
|
|
336
|
+
eventId: expect.any(String),
|
|
337
|
+
eventType: 'catalog',
|
|
338
|
+
category: 'Engagement',
|
|
339
|
+
searchResultTitle: 'oxford glove',
|
|
340
|
+
searchResultPosition: 0,
|
|
341
|
+
searchResultPageNumber: 1,
|
|
342
|
+
searchResultId: expect.any(String),
|
|
343
|
+
id: 'TG250M',
|
|
344
|
+
type: 'Product',
|
|
345
|
+
webStoreId: 'pwa',
|
|
346
|
+
interactionName: 'catalog-object-impression'
|
|
347
|
+
})
|
|
348
|
+
]
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export const mockRecommendationIds = [{id: '11111111'}, {id: '22222222'}]
|
|
352
|
+
|
|
353
|
+
export const mockViewRecommendationsEvent = {
|
|
354
|
+
events: [
|
|
355
|
+
expect.objectContaining({
|
|
356
|
+
guestId: 'guest-usid',
|
|
357
|
+
siteId: 'RefArch',
|
|
358
|
+
sessionId: expect.any(String),
|
|
359
|
+
deviceId: expect.any(String),
|
|
360
|
+
dateTime: expect.any(String),
|
|
361
|
+
customerId: 1234567890,
|
|
362
|
+
eventId: expect.any(String),
|
|
363
|
+
eventType: 'identity',
|
|
364
|
+
category: 'Profile',
|
|
365
|
+
isAnonymous: 0,
|
|
366
|
+
firstName: 'John',
|
|
367
|
+
lastName: 'Smith'
|
|
368
|
+
}),
|
|
369
|
+
expect.objectContaining({
|
|
370
|
+
guestId: 'guest-usid',
|
|
371
|
+
siteId: 'RefArch',
|
|
372
|
+
sessionId: expect.any(String),
|
|
373
|
+
deviceId: expect.any(String),
|
|
374
|
+
dateTime: expect.any(String),
|
|
375
|
+
customerId: 1234567890,
|
|
376
|
+
eventId: expect.any(String),
|
|
377
|
+
eventType: 'catalog',
|
|
378
|
+
category: 'Engagement',
|
|
379
|
+
id: '11111111',
|
|
380
|
+
type: 'Product',
|
|
381
|
+
webStoreId: 'pwa',
|
|
382
|
+
interactionName: 'catalog-object-impression',
|
|
383
|
+
personalizationId: 'testRecommender',
|
|
384
|
+
personalizationContextId: '883360544021M'
|
|
385
|
+
}),
|
|
386
|
+
expect.objectContaining({
|
|
387
|
+
guestId: 'guest-usid',
|
|
388
|
+
siteId: 'RefArch',
|
|
389
|
+
sessionId: expect.any(String),
|
|
390
|
+
deviceId: expect.any(String),
|
|
391
|
+
dateTime: expect.any(String),
|
|
392
|
+
customerId: 1234567890,
|
|
393
|
+
eventId: expect.any(String),
|
|
394
|
+
eventType: 'catalog',
|
|
395
|
+
category: 'Engagement',
|
|
396
|
+
id: '22222222',
|
|
397
|
+
type: 'Product',
|
|
398
|
+
webStoreId: 'pwa',
|
|
399
|
+
interactionName: 'catalog-object-impression',
|
|
400
|
+
personalizationId: 'testRecommender',
|
|
401
|
+
personalizationContextId: '883360544021M'
|
|
402
|
+
})
|
|
403
|
+
]
|
|
404
|
+
}
|
|
@@ -41,6 +41,7 @@ import useNavigation from '@salesforce/retail-react-app/app/hooks/use-navigation
|
|
|
41
41
|
import LoadingSpinner from '@salesforce/retail-react-app/app/components/loading-spinner'
|
|
42
42
|
import useMultiSite from '@salesforce/retail-react-app/app/hooks/use-multi-site'
|
|
43
43
|
import useEinstein from '@salesforce/retail-react-app/app/hooks/use-einstein'
|
|
44
|
+
import useDataCloud from '@salesforce/retail-react-app/app/hooks/use-datacloud'
|
|
44
45
|
import {useAuthHelper, AuthHelpers} from '@salesforce/commerce-sdk-react'
|
|
45
46
|
import {useCurrentCustomer} from '@salesforce/retail-react-app/app/hooks/use-current-customer'
|
|
46
47
|
import {isHydrated} from '@salesforce/retail-react-app/app/utils/utils'
|
|
@@ -94,11 +95,13 @@ const Account = () => {
|
|
|
94
95
|
const [showLoading, setShowLoading] = useState(false)
|
|
95
96
|
|
|
96
97
|
const einstein = useEinstein()
|
|
98
|
+
const dataCloud = useDataCloud()
|
|
97
99
|
|
|
98
100
|
const {buildUrl} = useMultiSite()
|
|
99
101
|
/**************** Einstein ****************/
|
|
100
102
|
useEffect(() => {
|
|
101
103
|
einstein.sendViewPage(location.pathname)
|
|
104
|
+
dataCloud.sendViewPage(location.pathname)
|
|
102
105
|
}, [location])
|
|
103
106
|
|
|
104
107
|
const onSignoutClick = async () => {
|
|
@@ -20,11 +20,12 @@ import {
|
|
|
20
20
|
mockOrderProducts,
|
|
21
21
|
mockPasswordUpdateFalure
|
|
22
22
|
} from '@salesforce/retail-react-app/app/mocks/mock-data'
|
|
23
|
+
import {useCustomerType} from '@salesforce/commerce-sdk-react'
|
|
23
24
|
import Account from '@salesforce/retail-react-app/app/pages/account/index'
|
|
24
25
|
import Login from '@salesforce/retail-react-app/app/pages/login'
|
|
25
26
|
import mockConfig from '@salesforce/retail-react-app/config/mocks/default'
|
|
26
|
-
import * as sdk from '@salesforce/commerce-sdk-react'
|
|
27
27
|
|
|
28
|
+
jest.setTimeout(60000)
|
|
28
29
|
jest.mock('@salesforce/commerce-sdk-react', () => ({
|
|
29
30
|
...jest.requireActual('@salesforce/commerce-sdk-react'),
|
|
30
31
|
useCustomerType: jest.fn()
|
|
@@ -73,7 +74,7 @@ beforeEach(() => {
|
|
|
73
74
|
})
|
|
74
75
|
afterEach(() => {
|
|
75
76
|
jest.resetModules()
|
|
76
|
-
|
|
77
|
+
jest.restoreAllMocks()
|
|
77
78
|
})
|
|
78
79
|
|
|
79
80
|
const expectedBasePath = '/uk/en-GB'
|
|
@@ -86,50 +87,45 @@ describe('Test redirects', function () {
|
|
|
86
87
|
)
|
|
87
88
|
})
|
|
88
89
|
test('Redirects to login page if the customer is not logged in', async () => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return (
|
|
92
|
-
<Switch>
|
|
93
|
-
<Route
|
|
94
|
-
path={createPathWithDefaults('/account')}
|
|
95
|
-
render={(props) => <Account {...props} />}
|
|
96
|
-
/>
|
|
97
|
-
</Switch>
|
|
98
|
-
)
|
|
99
|
-
}
|
|
100
|
-
renderWithProviders(<Component />, {
|
|
90
|
+
useCustomerType.mockReturnValue({isRegistered: false, isGuest: true})
|
|
91
|
+
renderWithProviders(<MockedComponent />, {
|
|
101
92
|
wrapperProps: {siteAlias: 'uk', appConfig: mockConfig.app, isGuest: true}
|
|
102
93
|
})
|
|
103
94
|
await waitFor(() => expect(window.location.pathname).toBe(`${expectedBasePath}/login`))
|
|
104
95
|
})
|
|
105
96
|
})
|
|
106
|
-
|
|
107
|
-
test('
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
97
|
+
describe('Page Navigation', () => {
|
|
98
|
+
test('works for subpages', async () => {
|
|
99
|
+
useCustomerType.mockReturnValue({isRegistered: true, isGuest: false})
|
|
100
|
+
global.server.use(
|
|
101
|
+
rest.get('*/products', (req, res, ctx) => {
|
|
102
|
+
return res(ctx.delay(0), ctx.json(mockOrderProducts))
|
|
103
|
+
}),
|
|
104
|
+
rest.get('*/customers/:customerId/orders', (req, res, ctx) => {
|
|
105
|
+
return res(ctx.delay(0), ctx.json(mockOrderHistory))
|
|
106
|
+
})
|
|
107
|
+
)
|
|
108
|
+
const {user} = renderWithProviders(<MockedComponent />, {
|
|
109
|
+
wrapperProps: {siteAlias: 'uk', appConfig: mockConfig.app}
|
|
115
110
|
})
|
|
116
|
-
|
|
117
|
-
const {user} = renderWithProviders(<MockedComponent />, {
|
|
118
|
-
wrapperProps: {siteAlias: 'uk', appConfig: mockConfig.app}
|
|
119
|
-
})
|
|
120
|
-
expect(await screen.findByTestId('account-page')).toBeInTheDocument()
|
|
111
|
+
expect(await screen.findByTestId('account-page')).toBeInTheDocument()
|
|
121
112
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
113
|
+
const nav = within(screen.getByTestId('account-detail-nav'))
|
|
114
|
+
await user.click(nav.getByText('Addresses'))
|
|
115
|
+
await waitFor(() =>
|
|
116
|
+
expect(window.location.pathname).toBe(`${expectedBasePath}/account/addresses`)
|
|
117
|
+
)
|
|
118
|
+
await user.click(nav.getByText('Order History'))
|
|
119
|
+
await waitFor(() =>
|
|
120
|
+
expect(window.location.pathname).toBe(`${expectedBasePath}/account/orders`)
|
|
121
|
+
)
|
|
122
|
+
})
|
|
129
123
|
})
|
|
130
124
|
|
|
131
125
|
describe('Render and logs out', function () {
|
|
132
126
|
test('Renders account detail page by default for logged-in customer, and can log out', async () => {
|
|
127
|
+
useCustomerType.mockReturnValue({isRegistered: true, isGuest: false})
|
|
128
|
+
|
|
133
129
|
const {user} = renderWithProviders(<MockedComponent />)
|
|
134
130
|
|
|
135
131
|
// Render user profile page
|
|
@@ -145,7 +141,10 @@ describe('Render and logs out', function () {
|
|
|
145
141
|
})
|
|
146
142
|
|
|
147
143
|
await user.click(screen.getAllByText(/Log Out/)[0])
|
|
144
|
+
useCustomerType.mockReturnValue({isRegistered: false, isGuest: true})
|
|
145
|
+
|
|
148
146
|
await waitFor(() => {
|
|
147
|
+
expect(window.location.pathname).toBe(`${expectedBasePath}/login`)
|
|
149
148
|
expect(screen.getByTestId('login-page')).toBeInTheDocument()
|
|
150
149
|
})
|
|
151
150
|
})
|
|
@@ -166,7 +165,7 @@ describe('updating profile', function () {
|
|
|
166
165
|
)
|
|
167
166
|
})
|
|
168
167
|
test('Allows customer to edit profile details', async () => {
|
|
169
|
-
|
|
168
|
+
useCustomerType.mockReturnValue({isRegistered: true, isExternal: false})
|
|
170
169
|
const {user} = renderWithProviders(<MockedComponent />)
|
|
171
170
|
expect(await screen.findByTestId('account-page')).toBeInTheDocument()
|
|
172
171
|
expect(await screen.findByTestId('account-detail-page')).toBeInTheDocument()
|
|
@@ -190,6 +189,7 @@ describe('updating profile', function () {
|
|
|
190
189
|
|
|
191
190
|
describe('updating password', function () {
|
|
192
191
|
beforeEach(() => {
|
|
192
|
+
useCustomerType.mockReturnValue({isRegistered: true, isExternal: false})
|
|
193
193
|
global.server.use(
|
|
194
194
|
rest.post('*/oauth2/token', (req, res, ctx) =>
|
|
195
195
|
res(
|
package/app/pages/home/index.jsx
CHANGED
|
@@ -35,6 +35,7 @@ import {heroFeatures, features} from '@salesforce/retail-react-app/app/pages/hom
|
|
|
35
35
|
|
|
36
36
|
//Hooks
|
|
37
37
|
import useEinstein from '@salesforce/retail-react-app/app/hooks/use-einstein'
|
|
38
|
+
import useDataCloud from '@salesforce/retail-react-app/app/hooks/use-datacloud'
|
|
38
39
|
|
|
39
40
|
// Constants
|
|
40
41
|
import {
|
|
@@ -55,6 +56,7 @@ import {useProductSearch} from '@salesforce/commerce-sdk-react'
|
|
|
55
56
|
const Home = () => {
|
|
56
57
|
const intl = useIntl()
|
|
57
58
|
const einstein = useEinstein()
|
|
59
|
+
const dataCloud = useDataCloud()
|
|
58
60
|
const {pathname} = useLocation()
|
|
59
61
|
|
|
60
62
|
const {res} = useServerContext()
|
|
@@ -79,6 +81,7 @@ const Home = () => {
|
|
|
79
81
|
/**************** Einstein ****************/
|
|
80
82
|
useEffect(() => {
|
|
81
83
|
einstein.sendViewPage(pathname)
|
|
84
|
+
dataCloud.sendViewPage(pathname)
|
|
82
85
|
}, [])
|
|
83
86
|
|
|
84
87
|
return (
|
|
@@ -23,6 +23,7 @@ import {useForm} from 'react-hook-form'
|
|
|
23
23
|
import {useRouteMatch} from 'react-router'
|
|
24
24
|
import {useLocation} from 'react-router-dom'
|
|
25
25
|
import useEinstein from '@salesforce/retail-react-app/app/hooks/use-einstein'
|
|
26
|
+
import useDataCloud from '@salesforce/retail-react-app/app/hooks/use-datacloud'
|
|
26
27
|
import LoginForm from '@salesforce/retail-react-app/app/components/login'
|
|
27
28
|
import PasswordlessEmailConfirmation from '@salesforce/retail-react-app/app/components/email-confirmation/index'
|
|
28
29
|
import {
|
|
@@ -56,6 +57,7 @@ const Login = ({initialView = LOGIN_VIEW}) => {
|
|
|
56
57
|
const queryParams = new URLSearchParams(location.search)
|
|
57
58
|
const {path} = useRouteMatch()
|
|
58
59
|
const einstein = useEinstein()
|
|
60
|
+
const dataCloud = useDataCloud()
|
|
59
61
|
const {isRegistered, customerType} = useCustomerType()
|
|
60
62
|
const login = useAuthHelper(AuthHelpers.LoginRegisteredUserB2C)
|
|
61
63
|
const loginPasswordless = useAuthHelper(AuthHelpers.LoginPasswordlessUser)
|
|
@@ -184,6 +186,7 @@ const Login = ({initialView = LOGIN_VIEW}) => {
|
|
|
184
186
|
/**************** Einstein ****************/
|
|
185
187
|
useEffect(() => {
|
|
186
188
|
einstein.sendViewPage(location.pathname)
|
|
189
|
+
dataCloud.sendViewPage(location.pathname)
|
|
187
190
|
}, [])
|
|
188
191
|
|
|
189
192
|
return (
|
|
@@ -31,6 +31,7 @@ import {useCurrentBasket} from '@salesforce/retail-react-app/app/hooks/use-curre
|
|
|
31
31
|
import {useVariant} from '@salesforce/retail-react-app/app/hooks'
|
|
32
32
|
import useNavigation from '@salesforce/retail-react-app/app/hooks/use-navigation'
|
|
33
33
|
import useEinstein from '@salesforce/retail-react-app/app/hooks/use-einstein'
|
|
34
|
+
import useDataCloud from '@salesforce/retail-react-app/app/hooks/use-datacloud'
|
|
34
35
|
import useActiveData from '@salesforce/retail-react-app/app/hooks/use-active-data'
|
|
35
36
|
import {useServerContext} from '@salesforce/pwa-kit-react-sdk/ssr/universal/hooks'
|
|
36
37
|
// Project Components
|
|
@@ -61,6 +62,7 @@ const ProductDetail = () => {
|
|
|
61
62
|
const history = useHistory()
|
|
62
63
|
const location = useLocation()
|
|
63
64
|
const einstein = useEinstein()
|
|
65
|
+
const dataCloud = useDataCloud()
|
|
64
66
|
const activeData = useActiveData()
|
|
65
67
|
const toast = useToast()
|
|
66
68
|
const navigate = useNavigation()
|
|
@@ -99,7 +101,8 @@ const ProductDetail = () => {
|
|
|
99
101
|
'prices',
|
|
100
102
|
'variations',
|
|
101
103
|
'set_products',
|
|
102
|
-
'bundled_products'
|
|
104
|
+
'bundled_products',
|
|
105
|
+
'page_meta_tags'
|
|
103
106
|
],
|
|
104
107
|
allImages: true
|
|
105
108
|
}
|
|
@@ -422,6 +425,7 @@ const ProductDetail = () => {
|
|
|
422
425
|
useEffect(() => {
|
|
423
426
|
if (product && product.type.set) {
|
|
424
427
|
einstein.sendViewProduct(product)
|
|
428
|
+
dataCloud.sendViewProduct(product)
|
|
425
429
|
const childrenProducts = product.setProducts
|
|
426
430
|
childrenProducts.map((child) => {
|
|
427
431
|
try {
|
|
@@ -433,6 +437,7 @@ const ProductDetail = () => {
|
|
|
433
437
|
})
|
|
434
438
|
}
|
|
435
439
|
activeData.sendViewProduct(category, child, 'detail')
|
|
440
|
+
dataCloud.sendViewProduct(child)
|
|
436
441
|
})
|
|
437
442
|
} else if (product) {
|
|
438
443
|
try {
|
|
@@ -444,6 +449,7 @@ const ProductDetail = () => {
|
|
|
444
449
|
})
|
|
445
450
|
}
|
|
446
451
|
activeData.sendViewProduct(category, product, 'detail')
|
|
452
|
+
dataCloud.sendViewProduct(product)
|
|
447
453
|
}
|
|
448
454
|
}, [product])
|
|
449
455
|
|
|
@@ -455,7 +461,15 @@ const ProductDetail = () => {
|
|
|
455
461
|
>
|
|
456
462
|
<Helmet>
|
|
457
463
|
<title>{product?.pageTitle}</title>
|
|
458
|
-
|
|
464
|
+
{product?.pageMetaTags?.length > 0 &&
|
|
465
|
+
product.pageMetaTags.map(({id, value}) => (
|
|
466
|
+
<meta name={id} content={value} key={id} />
|
|
467
|
+
))}
|
|
468
|
+
{/* Fallback for description if not included in pageMetaTags */}
|
|
469
|
+
{!product?.pageMetaTags?.some((tag) => tag.id === 'description') &&
|
|
470
|
+
product?.pageDescription && (
|
|
471
|
+
<meta name="description" content={product.pageDescription} />
|
|
472
|
+
)}
|
|
459
473
|
</Helmet>
|
|
460
474
|
|
|
461
475
|
<Stack spacing={16}>
|