@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,40 @@
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 {useIntl} from 'react-intl'
8
+
9
+ export default function useResetPasswordFields({
10
+ form: {
11
+ control,
12
+ formState: {errors}
13
+ },
14
+ prefix = ''
15
+ }) {
16
+ const {formatMessage} = useIntl()
17
+
18
+ const fields = {
19
+ email: {
20
+ name: `${prefix}email`,
21
+ label: formatMessage({
22
+ defaultMessage: 'Email',
23
+ id: 'use_reset_password_fields.label.email'
24
+ }),
25
+ placeholder: 'you@email.com',
26
+ defaultValue: '',
27
+ type: 'email',
28
+ rules: {
29
+ required: formatMessage({
30
+ defaultMessage: 'Please enter a valid email address.',
31
+ id: 'use_reset_password_fields.error.required_email'
32
+ })
33
+ },
34
+ error: errors[`${prefix}email`],
35
+ control
36
+ }
37
+ }
38
+
39
+ return fields
40
+ }
@@ -0,0 +1,89 @@
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 {useIntl} from 'react-intl'
8
+ import {validatePassword} from '@salesforce/retail-react-app/app/utils/password-utils'
9
+
10
+ export default function useUpdatePasswordFields({
11
+ form: {
12
+ control,
13
+ formState: {errors}
14
+ },
15
+ prefix = ''
16
+ }) {
17
+ const {formatMessage} = useIntl()
18
+
19
+ const fields = {
20
+ currentPassword: {
21
+ name: `${prefix}currentPassword`,
22
+ label: formatMessage({
23
+ defaultMessage: 'Current Password',
24
+ id: 'use_update_password_fields.label.current_password'
25
+ }),
26
+ defaultValue: '',
27
+ type: 'password',
28
+ rules: {
29
+ required: formatMessage({
30
+ defaultMessage: 'Please enter your password.',
31
+ id: 'use_update_password_fields.error.required_password'
32
+ })
33
+ },
34
+ error: errors[`${prefix}currentPassword`],
35
+ control
36
+ },
37
+ password: {
38
+ name: `${prefix}password`,
39
+ label: formatMessage({
40
+ defaultMessage: 'New Password',
41
+ id: 'use_update_password_fields.label.new_password'
42
+ }),
43
+ type: 'password',
44
+ defaultValue: '',
45
+ rules: {
46
+ required: formatMessage({
47
+ defaultMessage: 'Please provide a new password.',
48
+ id: 'use_update_password_fields.error.required_new_password'
49
+ }),
50
+ validate: {
51
+ hasMinChars: (val) =>
52
+ validatePassword(val).hasMinChars ||
53
+ formatMessage({
54
+ defaultMessage: 'Password must contain at least 8 characters.',
55
+ id: 'use_update_password_fields.error.minimum_characters'
56
+ }),
57
+ hasUppercase: (val) =>
58
+ validatePassword(val).hasUppercase ||
59
+ formatMessage({
60
+ defaultMessage: 'Password must contain at least one uppercase letter.',
61
+ id: 'use_update_password_fields.error.uppercase_letter'
62
+ }),
63
+ hasLowercase: (val) =>
64
+ validatePassword(val).hasLowercase ||
65
+ formatMessage({
66
+ defaultMessage: 'Password must contain at least one lowercase letter.',
67
+ id: 'use_update_password_fields.error.lowercase_letter'
68
+ }),
69
+ hasNumber: (val) =>
70
+ validatePassword(val).hasNumber ||
71
+ formatMessage({
72
+ defaultMessage: 'Password must contain at least one number.',
73
+ id: 'use_update_password_fields.error.contain_number'
74
+ }),
75
+ hasSpecialChar: (val) =>
76
+ validatePassword(val).hasSpecialChar ||
77
+ formatMessage({
78
+ defaultMessage: 'Password must contain at least one special character.',
79
+ id: 'use_update_password_fields.error.special_character'
80
+ })
81
+ }
82
+ },
83
+ error: errors[`${prefix}password`],
84
+ control
85
+ }
86
+ }
87
+
88
+ return fields
89
+ }
@@ -0,0 +1,290 @@
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, {useRef, useState} from 'react'
8
+ import PropTypes from 'prop-types'
9
+ import {useIntl} from 'react-intl'
10
+ import {
11
+ useMultiStyleConfig,
12
+ Box,
13
+ Flex,
14
+ IconButton,
15
+ Badge,
16
+ Button,
17
+ Popover,
18
+ PopoverHeader,
19
+ PopoverTrigger,
20
+ PopoverContent,
21
+ PopoverBody,
22
+ PopoverFooter,
23
+ PopoverArrow,
24
+ Stack,
25
+ Text,
26
+ Divider,
27
+ useDisclosure,
28
+ useMediaQuery
29
+ } from '@chakra-ui/react'
30
+ import {AuthHelpers, useAuthHelper, useCustomerType} from '@salesforce/commerce-sdk-react'
31
+
32
+ import {useCurrentBasket} from '@salesforce/retail-react-app/app/hooks/use-current-basket'
33
+
34
+ import Link from '@salesforce/retail-react-app/app/components/link'
35
+ import Search from '@salesforce/retail-react-app/app/components/search'
36
+ import withRegistration from '@salesforce/retail-react-app/app/components/with-registration'
37
+ import {
38
+ AccountIcon,
39
+ BrandLogo,
40
+ BasketIcon,
41
+ HamburgerIcon,
42
+ ChevronDownIcon,
43
+ HeartIcon,
44
+ SignoutIcon
45
+ } from '@salesforce/retail-react-app/app/components/icons'
46
+
47
+ import {navLinks, messages} from '@salesforce/retail-react-app/app/pages/account/constant'
48
+ import useNavigation from '@salesforce/retail-react-app/app/hooks/use-navigation'
49
+ import LoadingSpinner from '@salesforce/retail-react-app/app/components/loading-spinner'
50
+ import {isHydrated, noop} from '@salesforce/retail-react-app/app/utils/utils'
51
+
52
+ const ENTER_KEY = 'Enter'
53
+
54
+ const IconButtonWithRegistration = withRegistration(IconButton)
55
+ /**
56
+ * The header is the main source for accessing
57
+ * navigation, search, basket, and other
58
+ * important information and actions. It persists
59
+ * on the top of your application and will
60
+ * respond to changes in device size.
61
+ *
62
+ * To customize the styles, update the themes
63
+ * in theme/components/project/header.js
64
+ * @param props
65
+ * @param {func} props.onMenuClick click event handler for menu button
66
+ * @param {func} props.onLogoClick click event handler for menu button
67
+ * @param {object} props.searchInputRef reference of the search input
68
+ * @param {func} props.onMyAccountClick click event handler for my account button
69
+ * @param {func} props.onMyCartClick click event handler for my cart button
70
+ * @return {React.ReactElement} - Header component
71
+ */
72
+ const Header = ({
73
+ children,
74
+ onMenuClick = noop,
75
+ onMyAccountClick = noop,
76
+ onLogoClick = noop,
77
+ onMyCartClick = noop,
78
+ onWishlistClick = noop,
79
+ ...props
80
+ }) => {
81
+ const intl = useIntl()
82
+ const {
83
+ derivedData: {totalItems},
84
+ data: basket
85
+ } = useCurrentBasket()
86
+ const {isRegistered} = useCustomerType()
87
+ const logout = useAuthHelper(AuthHelpers.Logout)
88
+ const navigate = useNavigation()
89
+ const {isOpen, onClose, onOpen} = useDisclosure()
90
+ const [isDesktop] = useMediaQuery('(min-width: 992px)')
91
+
92
+ const [showLoading, setShowLoading] = useState(false)
93
+ // tracking if users enter the popover Content,
94
+ // so we can decide whether to close the menu when users leave account icons
95
+ const hasEnterPopoverContent = useRef()
96
+
97
+ const styles = useMultiStyleConfig('Header')
98
+
99
+ const onSignoutClick = async () => {
100
+ setShowLoading(true)
101
+ await logout.mutateAsync()
102
+ navigate('/login')
103
+ setShowLoading(false)
104
+ }
105
+
106
+ const keyMap = {
107
+ Escape: () => onClose(),
108
+ Enter: () => onOpen()
109
+ }
110
+
111
+ const handleIconsMouseLeave = () => {
112
+ // don't close the menu if users enter the popover content
113
+ setTimeout(() => {
114
+ if (!hasEnterPopoverContent.current) onClose()
115
+ }, 100)
116
+ }
117
+ return (
118
+ <Box {...styles.container} {...props}>
119
+ <Box {...styles.content}>
120
+ {showLoading && <LoadingSpinner wrapperStyles={{height: '100vh'}} />}
121
+ <Flex wrap="wrap" alignItems={['baseline', 'baseline', 'baseline', 'center']}>
122
+ <IconButton
123
+ aria-label={intl.formatMessage({
124
+ id: 'header.button.assistive_msg.menu',
125
+ defaultMessage: 'Menu'
126
+ })}
127
+ icon={<HamburgerIcon />}
128
+ variant="unstyled"
129
+ display={{lg: 'none'}}
130
+ {...styles.icons}
131
+ onClick={onMenuClick}
132
+ />
133
+ <IconButton
134
+ aria-label={intl.formatMessage({
135
+ id: 'header.button.assistive_msg.logo',
136
+ defaultMessage: 'Logo'
137
+ })}
138
+ icon={<BrandLogo {...styles.logo} />}
139
+ {...styles.icons}
140
+ variant="unstyled"
141
+ onClick={onLogoClick}
142
+ />
143
+ <Box {...styles.bodyContainer}>{children}</Box>
144
+ <Box {...styles.searchContainer}>
145
+ <Search
146
+ placeholder={intl.formatMessage({
147
+ id: 'header.field.placeholder.search_for_products',
148
+ defaultMessage: 'Search for products...'
149
+ })}
150
+ {...styles.search}
151
+ />
152
+ </Box>
153
+ <AccountIcon
154
+ {...styles.accountIcon}
155
+ tabIndex={0}
156
+ onMouseOver={isDesktop ? onOpen : noop}
157
+ onKeyDown={(e) => {
158
+ e.key === ENTER_KEY ? onMyAccountClick() : noop
159
+ }}
160
+ onClick={onMyAccountClick}
161
+ aria-label={intl.formatMessage({
162
+ id: 'header.button.assistive_msg.my_account',
163
+ defaultMessage: 'My account'
164
+ })}
165
+ />
166
+
167
+ {isRegistered && isHydrated() && (
168
+ <Popover
169
+ isLazy
170
+ arrowSize={15}
171
+ isOpen={isOpen}
172
+ placement="bottom-end"
173
+ onClose={onClose}
174
+ onOpen={onOpen}
175
+ >
176
+ <PopoverTrigger>
177
+ <ChevronDownIcon
178
+ aria-label="My account trigger"
179
+ onMouseLeave={handleIconsMouseLeave}
180
+ onKeyDown={(e) => {
181
+ keyMap[e.key]?.(e)
182
+ }}
183
+ {...styles.arrowDown}
184
+ onMouseOver={onOpen}
185
+ tabIndex={0}
186
+ />
187
+ </PopoverTrigger>
188
+
189
+ <PopoverContent
190
+ {...styles.popoverContent}
191
+ onMouseLeave={() => {
192
+ hasEnterPopoverContent.current = false
193
+ onClose()
194
+ }}
195
+ onMouseOver={() => {
196
+ hasEnterPopoverContent.current = true
197
+ }}
198
+ >
199
+ <PopoverArrow />
200
+ <PopoverHeader>
201
+ <Text>
202
+ {intl.formatMessage({
203
+ defaultMessage: 'My Account',
204
+ id: 'header.popover.title.my_account'
205
+ })}
206
+ </Text>
207
+ </PopoverHeader>
208
+ <PopoverBody>
209
+ <Stack spacing={0} as="nav" data-testid="account-detail-nav">
210
+ {navLinks.map((link) => {
211
+ const LinkIcon = link.icon
212
+ return (
213
+ <Button
214
+ key={link.name}
215
+ as={Link}
216
+ to={`/account${link.path}`}
217
+ useNavLink={true}
218
+ variant="menu-link"
219
+ leftIcon={<LinkIcon boxSize={5} />}
220
+ >
221
+ {intl.formatMessage(messages[link.name])}
222
+ </Button>
223
+ )
224
+ })}
225
+ </Stack>
226
+ </PopoverBody>
227
+ <PopoverFooter onClick={onSignoutClick} cursor="pointer">
228
+ <Divider colorScheme="gray" />
229
+ <Button variant="unstyled" {...styles.signout}>
230
+ <Flex>
231
+ <SignoutIcon boxSize={5} {...styles.signoutIcon} />
232
+ <Text as="span" {...styles.signoutText}>
233
+ {intl.formatMessage({
234
+ defaultMessage: 'Log out',
235
+ id: 'header.popover.action.log_out'
236
+ })}
237
+ </Text>
238
+ </Flex>
239
+ </Button>
240
+ </PopoverFooter>
241
+ </PopoverContent>
242
+ </Popover>
243
+ )}
244
+ <IconButtonWithRegistration
245
+ aria-label={intl.formatMessage({
246
+ defaultMessage: 'Wishlist',
247
+ id: 'header.button.assistive_msg.wishlist'
248
+ })}
249
+ icon={<HeartIcon />}
250
+ variant="unstyled"
251
+ {...styles.icons}
252
+ onClick={onWishlistClick}
253
+ />
254
+ <IconButton
255
+ aria-label={intl.formatMessage({
256
+ id: 'header.button.assistive_msg.my_cart',
257
+ defaultMessage: 'My cart'
258
+ })}
259
+ icon={
260
+ <>
261
+ <BasketIcon />
262
+ {basket && totalItems > 0 && (
263
+ <Badge variant="notification">{totalItems}</Badge>
264
+ )}
265
+ </>
266
+ }
267
+ variant="unstyled"
268
+ {...styles.icons}
269
+ onClick={onMyCartClick}
270
+ />
271
+ </Flex>
272
+ </Box>
273
+ </Box>
274
+ )
275
+ }
276
+
277
+ Header.propTypes = {
278
+ children: PropTypes.node,
279
+ onMenuClick: PropTypes.func,
280
+ onLogoClick: PropTypes.func,
281
+ onMyAccountClick: PropTypes.func,
282
+ onWishlistClick: PropTypes.func,
283
+ onMyCartClick: PropTypes.func,
284
+ searchInputRef: PropTypes.oneOfType([
285
+ PropTypes.func,
286
+ PropTypes.shape({current: PropTypes.elementType})
287
+ ])
288
+ }
289
+
290
+ export default Header
@@ -0,0 +1,217 @@
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 PropTypes from 'prop-types'
9
+ import userEvent from '@testing-library/user-event'
10
+ import {fireEvent, screen, waitFor, act} from '@testing-library/react'
11
+ import Header from '@salesforce/retail-react-app/app/components/header/index'
12
+ import {
13
+ renderWithProviders,
14
+ createPathWithDefaults
15
+ } from '@salesforce/retail-react-app/app/utils/test-utils'
16
+ import {rest} from 'msw'
17
+ import {createMemoryHistory} from 'history'
18
+ import {
19
+ mockCustomerBaskets,
20
+ mockedRegisteredCustomer
21
+ } from '@salesforce/retail-react-app/app/mocks/mock-data'
22
+
23
+ jest.mock('@chakra-ui/react', () => {
24
+ const originalModule = jest.requireActual('@chakra-ui/react')
25
+ return {
26
+ ...originalModule,
27
+ useMediaQuery: jest.fn().mockReturnValue([true])
28
+ }
29
+ })
30
+ const MockedComponent = ({history}) => {
31
+ const onAccountClick = () => {
32
+ history.push(createPathWithDefaults('/account'))
33
+ }
34
+ const onWishlistClick = () => {
35
+ history.push(createPathWithDefaults('/account/wishlist'))
36
+ }
37
+ return (
38
+ <div>
39
+ <Header onMyAccountClick={onAccountClick} onWishlistClick={onWishlistClick} />
40
+ </div>
41
+ )
42
+ }
43
+ MockedComponent.propTypes = {
44
+ history: PropTypes.object
45
+ }
46
+
47
+ // Set up and clean up
48
+ beforeEach(() => {
49
+ jest.resetModules()
50
+ global.server.use(
51
+ rest.get('*/customers/:customerId/baskets', (req, res, ctx) => {
52
+ return res(ctx.delay(0), ctx.status(200), ctx.json(mockCustomerBaskets))
53
+ })
54
+ )
55
+ })
56
+ afterEach(() => {
57
+ localStorage.clear()
58
+ })
59
+ test('renders Header', async () => {
60
+ renderWithProviders(<Header />)
61
+
62
+ await waitFor(() => {
63
+ const menu = document.querySelector('button[aria-label="Menu"]')
64
+ const logo = document.querySelector('button[aria-label="Logo"]')
65
+ const account = document.querySelector('svg[aria-label="My account"]')
66
+ const cart = document.querySelector('button[aria-label="My cart"]')
67
+ const wishlist = document.querySelector('button[aria-label="Wishlist"]')
68
+ const searchInput = document.querySelector('input[type="search"]')
69
+ expect(menu).toBeInTheDocument()
70
+ expect(logo).toBeInTheDocument()
71
+ expect(account).toBeInTheDocument()
72
+ expect(cart).toBeInTheDocument()
73
+ expect(wishlist).toBeInTheDocument()
74
+ expect(searchInput).toBeInTheDocument()
75
+ })
76
+ })
77
+
78
+ test('renders Header with event handlers', async () => {
79
+ const onMenuClick = jest.fn()
80
+ const onLogoClick = jest.fn()
81
+ const onMyAccountClick = jest.fn()
82
+ const onMyCartClick = jest.fn()
83
+ renderWithProviders(
84
+ <Header
85
+ onMenuClick={onMenuClick}
86
+ onLogoClick={onLogoClick}
87
+ onMyAccountClick={onMyAccountClick}
88
+ onMyCartClick={onMyCartClick}
89
+ />
90
+ )
91
+ await waitFor(() => {
92
+ const menu = document.querySelector('button[aria-label="Menu"]')
93
+ const logo = document.querySelector('button[aria-label="Logo"]')
94
+ const account = document.querySelector('svg[aria-label="My account"]')
95
+ const cart = document.querySelector('button[aria-label="My cart"]')
96
+ expect(menu).toBeInTheDocument()
97
+ fireEvent.click(menu)
98
+ expect(onMenuClick).toHaveBeenCalledTimes(1)
99
+ fireEvent.click(logo)
100
+ expect(onLogoClick).toHaveBeenCalledTimes(1)
101
+ fireEvent.click(account)
102
+ expect(onMyAccountClick).toHaveBeenCalledTimes(1)
103
+ fireEvent.click(cart)
104
+ expect(onMyCartClick).toHaveBeenCalledTimes(1)
105
+ })
106
+ })
107
+
108
+ /**
109
+ * The badge component on the cart that shows the number of items in the cart
110
+ * should only be displayed when there is a valid cart loaded.
111
+ */
112
+ const testBaskets = [null, undefined, {total: 0}]
113
+
114
+ test.each(testBaskets)(
115
+ `does not render cart badge when basket value is not defined`,
116
+ async (initialBasket) => {
117
+ global.server.use(
118
+ rest.get('*/customers/:customerId/baskets', (req, res, ctx) => {
119
+ return res(ctx.delay(0), ctx.status(200), ctx.json(initialBasket))
120
+ })
121
+ )
122
+ renderWithProviders(<Header />)
123
+
124
+ await waitFor(() => {
125
+ // Look for badge.
126
+ const badge = document.querySelector('button[aria-label="My cart"] .chakra-badge')
127
+ expect(badge).not.toBeInTheDocument()
128
+ })
129
+ }
130
+ )
131
+
132
+ test('renders cart badge when basket is loaded', async () => {
133
+ renderWithProviders(<Header />)
134
+
135
+ await waitFor(() => {
136
+ // Look for badge.
137
+ const badge = document.querySelector('button[aria-label="My cart"] .chakra-badge')
138
+ expect(badge).toBeInTheDocument()
139
+ })
140
+ })
141
+
142
+ test('route to account page when an authenticated users click on account icon', async () => {
143
+ const history = createMemoryHistory()
144
+ // mock push function
145
+ history.push = jest.fn()
146
+ renderWithProviders(<MockedComponent history={history} />)
147
+
148
+ await waitFor(() => {
149
+ // Look for account icon
150
+ const accountTrigger = document.querySelector('svg[aria-label="My account trigger"]')
151
+ expect(accountTrigger).toBeInTheDocument()
152
+ })
153
+ const accountIcon = document.querySelector('svg[aria-label="My account"]')
154
+ fireEvent.click(accountIcon)
155
+ await waitFor(() => {
156
+ expect(history.push).toHaveBeenCalledWith(createPathWithDefaults('/account'))
157
+ })
158
+
159
+ fireEvent.keyDown(accountIcon, {key: 'Enter', code: 'Enter'})
160
+ await waitFor(() => {
161
+ expect(history.push).toHaveBeenCalledWith(createPathWithDefaults('/account'))
162
+ })
163
+ })
164
+
165
+ test('route to wishlist page when an authenticated users click on wishlist icon', async () => {
166
+ const user = userEvent.setup()
167
+ const history = createMemoryHistory()
168
+ // mock push function
169
+ history.push = jest.fn()
170
+
171
+ renderWithProviders(<MockedComponent history={history} />)
172
+
173
+ await waitFor(() => {
174
+ // Look for account icon
175
+ const accountTrigger = document.querySelector('svg[aria-label="My account trigger"]')
176
+ expect(accountTrigger).toBeInTheDocument()
177
+ })
178
+ const wishlistIcon = screen.getByRole('button', {name: /wishlist/i})
179
+ await user.click(wishlistIcon)
180
+ await waitFor(() => {
181
+ expect(history.push).toHaveBeenCalledWith(createPathWithDefaults('/account/wishlist'))
182
+ })
183
+ })
184
+
185
+ test('shows dropdown menu when an authenticated users hover on the account icon', async () => {
186
+ const user = userEvent.setup()
187
+ global.server.use(
188
+ rest.post('*/customers/action/login', (req, res, ctx) => {
189
+ return res(ctx.delay(0), ctx.status(200), ctx.json(mockedRegisteredCustomer))
190
+ })
191
+ )
192
+ const history = createMemoryHistory()
193
+ // mock push function
194
+ history.push = jest.fn()
195
+ await act(async () => {
196
+ renderWithProviders(<MockedComponent history={history} />)
197
+ })
198
+
199
+ await waitFor(() => {
200
+ // Look for account icon
201
+ const accountTrigger = document.querySelector('svg[aria-label="My account trigger"]')
202
+ expect(accountTrigger).toBeInTheDocument()
203
+ })
204
+ const accountIcon = document.querySelector('svg[aria-label="My account"]')
205
+ fireEvent.click(accountIcon)
206
+ await waitFor(() => {
207
+ expect(history.push).toHaveBeenCalledWith(createPathWithDefaults('/account'))
208
+ })
209
+ await user.hover(accountIcon)
210
+
211
+ await waitFor(() => {
212
+ expect(screen.getByText(/account details/i)).toBeInTheDocument()
213
+ expect(screen.getByText(/addresses/i)).toBeInTheDocument()
214
+ expect(screen.getByText(/wishlist/i)).toBeInTheDocument()
215
+ expect(screen.getByText(/order history/i)).toBeInTheDocument()
216
+ })
217
+ })