@salesforce/retail-react-app 1.0.0-preview.0

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 (425) hide show
  1. package/.eslintignore +7 -0
  2. package/.eslintrc.js +25 -0
  3. package/.prettierignore +4 -0
  4. package/.prettierrc.yaml +7 -0
  5. package/CHANGELOG.md +173 -0
  6. package/LICENSE +14 -0
  7. package/README.md +48 -0
  8. package/app/assets/svg/account.svg +3 -0
  9. package/app/assets/svg/alert.svg +3 -0
  10. package/app/assets/svg/basket.svg +3 -0
  11. package/app/assets/svg/brand-logo.svg +10 -0
  12. package/app/assets/svg/cc-amex.svg +7 -0
  13. package/app/assets/svg/cc-cvv.svg +8 -0
  14. package/app/assets/svg/cc-discover.svg +14 -0
  15. package/app/assets/svg/cc-mastercard.svg +8 -0
  16. package/app/assets/svg/cc-visa.svg +11 -0
  17. package/app/assets/svg/check-circle.svg +3 -0
  18. package/app/assets/svg/check.svg +3 -0
  19. package/app/assets/svg/chevron-down.svg +3 -0
  20. package/app/assets/svg/chevron-left.svg +3 -0
  21. package/app/assets/svg/chevron-right.svg +3 -0
  22. package/app/assets/svg/chevron-up.svg +3 -0
  23. package/app/assets/svg/close.svg +3 -0
  24. package/app/assets/svg/dashboard.svg +4 -0
  25. package/app/assets/svg/figma-logo.svg +14 -0
  26. package/app/assets/svg/file.svg +3 -0
  27. package/app/assets/svg/filter.svg +3 -0
  28. package/app/assets/svg/flag-ca.svg +5 -0
  29. package/app/assets/svg/flag-cn.svg +19 -0
  30. package/app/assets/svg/flag-fr.svg +19 -0
  31. package/app/assets/svg/flag-gb.svg +16 -0
  32. package/app/assets/svg/flag-it.svg +29 -0
  33. package/app/assets/svg/flag-jp.svg +10 -0
  34. package/app/assets/svg/flag-us.svg +7 -0
  35. package/app/assets/svg/github-logo.svg +40 -0
  36. package/app/assets/svg/hamburger.svg +8 -0
  37. package/app/assets/svg/heart-solid.svg +7 -0
  38. package/app/assets/svg/heart.svg +3 -0
  39. package/app/assets/svg/info.svg +3 -0
  40. package/app/assets/svg/like.svg +4 -0
  41. package/app/assets/svg/location.svg +3 -0
  42. package/app/assets/svg/lock.svg +3 -0
  43. package/app/assets/svg/paypal.svg +19 -0
  44. package/app/assets/svg/plug.svg +3 -0
  45. package/app/assets/svg/plus.svg +3 -0
  46. package/app/assets/svg/receipt.svg +3 -0
  47. package/app/assets/svg/search.svg +8 -0
  48. package/app/assets/svg/signout.svg +3 -0
  49. package/app/assets/svg/social-facebook.svg +3 -0
  50. package/app/assets/svg/social-instagram.svg +3 -0
  51. package/app/assets/svg/social-pinterest.svg +4 -0
  52. package/app/assets/svg/social-twitter.svg +3 -0
  53. package/app/assets/svg/social-youtube.svg +3 -0
  54. package/app/assets/svg/user.svg +3 -0
  55. package/app/assets/svg/visibility-off.svg +5 -0
  56. package/app/assets/svg/visibility.svg +3 -0
  57. package/app/components/_app/index.jsx +401 -0
  58. package/app/components/_app/index.test.js +85 -0
  59. package/app/components/_app/partials/above-header.jsx +10 -0
  60. package/app/components/_app-config/index.jsx +125 -0
  61. package/app/components/_app-config/index.test.js +77 -0
  62. package/app/components/_error/index.jsx +142 -0
  63. package/app/components/_error/index.test.js +25 -0
  64. package/app/components/action-card/index.jsx +75 -0
  65. package/app/components/address-display/index.jsx +30 -0
  66. package/app/components/basic-tile/index.jsx +65 -0
  67. package/app/components/basic-tile/index.test.js +23 -0
  68. package/app/components/breadcrumb/index.jsx +67 -0
  69. package/app/components/breadcrumb/index.test.js +30 -0
  70. package/app/components/confirmation-modal/index.jsx +111 -0
  71. package/app/components/confirmation-modal/index.test.js +98 -0
  72. package/app/components/drawer-menu/index.jsx +405 -0
  73. package/app/components/drawer-menu/index.test.js +33 -0
  74. package/app/components/dynamic-image/index.jsx +56 -0
  75. package/app/components/field/index.jsx +161 -0
  76. package/app/components/footer/index.jsx +269 -0
  77. package/app/components/footer/index.test.js +22 -0
  78. package/app/components/forms/address-fields.jsx +49 -0
  79. package/app/components/forms/credit-card-fields.jsx +149 -0
  80. package/app/components/forms/form-action-buttons.jsx +55 -0
  81. package/app/components/forms/login-fields.jsx +31 -0
  82. package/app/components/forms/password-requirements.jsx +99 -0
  83. package/app/components/forms/post-checkout-registration-fields.jsx +43 -0
  84. package/app/components/forms/profile-fields.jsx +36 -0
  85. package/app/components/forms/promo-code-fields.jsx +43 -0
  86. package/app/components/forms/registration-fields.jsx +42 -0
  87. package/app/components/forms/reset-password-fields.jsx +31 -0
  88. package/app/components/forms/state-province-options.jsx +75 -0
  89. package/app/components/forms/update-password-fields.jsx +49 -0
  90. package/app/components/forms/useAddressFields.jsx +196 -0
  91. package/app/components/forms/useCreditCardFields.jsx +146 -0
  92. package/app/components/forms/useLoginFields.jsx +52 -0
  93. package/app/components/forms/useProfileFields.jsx +95 -0
  94. package/app/components/forms/usePromoCodeFields.jsx +39 -0
  95. package/app/components/forms/useRegistrationFields.jsx +136 -0
  96. package/app/components/forms/useResetPasswordFields.jsx +40 -0
  97. package/app/components/forms/useUpdatePasswordFields.jsx +89 -0
  98. package/app/components/header/index.jsx +290 -0
  99. package/app/components/header/index.test.js +217 -0
  100. package/app/components/hero/index.jsx +84 -0
  101. package/app/components/hero/index.test.js +40 -0
  102. package/app/components/icons/index.jsx +158 -0
  103. package/app/components/icons/index.test.js +20 -0
  104. package/app/components/image-gallery/index.jsx +176 -0
  105. package/app/components/image-gallery/index.test.js +485 -0
  106. package/app/components/item-variant/index.jsx +33 -0
  107. package/app/components/item-variant/item-attributes.jsx +107 -0
  108. package/app/components/item-variant/item-image.jsx +73 -0
  109. package/app/components/item-variant/item-name.jsx +28 -0
  110. package/app/components/item-variant/item-price.jsx +117 -0
  111. package/app/components/link/index.jsx +32 -0
  112. package/app/components/link/index.test.js +72 -0
  113. package/app/components/links-list/index.jsx +89 -0
  114. package/app/components/links-list/index.test.js +62 -0
  115. package/app/components/list-menu/index.jsx +280 -0
  116. package/app/components/list-menu/index.test.js +44 -0
  117. package/app/components/loading-spinner/index.jsx +46 -0
  118. package/app/components/locale-selector/index.jsx +124 -0
  119. package/app/components/locale-selector/index.test.js +37 -0
  120. package/app/components/locale-text/index.jsx +97 -0
  121. package/app/components/locale-text/index.test.js +36 -0
  122. package/app/components/login/index.jsx +96 -0
  123. package/app/components/nested-accordion/index.jsx +185 -0
  124. package/app/components/nested-accordion/index.test.js +98 -0
  125. package/app/components/offline-banner/index.jsx +40 -0
  126. package/app/components/offline-banner/index.test.js +15 -0
  127. package/app/components/offline-boundary/index.jsx +104 -0
  128. package/app/components/offline-boundary/index.test.js +123 -0
  129. package/app/components/order-summary/index.jsx +331 -0
  130. package/app/components/page-action-placeholder/index.jsx +50 -0
  131. package/app/components/pagination/index.jsx +134 -0
  132. package/app/components/pagination/index.test.js +25 -0
  133. package/app/components/product-item/index.jsx +146 -0
  134. package/app/components/product-item/index.test.js +38 -0
  135. package/app/components/product-scroller/index.jsx +172 -0
  136. package/app/components/product-scroller/index.test.js +98 -0
  137. package/app/components/product-tile/index.jsx +195 -0
  138. package/app/components/product-tile/index.test.js +96 -0
  139. package/app/components/product-view/index.jsx +538 -0
  140. package/app/components/product-view/index.test.js +224 -0
  141. package/app/components/product-view-modal/index.jsx +48 -0
  142. package/app/components/product-view-modal/index.test.js +72 -0
  143. package/app/components/promo-code/index.jsx +162 -0
  144. package/app/components/promo-popover/index.jsx +83 -0
  145. package/app/components/quantity-picker/index.jsx +58 -0
  146. package/app/components/radio-card/index.jsx +75 -0
  147. package/app/components/recommended-products/index.jsx +227 -0
  148. package/app/components/register/index.jsx +114 -0
  149. package/app/components/reset-password/index.jsx +87 -0
  150. package/app/components/responsive/index.jsx +29 -0
  151. package/app/components/scroll-to-top/index.jsx +24 -0
  152. package/app/components/scroll-to-top/index.test.js +46 -0
  153. package/app/components/search/index.jsx +279 -0
  154. package/app/components/search/index.test.js +127 -0
  155. package/app/components/search/partials/recent-searches.jsx +76 -0
  156. package/app/components/search/partials/search-suggestions.jsx +45 -0
  157. package/app/components/search/partials/suggestions.jsx +43 -0
  158. package/app/components/section/index.jsx +68 -0
  159. package/app/components/seo/index.jsx +33 -0
  160. package/app/components/social-icons/index.jsx +101 -0
  161. package/app/components/social-icons/index.test.js +30 -0
  162. package/app/components/swatch-group/index.jsx +77 -0
  163. package/app/components/swatch-group/index.test.js +136 -0
  164. package/app/components/swatch-group/swatch.jsx +94 -0
  165. package/app/components/toggle-card/index.jsx +97 -0
  166. package/app/components/with-registration/index.jsx +58 -0
  167. package/app/components/with-registration/index.test.js +85 -0
  168. package/app/constants.js +109 -0
  169. package/app/contexts/index.js +92 -0
  170. package/app/hooks/einstein-mock-data.js +916 -0
  171. package/app/hooks/index.js +17 -0
  172. package/app/hooks/use-add-to-cart-modal.js +344 -0
  173. package/app/hooks/use-add-to-cart-modal.test.js +625 -0
  174. package/app/hooks/use-auth-modal.js +337 -0
  175. package/app/hooks/use-auth-modal.test.js +365 -0
  176. package/app/hooks/use-currency.js +20 -0
  177. package/app/hooks/use-currency.test.js +41 -0
  178. package/app/hooks/use-current-basket.js +39 -0
  179. package/app/hooks/use-current-customer.js +29 -0
  180. package/app/hooks/use-derived-product.js +77 -0
  181. package/app/hooks/use-derived-product.test.js +69 -0
  182. package/app/hooks/use-einstein.js +512 -0
  183. package/app/hooks/use-einstein.test.js +224 -0
  184. package/app/hooks/use-intersection-observer.js +64 -0
  185. package/app/hooks/use-limit-urls.js +31 -0
  186. package/app/hooks/use-limit-urls.test.js +40 -0
  187. package/app/hooks/use-multi-site.js +36 -0
  188. package/app/hooks/use-multi-site.test.js +53 -0
  189. package/app/hooks/use-navigation.js +37 -0
  190. package/app/hooks/use-navigation.test.js +109 -0
  191. package/app/hooks/use-page-urls.js +35 -0
  192. package/app/hooks/use-page-urls.test.js +39 -0
  193. package/app/hooks/use-pdp-search-params.js +16 -0
  194. package/app/hooks/use-pdp-search-params.test.js +52 -0
  195. package/app/hooks/use-previous.js +17 -0
  196. package/app/hooks/use-product-view-modal.js +93 -0
  197. package/app/hooks/use-product-view-modal.test.js +172 -0
  198. package/app/hooks/use-search-params.js +96 -0
  199. package/app/hooks/use-search-params.test.js +91 -0
  200. package/app/hooks/use-sort-urls.js +33 -0
  201. package/app/hooks/use-sort-urls.test.js +42 -0
  202. package/app/hooks/use-toast.js +68 -0
  203. package/app/hooks/use-toast.test.js +58 -0
  204. package/app/hooks/use-variant.js +32 -0
  205. package/app/hooks/use-variant.test.js +81 -0
  206. package/app/hooks/use-variation-attributes.js +138 -0
  207. package/app/hooks/use-variation-attributes.test.js +119 -0
  208. package/app/hooks/use-variation-params.js +31 -0
  209. package/app/hooks/use-variation-params.test.js +73 -0
  210. package/app/hooks/use-wish-list.js +42 -0
  211. package/app/main.jsx +14 -0
  212. package/app/mocks/basket-with-suit.js +146 -0
  213. package/app/mocks/empty-basket.js +39 -0
  214. package/app/mocks/mock-data.js +5632 -0
  215. package/app/mocks/product-set-winter-lookM.js +1224 -0
  216. package/app/mocks/searchResults.js +144 -0
  217. package/app/mocks/variant-750518699578M.js +434 -0
  218. package/app/page-designer/README.md +102 -0
  219. package/app/page-designer/assets/image-tile/index.jsx +51 -0
  220. package/app/page-designer/assets/image-tile/index.test.js +30 -0
  221. package/app/page-designer/assets/image-with-text/index.jsx +140 -0
  222. package/app/page-designer/assets/image-with-text/index.test.js +38 -0
  223. package/app/page-designer/assets/index.js +9 -0
  224. package/app/page-designer/index.js +10 -0
  225. package/app/page-designer/layouts/carousel/index.jsx +222 -0
  226. package/app/page-designer/layouts/carousel/index.test.js +43 -0
  227. package/app/page-designer/layouts/index.js +14 -0
  228. package/app/page-designer/layouts/mobileGrid1r1c/index.jsx +36 -0
  229. package/app/page-designer/layouts/mobileGrid1r1c/index.test.js +35 -0
  230. package/app/page-designer/layouts/mobileGrid2r1c/index.jsx +37 -0
  231. package/app/page-designer/layouts/mobileGrid2r1c/index.test.js +47 -0
  232. package/app/page-designer/layouts/mobileGrid2r2c/index.jsx +37 -0
  233. package/app/page-designer/layouts/mobileGrid2r2c/index.test.js +71 -0
  234. package/app/page-designer/layouts/mobileGrid2r3c/index.jsx +37 -0
  235. package/app/page-designer/layouts/mobileGrid2r3c/index.test.js +95 -0
  236. package/app/page-designer/layouts/mobileGrid3r1c/index.jsx +37 -0
  237. package/app/page-designer/layouts/mobileGrid3r1c/index.test.js +59 -0
  238. package/app/page-designer/layouts/mobileGrid3r2c/index.jsx +37 -0
  239. package/app/page-designer/layouts/mobileGrid3r2c/index.test.js +95 -0
  240. package/app/page-designer/utils.js +14 -0
  241. package/app/pages/account/addresses.jsx +382 -0
  242. package/app/pages/account/addresses.test.js +120 -0
  243. package/app/pages/account/constant.js +57 -0
  244. package/app/pages/account/index.jsx +237 -0
  245. package/app/pages/account/index.test.js +188 -0
  246. package/app/pages/account/order-detail.jsx +397 -0
  247. package/app/pages/account/order-history.jsx +264 -0
  248. package/app/pages/account/orders.jsx +30 -0
  249. package/app/pages/account/orders.test.js +95 -0
  250. package/app/pages/account/profile.jsx +357 -0
  251. package/app/pages/account/wishlist/index.jsx +195 -0
  252. package/app/pages/account/wishlist/index.mock.js +1481 -0
  253. package/app/pages/account/wishlist/index.test.js +56 -0
  254. package/app/pages/account/wishlist/partials/wishlist-primary-action.jsx +170 -0
  255. package/app/pages/account/wishlist/partials/wishlist-primary-action.mock.js +1623 -0
  256. package/app/pages/account/wishlist/partials/wishlist-primary-action.test.js +99 -0
  257. package/app/pages/account/wishlist/partials/wishlist-secondary-button-group.jsx +120 -0
  258. package/app/pages/account/wishlist/partials/wishlist-secondary-button-group.test.js +391 -0
  259. package/app/pages/cart/index.jsx +476 -0
  260. package/app/pages/cart/index.test.js +481 -0
  261. package/app/pages/cart/partials/cart-cta.jsx +46 -0
  262. package/app/pages/cart/partials/cart-secondary-button-group.jsx +135 -0
  263. package/app/pages/cart/partials/cart-secondary-button-group.test.js +103 -0
  264. package/app/pages/cart/partials/cart-skeleton.jsx +93 -0
  265. package/app/pages/cart/partials/cart-title.jsx +27 -0
  266. package/app/pages/cart/partials/empty-cart.jsx +86 -0
  267. package/app/pages/checkout/confirmation.jsx +541 -0
  268. package/app/pages/checkout/confirmation.mock.js +450 -0
  269. package/app/pages/checkout/confirmation.test.js +114 -0
  270. package/app/pages/checkout/index.jsx +169 -0
  271. package/app/pages/checkout/index.test.js +582 -0
  272. package/app/pages/checkout/partials/cc-radio-group.jsx +122 -0
  273. package/app/pages/checkout/partials/checkout-footer.jsx +140 -0
  274. package/app/pages/checkout/partials/checkout-footer.test.js +16 -0
  275. package/app/pages/checkout/partials/checkout-header.jsx +54 -0
  276. package/app/pages/checkout/partials/checkout-header.test.js +16 -0
  277. package/app/pages/checkout/partials/checkout-skeleton.jsx +52 -0
  278. package/app/pages/checkout/partials/contact-info.jsx +251 -0
  279. package/app/pages/checkout/partials/contact-info.test.js +43 -0
  280. package/app/pages/checkout/partials/payment-form.jsx +97 -0
  281. package/app/pages/checkout/partials/payment.jsx +276 -0
  282. package/app/pages/checkout/partials/shipping-address-selection.jsx +377 -0
  283. package/app/pages/checkout/partials/shipping-address.jsx +132 -0
  284. package/app/pages/checkout/partials/shipping-options.jsx +232 -0
  285. package/app/pages/checkout/util/checkout-context.js +94 -0
  286. package/app/pages/home/data.js +134 -0
  287. package/app/pages/home/index.jsx +301 -0
  288. package/app/pages/home/index.test.js +23 -0
  289. package/app/pages/login/index.jsx +123 -0
  290. package/app/pages/login/index.test.js +229 -0
  291. package/app/pages/login-redirect/index.jsx +23 -0
  292. package/app/pages/login-redirect/index.test.js +16 -0
  293. package/app/pages/page-not-found/index.jsx +90 -0
  294. package/app/pages/page-not-found/index.test.js +31 -0
  295. package/app/pages/product-detail/index.jsx +394 -0
  296. package/app/pages/product-detail/index.mock.js +197 -0
  297. package/app/pages/product-detail/index.test.js +162 -0
  298. package/app/pages/product-detail/partials/information-accordion.jsx +121 -0
  299. package/app/pages/product-list/index.jsx +735 -0
  300. package/app/pages/product-list/index.test.js +180 -0
  301. package/app/pages/product-list/partials/above-page-header.jsx +10 -0
  302. package/app/pages/product-list/partials/checkbox-refinements.jsx +41 -0
  303. package/app/pages/product-list/partials/checkbox-refinements.test.js +53 -0
  304. package/app/pages/product-list/partials/color-refinements.jsx +88 -0
  305. package/app/pages/product-list/partials/empty-results.jsx +118 -0
  306. package/app/pages/product-list/partials/link-refinements.jsx +38 -0
  307. package/app/pages/product-list/partials/page-header.jsx +42 -0
  308. package/app/pages/product-list/partials/radio-refinements.jsx +60 -0
  309. package/app/pages/product-list/partials/refinements.jsx +144 -0
  310. package/app/pages/product-list/partials/selected-refinements.jsx +100 -0
  311. package/app/pages/product-list/partials/size-refinements.jsx +55 -0
  312. package/app/pages/registration/index.jsx +87 -0
  313. package/app/pages/registration/index.test.jsx +132 -0
  314. package/app/pages/reset-password/index.jsx +112 -0
  315. package/app/pages/reset-password/index.test.jsx +141 -0
  316. package/app/request-processor.js +118 -0
  317. package/app/request-processor.test.js +23 -0
  318. package/app/routes.jsx +111 -0
  319. package/app/routes.test.js +13 -0
  320. package/app/ssr.js +70 -0
  321. package/app/static/ico/favicon.ico +0 -0
  322. package/app/static/img/global/app-icon-192.png +0 -0
  323. package/app/static/img/global/app-icon-512.png +0 -0
  324. package/app/static/img/global/apple-touch-icon.png +0 -0
  325. package/app/static/img/hero.png +0 -0
  326. package/app/static/manifest.json +19 -0
  327. package/app/static/robots.txt +2 -0
  328. package/app/theme/components/base/accordion.js +21 -0
  329. package/app/theme/components/base/alert.js +17 -0
  330. package/app/theme/components/base/badge.js +25 -0
  331. package/app/theme/components/base/button.js +77 -0
  332. package/app/theme/components/base/checkbox.js +30 -0
  333. package/app/theme/components/base/container.js +17 -0
  334. package/app/theme/components/base/drawer.js +26 -0
  335. package/app/theme/components/base/formLabel.js +13 -0
  336. package/app/theme/components/base/icon.js +13 -0
  337. package/app/theme/components/base/input.js +44 -0
  338. package/app/theme/components/base/modal.js +11 -0
  339. package/app/theme/components/base/popover.js +61 -0
  340. package/app/theme/components/base/radio.js +33 -0
  341. package/app/theme/components/base/select.js +15 -0
  342. package/app/theme/components/base/skeleton.js +12 -0
  343. package/app/theme/components/base/tooltip.js +19 -0
  344. package/app/theme/components/project/_app.js +25 -0
  345. package/app/theme/components/project/breadcrumb.js +25 -0
  346. package/app/theme/components/project/checkout-footer.js +35 -0
  347. package/app/theme/components/project/drawer-menu.js +66 -0
  348. package/app/theme/components/project/footer.js +84 -0
  349. package/app/theme/components/project/header.js +84 -0
  350. package/app/theme/components/project/image-gallery.js +59 -0
  351. package/app/theme/components/project/links-list.js +43 -0
  352. package/app/theme/components/project/list-menu.js +91 -0
  353. package/app/theme/components/project/locale-selector.js +42 -0
  354. package/app/theme/components/project/nested-accordion.js +26 -0
  355. package/app/theme/components/project/offline-banner.js +25 -0
  356. package/app/theme/components/project/pagination.js +22 -0
  357. package/app/theme/components/project/product-tile.js +32 -0
  358. package/app/theme/components/project/social-icons.js +52 -0
  359. package/app/theme/components/project/swatch-group.js +115 -0
  360. package/app/theme/foundations/colors.js +170 -0
  361. package/app/theme/foundations/gradients.js +9 -0
  362. package/app/theme/foundations/layerStyles.js +41 -0
  363. package/app/theme/foundations/shadows.js +9 -0
  364. package/app/theme/foundations/sizes.js +18 -0
  365. package/app/theme/foundations/space.js +9 -0
  366. package/app/theme/foundations/styles.js +21 -0
  367. package/app/theme/index.js +104 -0
  368. package/app/utils/cc-utils.js +112 -0
  369. package/app/utils/cc-utils.test.js +41 -0
  370. package/app/utils/image-groups-utils.js +62 -0
  371. package/app/utils/image-groups-utils.test.js +65 -0
  372. package/app/utils/locale.js +78 -0
  373. package/app/utils/locale.test.js +112 -0
  374. package/app/utils/password-utils.js +21 -0
  375. package/app/utils/phone-utils.js +22 -0
  376. package/app/utils/phone-utils.test.js +15 -0
  377. package/app/utils/product-utils.js +35 -0
  378. package/app/utils/product-utils.test.js +51 -0
  379. package/app/utils/responsive-image.js +198 -0
  380. package/app/utils/responsive-image.test.js +170 -0
  381. package/app/utils/routes-utils.js +111 -0
  382. package/app/utils/routes-utils.test.js +291 -0
  383. package/app/utils/site-utils.js +222 -0
  384. package/app/utils/site-utils.test.js +376 -0
  385. package/app/utils/test-utils.js +257 -0
  386. package/app/utils/url.js +291 -0
  387. package/app/utils/url.test.js +421 -0
  388. package/app/utils/utils.js +201 -0
  389. package/app/utils/utils.test.js +182 -0
  390. package/babel.config.js +7 -0
  391. package/cache-hash-config.json +8 -0
  392. package/config/default.js +64 -0
  393. package/config/mocks/default.js +131 -0
  394. package/config/sites.js +78 -0
  395. package/jest-setup.js +191 -0
  396. package/jest.config.js +50 -0
  397. package/jsconfig.json +13 -0
  398. package/package.json +105 -0
  399. package/scripts/extract-default-messages.js +92 -0
  400. package/tests/lighthouserc.js +37 -0
  401. package/translations/README.md +127 -0
  402. package/translations/compiled/de-DE.json +3212 -0
  403. package/translations/compiled/en-GB.json +3212 -0
  404. package/translations/compiled/en-US.json +3212 -0
  405. package/translations/compiled/en-XA.json +6948 -0
  406. package/translations/compiled/es-MX.json +3216 -0
  407. package/translations/compiled/fr-FR.json +3216 -0
  408. package/translations/compiled/it-IT.json +3188 -0
  409. package/translations/compiled/ja-JP.json +3200 -0
  410. package/translations/compiled/ko-KR.json +3180 -0
  411. package/translations/compiled/pt-BR.json +3220 -0
  412. package/translations/compiled/zh-CN.json +3212 -0
  413. package/translations/compiled/zh-TW.json +3208 -0
  414. package/translations/de-DE.json +1417 -0
  415. package/translations/en-GB.json +1417 -0
  416. package/translations/en-US.json +1417 -0
  417. package/translations/es-MX.json +1417 -0
  418. package/translations/fr-FR.json +1417 -0
  419. package/translations/it-IT.json +1417 -0
  420. package/translations/ja-JP.json +1417 -0
  421. package/translations/ko-KR.json +1417 -0
  422. package/translations/pt-BR.json +1417 -0
  423. package/translations/zh-CN.json +1417 -0
  424. package/translations/zh-TW.json +1417 -0
  425. package/worker/main.js +36 -0
@@ -0,0 +1,301 @@
1
+ /*
2
+ * Copyright (c) 2022, Salesforce, 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
+
8
+ import React, {useEffect} from 'react'
9
+ import {useIntl, FormattedMessage} from 'react-intl'
10
+ import {useLocation} from 'react-router-dom'
11
+
12
+ // Components
13
+ import {
14
+ Box,
15
+ Button,
16
+ SimpleGrid,
17
+ HStack,
18
+ VStack,
19
+ Text,
20
+ Flex,
21
+ Stack,
22
+ Container,
23
+ Link
24
+ } from '@chakra-ui/react'
25
+
26
+ // Project Components
27
+ import Hero from '@salesforce/retail-react-app/app/components/hero'
28
+ import Seo from '@salesforce/retail-react-app/app/components/seo'
29
+ import Section from '@salesforce/retail-react-app/app/components/section'
30
+ import ProductScroller from '@salesforce/retail-react-app/app/components/product-scroller'
31
+
32
+ // Others
33
+ import {getAssetUrl} from '@salesforce/pwa-kit-react-sdk/ssr/universal/utils'
34
+ import {heroFeatures, features} from '@salesforce/retail-react-app/app/pages/home/data'
35
+
36
+ //Hooks
37
+ import useEinstein from '@salesforce/retail-react-app/app/hooks/use-einstein'
38
+
39
+ // Constants
40
+ import {
41
+ MAX_CACHE_AGE,
42
+ HOME_SHOP_PRODUCTS_CATEGORY_ID,
43
+ HOME_SHOP_PRODUCTS_LIMIT
44
+ } from '@salesforce/retail-react-app/app/constants'
45
+ import {useServerContext} from '@salesforce/pwa-kit-react-sdk/ssr/universal/hooks'
46
+ import {useProductSearch} from '@salesforce/commerce-sdk-react'
47
+
48
+ /**
49
+ * This is the home page for Retail React App.
50
+ * The page is created for demonstration purposes.
51
+ * The page renders SEO metadata and a few promotion
52
+ * categories and products, data is from local file.
53
+ */
54
+ const Home = () => {
55
+ const intl = useIntl()
56
+ const einstein = useEinstein()
57
+ const {pathname} = useLocation()
58
+
59
+ const {res} = useServerContext()
60
+ if (res) {
61
+ res.set('Cache-Control', `max-age=${MAX_CACHE_AGE}`)
62
+ }
63
+
64
+ const {data: productSearchResult, isLoading} = useProductSearch({
65
+ parameters: {
66
+ refine: [`cgid=${HOME_SHOP_PRODUCTS_CATEGORY_ID}`, 'htype=master'],
67
+ limit: HOME_SHOP_PRODUCTS_LIMIT
68
+ }
69
+ })
70
+
71
+ /**************** Einstein ****************/
72
+ useEffect(() => {
73
+ einstein.sendViewPage(pathname)
74
+ }, [])
75
+
76
+ return (
77
+ <Box data-testid="home-page" layerStyle="page">
78
+ <Seo
79
+ title="Home Page"
80
+ description="Commerce Cloud Retail React App"
81
+ keywords="Commerce Cloud, Retail React App, React Storefront"
82
+ />
83
+
84
+ <Hero
85
+ title={intl.formatMessage({
86
+ defaultMessage: 'The React PWA Starter Store for Retail',
87
+ id: 'home.title.react_starter_store'
88
+ })}
89
+ img={{
90
+ src: getAssetUrl('static/img/hero.png'),
91
+ alt: 'npx pwa-kit-create-app'
92
+ }}
93
+ actions={
94
+ <Stack spacing={{base: 4, sm: 6}} direction={{base: 'column', sm: 'row'}}>
95
+ <Button
96
+ as={Link}
97
+ href="https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/getting-started.html"
98
+ target="_blank"
99
+ width={{base: 'full', md: 'inherit'}}
100
+ paddingX={7}
101
+ _hover={{textDecoration: 'none'}}
102
+ >
103
+ <FormattedMessage
104
+ defaultMessage="Get started"
105
+ id="home.link.get_started"
106
+ />
107
+ </Button>
108
+ </Stack>
109
+ }
110
+ />
111
+
112
+ <Section
113
+ background={'gray.50'}
114
+ marginX="auto"
115
+ paddingY={{base: 8, md: 16}}
116
+ paddingX={{base: 4, md: 8}}
117
+ borderRadius="base"
118
+ width={{base: '100vw', md: 'inherit'}}
119
+ position={{base: 'relative', md: 'inherit'}}
120
+ left={{base: '50%', md: 'inherit'}}
121
+ right={{base: '50%', md: 'inherit'}}
122
+ marginLeft={{base: '-50vw', md: 'auto'}}
123
+ marginRight={{base: '-50vw', md: 'auto'}}
124
+ >
125
+ <SimpleGrid
126
+ columns={{base: 1, md: 1, lg: 3}}
127
+ spacingX={{base: 1, md: 4}}
128
+ spacingY={{base: 4, md: 14}}
129
+ >
130
+ {heroFeatures.map((feature, index) => {
131
+ const featureMessage = feature.message
132
+ return (
133
+ <Box
134
+ key={index}
135
+ background={'white'}
136
+ boxShadow={'0px 2px 2px rgba(0, 0, 0, 0.1)'}
137
+ borderRadius={'4px'}
138
+ >
139
+ <Link target="_blank" href={feature.href}>
140
+ <HStack>
141
+ <Flex
142
+ paddingLeft={6}
143
+ height={24}
144
+ align={'center'}
145
+ justify={'center'}
146
+ >
147
+ {feature.icon}
148
+ </Flex>
149
+ <Text fontWeight="700">
150
+ {intl.formatMessage(featureMessage.title)}
151
+ </Text>
152
+ </HStack>
153
+ </Link>
154
+ </Box>
155
+ )
156
+ })}
157
+ </SimpleGrid>
158
+ </Section>
159
+
160
+ {productSearchResult && (
161
+ <Section
162
+ padding={4}
163
+ paddingTop={16}
164
+ title={intl.formatMessage({
165
+ defaultMessage: 'Shop Products',
166
+ id: 'home.heading.shop_products'
167
+ })}
168
+ subtitle={intl.formatMessage(
169
+ {
170
+ defaultMessage:
171
+ 'This section contains content from the catalog. {docLink} on how to replace it.',
172
+ id: 'home.description.shop_products',
173
+ description:
174
+ '{docLink} is a html button that links the user to https://sfdc.co/business-manager-manage-catalogs'
175
+ },
176
+ {
177
+ docLink: (
178
+ <Link
179
+ target="_blank"
180
+ href={'https://sfdc.co/business-manager-manage-catalogs'}
181
+ textDecoration={'none'}
182
+ position={'relative'}
183
+ _after={{
184
+ position: 'absolute',
185
+ content: `""`,
186
+ height: '2px',
187
+ bottom: '-2px',
188
+ margin: '0 auto',
189
+ left: 0,
190
+ right: 0,
191
+ background: 'gray.700'
192
+ }}
193
+ _hover={{textDecoration: 'none'}}
194
+ >
195
+ {intl.formatMessage({
196
+ defaultMessage: 'Read docs',
197
+ id: 'home.link.read_docs'
198
+ })}
199
+ </Link>
200
+ )
201
+ }
202
+ )}
203
+ >
204
+ <Stack pt={8} spacing={16}>
205
+ <ProductScroller
206
+ products={productSearchResult?.hits}
207
+ isLoading={isLoading}
208
+ />
209
+ </Stack>
210
+ </Section>
211
+ )}
212
+
213
+ <Section
214
+ padding={4}
215
+ paddingTop={32}
216
+ title={intl.formatMessage({
217
+ defaultMessage: 'Features',
218
+ id: 'home.heading.features'
219
+ })}
220
+ subtitle={intl.formatMessage({
221
+ defaultMessage:
222
+ 'Out-of-the-box features so that you focus only on adding enhancements.',
223
+ id: 'home.description.features'
224
+ })}
225
+ >
226
+ <Container maxW={'6xl'} marginTop={10}>
227
+ <SimpleGrid columns={{base: 1, md: 2, lg: 3}} spacing={10}>
228
+ {features.map((feature, index) => {
229
+ const featureMessage = feature.message
230
+ return (
231
+ <HStack key={index} align={'top'}>
232
+ <VStack align={'start'}>
233
+ <Flex
234
+ width={16}
235
+ height={16}
236
+ align={'center'}
237
+ justify={'left'}
238
+ color={'gray.900'}
239
+ paddingX={2}
240
+ >
241
+ {feature.icon}
242
+ </Flex>
243
+ <Text color={'black'} fontWeight={700} fontSize={20}>
244
+ {intl.formatMessage(featureMessage.title)}
245
+ </Text>
246
+ <Text color={'black'}>
247
+ {intl.formatMessage(featureMessage.text)}
248
+ </Text>
249
+ </VStack>
250
+ </HStack>
251
+ )
252
+ })}
253
+ </SimpleGrid>
254
+ </Container>
255
+ </Section>
256
+
257
+ <Section
258
+ padding={4}
259
+ paddingTop={32}
260
+ title={intl.formatMessage({
261
+ defaultMessage: "We're here to help",
262
+ id: 'home.heading.here_to_help'
263
+ })}
264
+ subtitle={
265
+ <>
266
+ <>
267
+ {intl.formatMessage({
268
+ defaultMessage: 'Contact our support staff.',
269
+ id: 'home.description.here_to_help'
270
+ })}
271
+ </>
272
+ <br />
273
+ <>
274
+ {intl.formatMessage({
275
+ defaultMessage: 'They will get you to the right place.',
276
+ id: 'home.description.here_to_help_line_2'
277
+ })}
278
+ </>
279
+ </>
280
+ }
281
+ actions={
282
+ <Button
283
+ as={Link}
284
+ href="https://help.salesforce.com/s/?language=en_US"
285
+ target="_blank"
286
+ width={'auto'}
287
+ paddingX={7}
288
+ _hover={{textDecoration: 'none'}}
289
+ >
290
+ <FormattedMessage defaultMessage="Contact Us" id="home.link.contact_us" />
291
+ </Button>
292
+ }
293
+ maxWidth={'xl'}
294
+ />
295
+ </Box>
296
+ )
297
+ }
298
+
299
+ Home.getTemplateName = () => 'home'
300
+
301
+ export default Home
@@ -0,0 +1,23 @@
1
+ /*
2
+ * Copyright (c) 2021, 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 React from 'react'
8
+ import {renderWithProviders} from '@salesforce/retail-react-app/app/utils/test-utils'
9
+ import HomePage from '@salesforce/retail-react-app/app/pages/home'
10
+ import {rest} from 'msw'
11
+ import {mockProductSearch} from '@salesforce/retail-react-app/app/mocks/mock-data'
12
+
13
+ test('Home Page renders without errors', async () => {
14
+ global.server.use(
15
+ rest.get('*/product-search', (req, res, ctx) => {
16
+ return res(ctx.delay(0), ctx.status(200), ctx.json(mockProductSearch))
17
+ })
18
+ )
19
+ const {getByTestId} = renderWithProviders(<HomePage />)
20
+
21
+ expect(getByTestId('home-page')).toBeInTheDocument()
22
+ expect(typeof HomePage.getTemplateName()).toBe('string')
23
+ })
@@ -0,0 +1,123 @@
1
+ /*
2
+ * Copyright (c) 2021, 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
+
8
+ import React, {useEffect} from 'react'
9
+ import PropTypes from 'prop-types'
10
+ import {useIntl, defineMessage} from 'react-intl'
11
+ import {Box, Container} from '@chakra-ui/react'
12
+ import {
13
+ AuthHelpers,
14
+ useAuthHelper,
15
+ useCustomerBaskets,
16
+ useCustomerId,
17
+ useCustomerType,
18
+ useShopperBasketsMutation
19
+ } from '@salesforce/commerce-sdk-react'
20
+ import useNavigation from '@salesforce/retail-react-app/app/hooks/use-navigation'
21
+ import Seo from '@salesforce/retail-react-app/app/components/seo'
22
+ import {useForm} from 'react-hook-form'
23
+ import {useLocation} from 'react-router-dom'
24
+ import useEinstein from '@salesforce/retail-react-app/app/hooks/use-einstein'
25
+ import LoginForm from '@salesforce/retail-react-app/app/components/login'
26
+ import {API_ERROR_MESSAGE} from '@salesforce/retail-react-app/app/constants'
27
+ import {usePrevious} from '@salesforce/retail-react-app/app/hooks/use-previous'
28
+ import {isServer} from '@salesforce/retail-react-app/app/utils/utils'
29
+ const LOGIN_ERROR_MESSAGE = defineMessage({
30
+ defaultMessage: 'Incorrect username or password, please try again.',
31
+ id: 'login_page.error.incorrect_username_or_password'
32
+ })
33
+ const Login = () => {
34
+ const {formatMessage} = useIntl()
35
+ const navigate = useNavigation()
36
+ const form = useForm()
37
+ const location = useLocation()
38
+ const einstein = useEinstein()
39
+ const {isRegistered, customerType} = useCustomerType()
40
+ const login = useAuthHelper(AuthHelpers.LoginRegisteredUserB2C)
41
+
42
+ const customerId = useCustomerId()
43
+ const prevAuthType = usePrevious(customerType)
44
+ const {data: baskets} = useCustomerBaskets(
45
+ {parameters: {customerId}},
46
+ {enabled: !!customerId && !isServer, keepPreviousData: true}
47
+ )
48
+ const mergeBasket = useShopperBasketsMutation('mergeBasket')
49
+
50
+ const submitForm = async (data) => {
51
+ try {
52
+ await login.mutateAsync({username: data.email, password: data.password})
53
+ const hasBasketItem = baskets?.baskets?.[0]?.productItems?.length > 0
54
+ // we only want to merge basket when the user is logged in as a recurring user
55
+ // only recurring users trigger the login mutation, new user triggers register mutation
56
+ // this logic needs to stay in this block because this is the only place that tells if a user is a recurring user
57
+ // if you change logic here, also change it in login page
58
+ const shouldMergeBasket = hasBasketItem && prevAuthType === 'guest'
59
+ if (shouldMergeBasket) {
60
+ mergeBasket.mutate({
61
+ headers: {
62
+ // This is not required since the request has no body
63
+ // but CommerceAPI throws a '419 - Unsupported Media Type' error if this header is removed.
64
+ 'Content-Type': 'application/json'
65
+ },
66
+ parameters: {
67
+ createDestinationBasket: true
68
+ }
69
+ })
70
+ }
71
+ } catch (error) {
72
+ const message = /Unauthorized/i.test(error.message)
73
+ ? formatMessage(LOGIN_ERROR_MESSAGE)
74
+ : formatMessage(API_ERROR_MESSAGE)
75
+ form.setError('global', {type: 'manual', message})
76
+ }
77
+ }
78
+
79
+ // If customer is registered push to account page
80
+ useEffect(() => {
81
+ if (isRegistered) {
82
+ if (location?.state?.directedFrom) {
83
+ navigate(location.state.directedFrom)
84
+ } else {
85
+ navigate('/account')
86
+ }
87
+ }
88
+ }, [isRegistered])
89
+
90
+ /**************** Einstein ****************/
91
+ useEffect(() => {
92
+ einstein.sendViewPage(location.pathname)
93
+ }, [])
94
+ return (
95
+ <Box data-testid="login-page" bg="gray.50" py={[8, 16]}>
96
+ <Seo title="Sign in" description="Customer sign in" />
97
+ <Container
98
+ paddingTop={16}
99
+ width={['100%', '407px']}
100
+ bg="white"
101
+ paddingBottom={14}
102
+ marginTop={8}
103
+ marginBottom={8}
104
+ borderRadius="base"
105
+ >
106
+ <LoginForm
107
+ form={form}
108
+ submitForm={submitForm}
109
+ clickCreateAccount={() => navigate('/registration')}
110
+ clickForgotPassword={() => navigate('/reset-password')}
111
+ />
112
+ </Container>
113
+ </Box>
114
+ )
115
+ }
116
+
117
+ Login.getTemplateName = () => 'login'
118
+
119
+ Login.propTypes = {
120
+ match: PropTypes.object
121
+ }
122
+
123
+ export default Login
@@ -0,0 +1,229 @@
1
+ /*
2
+ * Copyright (c) 2021, 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 React from 'react'
8
+ import {screen, waitFor} from '@testing-library/react'
9
+ import {rest} from 'msw'
10
+ import {
11
+ renderWithProviders,
12
+ createPathWithDefaults,
13
+ guestToken
14
+ } from '@salesforce/retail-react-app/app/utils/test-utils'
15
+ import Login from '.'
16
+ import {BrowserRouter as Router, Route} from 'react-router-dom'
17
+ import Account from '@salesforce/retail-react-app/app/pages/account'
18
+ import Registration from '@salesforce/retail-react-app/app/pages/registration'
19
+ import ResetPassword from '@salesforce/retail-react-app/app/pages/reset-password'
20
+ import mockConfig from '@salesforce/retail-react-app/config/mocks/default'
21
+ import {mockedRegisteredCustomer} from '@salesforce/retail-react-app/app/mocks/mock-data'
22
+ const mockMergedBasket = {
23
+ basketId: 'a10ff320829cb0eef93ca5310a',
24
+ currency: 'USD',
25
+ customerInfo: {
26
+ customerId: 'registeredCustomerId',
27
+ email: 'customer@test.com'
28
+ }
29
+ }
30
+
31
+ const MockedComponent = () => {
32
+ const match = {
33
+ params: {pageName: 'profile'}
34
+ }
35
+ return (
36
+ <Router>
37
+ <Login />
38
+ <Route path={createPathWithDefaults('/registration')}>
39
+ <Registration />
40
+ </Route>
41
+ <Route path={createPathWithDefaults('/reset-password')}>
42
+ <ResetPassword />
43
+ </Route>
44
+ <Route path={createPathWithDefaults('/account')}>
45
+ <Account match={match} />
46
+ </Route>
47
+ </Router>
48
+ )
49
+ }
50
+
51
+ // Set up and clean up
52
+ beforeEach(() => {
53
+ jest.resetModules()
54
+ global.server.use(
55
+ rest.post('*/customers', (req, res, ctx) => {
56
+ return res(ctx.delay(0), ctx.status(200), ctx.json(mockedRegisteredCustomer))
57
+ }),
58
+ rest.get('*/customers/:customerId', (req, res, ctx) => {
59
+ const {customerId} = req.params
60
+ if (customerId === 'customerId') {
61
+ return res(
62
+ ctx.delay(0),
63
+ ctx.status(200),
64
+ ctx.json({
65
+ authType: 'guest',
66
+ customerId: 'customerid'
67
+ })
68
+ )
69
+ }
70
+ return res(ctx.delay(0), ctx.status(200), ctx.json(mockedRegisteredCustomer))
71
+ })
72
+ )
73
+ })
74
+ afterEach(() => {
75
+ jest.resetModules()
76
+ localStorage.clear()
77
+ })
78
+
79
+ describe('Logging in tests', function () {
80
+ beforeEach(() => {
81
+ global.server.use(
82
+ rest.post('*/oauth2/token', (req, res, ctx) =>
83
+ res(
84
+ ctx.delay(0),
85
+ ctx.json({
86
+ customer_id: 'customerid',
87
+ access_token: guestToken,
88
+ refresh_token: 'testrefeshtoken',
89
+ usid: 'testusid',
90
+ enc_user_id: 'testEncUserId',
91
+ id_token: 'testIdToken'
92
+ })
93
+ )
94
+ ),
95
+ rest.post('*/baskets/actions/merge', (req, res, ctx) => {
96
+ return res(ctx.delay(0), ctx.json(mockMergedBasket))
97
+ })
98
+ )
99
+ })
100
+ test('Allows customer to sign in to their account', async () => {
101
+ const {user} = renderWithProviders(<MockedComponent />, {
102
+ wrapperProps: {
103
+ siteAlias: 'uk',
104
+ locale: {id: 'en-GB'},
105
+ appConfig: mockConfig.app,
106
+ bypassAuth: false
107
+ }
108
+ })
109
+
110
+ // enter credentials and submit
111
+ await user.type(screen.getByLabelText('Email'), 'customer@test.com')
112
+ await user.type(screen.getByLabelText('Password'), 'Password!1')
113
+ // login with credentials
114
+ global.server.use(
115
+ rest.post('*/oauth2/token', (req, res, ctx) =>
116
+ res(
117
+ ctx.delay(0),
118
+ ctx.json({
119
+ customer_id: 'customerid_1',
120
+ access_token:
121
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdXQiOiJHVUlEIiwic2NwIjoic2ZjYy5zaG9wcGVyLW15YWNjb3VudC5iYXNrZXRzIHNmY2Muc2hvcHBlci1teWFjY291bnQuYWRkcmVzc2VzIHNmY2Muc2hvcHBlci1wcm9kdWN0cyBzZmNjLnNob3BwZXItZGlzY292ZXJ5LXNlYXJjaCBzZmNjLnNob3BwZXItbXlhY2NvdW50LnJ3IHNmY2Muc2hvcHBlci1teWFjY291bnQucGF5bWVudGluc3RydW1lbnRzIHNmY2Muc2hvcHBlci1jdXN0b21lcnMubG9naW4gc2ZjYy5zaG9wcGVyLWV4cGVyaWVuY2Ugc2ZjYy5zaG9wcGVyLW15YWNjb3VudC5vcmRlcnMgc2ZjYy5zaG9wcGVyLWN1c3RvbWVycy5yZWdpc3RlciBzZmNjLnNob3BwZXItYmFza2V0cy1vcmRlcnMgc2ZjYy5zaG9wcGVyLW15YWNjb3VudC5hZGRyZXNzZXMucncgc2ZjYy5zaG9wcGVyLW15YWNjb3VudC5wcm9kdWN0bGlzdHMucncgc2ZjYy5zaG9wcGVyLXByb2R1Y3RsaXN0cyBzZmNjLnNob3BwZXItcHJvbW90aW9ucyBzZmNjLnNob3BwZXItYmFza2V0cy1vcmRlcnMucncgc2ZjYy5zaG9wcGVyLW15YWNjb3VudC5wYXltZW50aW5zdHJ1bWVudHMucncgc2ZjYy5zaG9wcGVyLWdpZnQtY2VydGlmaWNhdGVzIHNmY2Muc2hvcHBlci1wcm9kdWN0LXNlYXJjaCBzZmNjLnNob3BwZXItbXlhY2NvdW50LnByb2R1Y3RsaXN0cyBzZmNjLnNob3BwZXItY2F0ZWdvcmllcyBzZmNjLnNob3BwZXItbXlhY2NvdW50Iiwic3ViIjoiY2Mtc2xhczo6enpyZl8wMDE6OnNjaWQ6YzljNDViZmQtMGVkMy00YWEyLTk5NzEtNDBmODg5NjJiODM2Ojp1c2lkOjhlODgzOTczLTY4ZWItNDFmZS1hM2M1LTc1NjIzMjY1MmZmNSIsImN0eCI6InNsYXMiLCJpc3MiOiJzbGFzL3Byb2QvenpyZl8wMDEiLCJpc3QiOjEsImF1ZCI6ImNvbW1lcmNlY2xvdWQvcHJvZC96enJmXzAwMSIsIm5iZiI6MTY3ODgzNDI3MSwic3R5IjoiVXNlciIsImlzYiI6InVpZG86ZWNvbTo6dXBuOmtldjVAdGVzdC5jb206OnVpZG46a2V2aW4gaGU6OmdjaWQ6YWJtZXMybWJrM2xYa1JsSEZKd0dZWWt1eEo6OnJjaWQ6YWJVTXNhdnBEOVk2alcwMGRpMlNqeEdDTVU6OmNoaWQ6UmVmQXJjaEdsb2JhbCIsImV4cCI6MjY3ODgzNjEwMSwiaWF0IjoxNjc4ODM0MzAxLCJqdGkiOiJDMkM0ODU2MjAxODYwLTE4OTA2Nzg5MDM0ODA1ODMyNTcwNjY2NTQyIn0._tUrxeXdFYPj6ZoY-GILFRd3-aD1RGPkZX6TqHeS494',
122
+ refresh_token: 'testrefeshtoken_1',
123
+ usid: 'testusid_1',
124
+ enc_user_id: 'testEncUserId_1',
125
+ id_token: 'testIdToken_1'
126
+ })
127
+ )
128
+ )
129
+ )
130
+
131
+ await user.click(screen.getByText(/sign in/i))
132
+ await waitFor(() => {
133
+ expect(window.location.pathname).toBe('/uk/en-GB/account')
134
+ expect(screen.getByText(/My Profile/i)).toBeInTheDocument()
135
+ })
136
+ })
137
+ })
138
+
139
+ describe('Error while logging in', function () {
140
+ beforeEach(() => {
141
+ global.server.use(
142
+ rest.post('*/oauth2/token', (req, res, ctx) =>
143
+ res(
144
+ ctx.delay(0),
145
+ ctx.json({
146
+ customer_id: 'customerid',
147
+ access_token: guestToken,
148
+ refresh_token: 'testrefeshtoken',
149
+ usid: 'testusid',
150
+ enc_user_id: 'testEncUserId',
151
+ id_token: 'testIdToken'
152
+ })
153
+ )
154
+ ),
155
+ rest.post('*/baskets/actions/merge', (req, res, ctx) => {
156
+ return res(ctx.delay(0), ctx.json(mockMergedBasket))
157
+ })
158
+ )
159
+ })
160
+
161
+ // TODO: Fix flaky/broken test
162
+ // eslint-disable-next-line jest/no-disabled-tests
163
+ test.skip('Renders error when given incorrect log in credentials', async () => {
164
+ const {user} = renderWithProviders(<MockedComponent />, {
165
+ wrapperProps: {
166
+ siteAlias: 'uk',
167
+ locale: {id: 'en-GB'},
168
+ appConfig: mockConfig.app,
169
+ bypassAuth: false
170
+ }
171
+ })
172
+
173
+ // enter credentials and submit
174
+ await user.type(screen.getByLabelText('Email'), 'foo@test.com')
175
+ await user.type(screen.getByLabelText('Password'), 'SomeFakePassword1!')
176
+
177
+ // mock failed auth request
178
+ global.server.use(
179
+ rest.post('*/oauth2/login', (req, res, ctx) =>
180
+ res(ctx.delay(0), ctx.status(401), ctx.json({message: 'Unauthorized Credentials.'}))
181
+ ),
182
+ rest.post('*/customers', (req, res, ctx) => {
183
+ return res(ctx.delay(0), ctx.status(404), ctx.json({message: 'Not Found.'}))
184
+ })
185
+ )
186
+
187
+ await user.click(screen.getByText(/sign in/i))
188
+ // wait for login error alert to appear
189
+ expect(
190
+ await screen.findByText(/Incorrect username or password, please try again./i)
191
+ ).toBeInTheDocument()
192
+ })
193
+ })
194
+ describe('Navigate away from login page tests', function () {
195
+ test('should navigate to sign up page when the user clicks Create Account', async () => {
196
+ const {user} = renderWithProviders(<MockedComponent />, {
197
+ wrapperProps: {
198
+ siteAlias: 'uk',
199
+ locale: {id: 'en-GB'},
200
+ appConfig: mockConfig.app,
201
+ isGuest: true
202
+ }
203
+ })
204
+ await user.click(await screen.findByText(/Create Account/i))
205
+
206
+ await waitFor(async () => {
207
+ // wait for sign up page to appear
208
+ expect(await screen.findByText(/Let's get started/i)).toBeInTheDocument()
209
+ })
210
+ })
211
+ test('should navigate to reset password page when the user clicks Forgot Password', async () => {
212
+ const {user} = renderWithProviders(<MockedComponent />, {
213
+ wrapperProps: {
214
+ siteAlias: 'uk',
215
+ locale: {id: 'en-GB'},
216
+ appConfig: mockConfig.app,
217
+ isGuest: true
218
+ }
219
+ })
220
+ await user.click(screen.getByText(/forgot password/i))
221
+
222
+ // wait for sign up page to appear
223
+ expect(
224
+ await screen.findByText(
225
+ /Enter your email to receive instructions on how to reset your password/i
226
+ )
227
+ ).toBeInTheDocument()
228
+ })
229
+ })