@newskit-render/core 0.0.0-d55b7d88

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 (456) hide show
  1. package/.ci/trigger-circleci-workflow.sh +37 -0
  2. package/.eslintignore +23 -0
  3. package/.eslintrc +74 -0
  4. package/.prettierignore +19 -0
  5. package/.prettierrc +7 -0
  6. package/CHANGELOG.md +7638 -0
  7. package/Dockerfile +14 -0
  8. package/Dockerfile.withNewRelic +15 -0
  9. package/README.md +274 -0
  10. package/__tests__/pages/__snapshots__/brightcove.test.tsx.snap +20 -0
  11. package/__tests__/pages/__snapshots__/home.test.tsx.snap +1195 -0
  12. package/__tests__/pages/brightcove.test.tsx +34 -0
  13. package/__tests__/pages/empty.test.tsx +10 -0
  14. package/__tests__/pages/home.test.tsx +16 -0
  15. package/app-context/InstrumentationContextProvider.tsx +36 -0
  16. package/assets/fontFamily.ts +416 -0
  17. package/assets/fonts/Graphik-Black-App.ttf +0 -0
  18. package/assets/fonts/Graphik-BlackItalic-App.ttf +0 -0
  19. package/assets/fonts/Graphik-Bold-App.ttf +0 -0
  20. package/assets/fonts/Graphik-BoldItalic-App.ttf +0 -0
  21. package/assets/fonts/Graphik-Extralight-App.ttf +0 -0
  22. package/assets/fonts/Graphik-ExtralightItalic-App.ttf +0 -0
  23. package/assets/fonts/Graphik-Light-App.ttf +0 -0
  24. package/assets/fonts/Graphik-LightItalic-App.ttf +0 -0
  25. package/assets/fonts/Graphik-Medium-App.ttf +0 -0
  26. package/assets/fonts/Graphik-Medium-Web.woff2 +0 -0
  27. package/assets/fonts/Graphik-MediumItalic-App.ttf +0 -0
  28. package/assets/fonts/Graphik-Regular-App.ttf +0 -0
  29. package/assets/fonts/Graphik-Regular-Web.woff2 +0 -0
  30. package/assets/fonts/Graphik-RegularItalic-App.ttf +0 -0
  31. package/assets/fonts/Graphik-Semibold-App.ttf +0 -0
  32. package/assets/fonts/Graphik-Semibold-Web.woff2 +0 -0
  33. package/assets/fonts/Graphik-SemiboldItalic-App.ttf +0 -0
  34. package/assets/fonts/Graphik-Super-App.ttf +0 -0
  35. package/assets/fonts/Graphik-SuperItalic-App.ttf +0 -0
  36. package/assets/fonts/Graphik-Thin-App.ttf +0 -0
  37. package/assets/fonts/Graphik-ThinItalic-App.ttf +0 -0
  38. package/assets/fonts/Montserrat-Black.ttf +0 -0
  39. package/assets/fonts/Montserrat-BlackItalic.ttf +0 -0
  40. package/assets/fonts/Montserrat-Bold.ttf +0 -0
  41. package/assets/fonts/Montserrat-BoldItalic.ttf +0 -0
  42. package/assets/fonts/Montserrat-ExtraBold.ttf +0 -0
  43. package/assets/fonts/Montserrat-ExtraBoldItalic.ttf +0 -0
  44. package/assets/fonts/Montserrat-ExtraLight.ttf +0 -0
  45. package/assets/fonts/Montserrat-ExtraLightItalic.ttf +0 -0
  46. package/assets/fonts/Montserrat-Italic.ttf +0 -0
  47. package/assets/fonts/Montserrat-Light.ttf +0 -0
  48. package/assets/fonts/Montserrat-LightItalic.ttf +0 -0
  49. package/assets/fonts/Montserrat-Medium.ttf +0 -0
  50. package/assets/fonts/Montserrat-MediumItalic.ttf +0 -0
  51. package/assets/fonts/Montserrat-Regular.ttf +0 -0
  52. package/assets/fonts/Montserrat-SemiBold.ttf +0 -0
  53. package/assets/fonts/Montserrat-SemiBoldItalic.ttf +0 -0
  54. package/assets/fonts/Montserrat-Thin.ttf +0 -0
  55. package/assets/fonts/Montserrat-ThinItalic.ttf +0 -0
  56. package/assets/fonts/PublicoHeadline-Black-App.ttf +0 -0
  57. package/assets/fonts/PublicoHeadline-BlackItalic-App.ttf +0 -0
  58. package/assets/fonts/PublicoHeadline-Bold-App.ttf +0 -0
  59. package/assets/fonts/PublicoHeadline-Bold-Web.woff2 +0 -0
  60. package/assets/fonts/PublicoHeadline-BoldItalic-App.ttf +0 -0
  61. package/assets/fonts/PublicoHeadline-Extrabold-App.ttf +0 -0
  62. package/assets/fonts/PublicoHeadline-ExtraboldItalic-App.ttf +0 -0
  63. package/assets/fonts/PublicoHeadline-Italic-App.ttf +0 -0
  64. package/assets/fonts/PublicoHeadline-Light-App.ttf +0 -0
  65. package/assets/fonts/PublicoHeadline-LightItalic-App.ttf +0 -0
  66. package/assets/fonts/PublicoHeadline-Medium-App.ttf +0 -0
  67. package/assets/fonts/PublicoHeadline-MediumItalic-App.ttf +0 -0
  68. package/assets/fonts/PublicoHeadline-Roman-App.ttf +0 -0
  69. package/assets/fonts/PublicoHeadline-Roman-Web.woff2 +0 -0
  70. package/assets/fonts/PublicoText-Bold-App.ttf +0 -0
  71. package/assets/fonts/PublicoText-BoldItalic-App.ttf +0 -0
  72. package/assets/fonts/PublicoText-Italic-App.ttf +0 -0
  73. package/assets/fonts/PublicoText-Italic-Web.woff2 +0 -0
  74. package/assets/fonts/PublicoText-Roman-App.ttf +0 -0
  75. package/assets/fonts/PublicoText-Roman-Web.woff2 +0 -0
  76. package/assets/fonts/PublicoText-Semibold-App.ttf +0 -0
  77. package/assets/fonts/PublicoText-SemiboldItalic-App.ttf +0 -0
  78. package/assets/fonts/Roboto-Bold.ttf +0 -0
  79. package/assets/fonts/Roboto-Light.ttf +0 -0
  80. package/assets/fonts/Roboto-Medium.ttf +0 -0
  81. package/assets/fonts/Roboto-Regular.ttf +0 -0
  82. package/assets/fonts/TheSun-Bold.ttf +0 -0
  83. package/assets/fonts/TheSun-BoldItalic.ttf +0 -0
  84. package/assets/fonts/TheSun-HeavyCondensed.ttf +0 -0
  85. package/assets/fonts/TheSun-HeavyNarrow.ttf +0 -0
  86. package/assets/fonts/TheSun-Italic.ttf +0 -0
  87. package/assets/fonts/TheSun-Medium.ttf +0 -0
  88. package/assets/fonts/TheSun-MediumItalic.ttf +0 -0
  89. package/assets/fonts/TheSun-Regular.ttf +0 -0
  90. package/assets/fonts/TimesDigital-Bold.ttf +0 -0
  91. package/assets/fonts/TimesDigital-BoldItalic.ttf +0 -0
  92. package/assets/fonts/TimesDigital-BoldSC.ttf +0 -0
  93. package/assets/fonts/TimesDigital-Italic.ttf +0 -0
  94. package/assets/fonts/TimesDigital-Regular.ttf +0 -0
  95. package/assets/fonts/TimesDigital-RegularSC.ttf +0 -0
  96. package/assets/fonts/TimesDigitalW04-Bold.ttf +0 -0
  97. package/assets/fonts/TimesDigitalW04-BoldItalic.ttf +0 -0
  98. package/assets/fonts/TimesDigitalW04-BoldSC.ttf +0 -0
  99. package/assets/fonts/TimesDigitalW04-Italic.ttf +0 -0
  100. package/assets/fonts/TimesDigitalW04-Regular.ttf +0 -0
  101. package/assets/fonts/TimesDigitalW04-RegularSC.ttf +0 -0
  102. package/assets/fonts/TimesModern-Regular.otf +0 -0
  103. package/assets/fonts/attribuitions.mdx +35 -0
  104. package/assets/fonts/bitter-medium.woff +0 -0
  105. package/assets/fonts/bitter-medium.woff2 +0 -0
  106. package/assets/fonts/bitter-mediumitalic.woff +0 -0
  107. package/assets/fonts/bitter-mediumitalic.woff2 +0 -0
  108. package/assets/fonts/bitter-regular.woff +0 -0
  109. package/assets/fonts/bitter-regular.woff2 +0 -0
  110. package/assets/fonts/bitter-semibold.woff +0 -0
  111. package/assets/fonts/bitter-semibold.woff2 +0 -0
  112. package/assets/fonts/dmmono-medium.woff +0 -0
  113. package/assets/fonts/dmmono-medium.woff2 +0 -0
  114. package/assets/fonts/dmsans-bold-webfont.woff +0 -0
  115. package/assets/fonts/dmsans-bold-webfont.woff2 +0 -0
  116. package/assets/fonts/dmsans-bolditalic-webfont.woff +0 -0
  117. package/assets/fonts/dmsans-bolditalic-webfont.woff2 +0 -0
  118. package/assets/fonts/dmsans-italic-webfont.woff +0 -0
  119. package/assets/fonts/dmsans-italic-webfont.woff2 +0 -0
  120. package/assets/fonts/dmsans-medium-webfont.woff +0 -0
  121. package/assets/fonts/dmsans-medium-webfont.woff2 +0 -0
  122. package/assets/fonts/dmsans-mediumitalic-webfont.woff +0 -0
  123. package/assets/fonts/dmsans-mediumitalic-webfont.woff2 +0 -0
  124. package/assets/fonts/dmsans-regular-webfont.woff +0 -0
  125. package/assets/fonts/dmsans-regular-webfont.woff2 +0 -0
  126. package/assets/fonts/notosans-bold-webfont.woff +0 -0
  127. package/assets/fonts/notosans-bold-webfont.woff2 +0 -0
  128. package/assets/fonts/notosans-italic-webfont.woff +0 -0
  129. package/assets/fonts/notosans-italic-webfont.woff2 +0 -0
  130. package/assets/fonts/notosans-medium-webfont.woff +0 -0
  131. package/assets/fonts/notosans-medium-webfont.woff2 +0 -0
  132. package/assets/fonts/notosans-regular-webfont.woff +0 -0
  133. package/assets/fonts/notosans-regular-webfont.woff2 +0 -0
  134. package/assets/fonts/poppins-bold-webfont.woff +0 -0
  135. package/assets/fonts/poppins-bold-webfont.woff2 +0 -0
  136. package/assets/fonts/poppins-bolditalic-webfont.woff +0 -0
  137. package/assets/fonts/poppins-bolditalic-webfont.woff2 +0 -0
  138. package/assets/fonts/poppins-extrabold-webfont.woff +0 -0
  139. package/assets/fonts/poppins-extrabold-webfont.woff2 +0 -0
  140. package/assets/fonts/poppins-extrabolditalic-webfont.woff +0 -0
  141. package/assets/fonts/poppins-extrabolditalic-webfont.woff2 +0 -0
  142. package/assets/fonts/poppins-italic-webfont.woff +0 -0
  143. package/assets/fonts/poppins-italic-webfont.woff2 +0 -0
  144. package/assets/fonts/poppins-light-webfont.woff +0 -0
  145. package/assets/fonts/poppins-light-webfont.woff2 +0 -0
  146. package/assets/fonts/poppins-lightitalic-webfont.woff +0 -0
  147. package/assets/fonts/poppins-lightitalic-webfont.woff2 +0 -0
  148. package/assets/fonts/poppins-medium-webfont.woff +0 -0
  149. package/assets/fonts/poppins-medium-webfont.woff2 +0 -0
  150. package/assets/fonts/poppins-mediumitalic-webfont.woff +0 -0
  151. package/assets/fonts/poppins-mediumitalic-webfont.woff2 +0 -0
  152. package/assets/fonts/poppins-regular-webfont.woff +0 -0
  153. package/assets/fonts/poppins-regular-webfont.woff2 +0 -0
  154. package/assets/fonts/poppins-semibold-webfont.woff +0 -0
  155. package/assets/fonts/poppins-semibold-webfont.woff2 +0 -0
  156. package/assets/fonts/poppins-semibolditalic-webfont.woff +0 -0
  157. package/assets/fonts/poppins-semibolditalic-webfont.woff2 +0 -0
  158. package/assets/fonts/source-serif-pro-600.woff +0 -0
  159. package/assets/fonts/source-serif-pro-600.woff2 +0 -0
  160. package/assets/fonts/source-serif-pro-600italic.woff +0 -0
  161. package/assets/fonts/source-serif-pro-600italic.woff2 +0 -0
  162. package/assets/fonts/source-serif-pro-700.woff +0 -0
  163. package/assets/fonts/source-serif-pro-700.woff2 +0 -0
  164. package/assets/fonts/source-serif-pro-700italic.woff +0 -0
  165. package/assets/fonts/source-serif-pro-700italic.woff2 +0 -0
  166. package/assets/fonts/source-serif-pro-italic.woff +0 -0
  167. package/assets/fonts/source-serif-pro-italic.woff2 +0 -0
  168. package/assets/fonts/source-serif-pro-regular.woff +0 -0
  169. package/assets/fonts/source-serif-pro-regular.woff2 +0 -0
  170. package/components/AccountDropdown/AccountDropdown.tsx +65 -0
  171. package/components/AccountDropdown/__tests__/AcountDropdown.test.tsx +83 -0
  172. package/components/AccountDropdown/accounts.ts +47 -0
  173. package/components/AccountDropdown/index.tsx +2 -0
  174. package/components/AccountDropdown/utils.ts +21 -0
  175. package/components/ErrorPage/ErrorPage.tsx +135 -0
  176. package/components/ad/index.tsx +35 -0
  177. package/components/article/Highlights.tsx +77 -0
  178. package/components/article/MetaBox.tsx +50 -0
  179. package/components/article/RelatedArticles.tsx +53 -0
  180. package/components/article/RelatedTopics.tsx +59 -0
  181. package/components/article/ShareBarComponent.tsx +53 -0
  182. package/components/article/__tests__/__snapshots__/index.test.tsx.snap +1568 -0
  183. package/components/article/__tests__/index.test.tsx +171 -0
  184. package/components/article/__tests__/mocks.ts +105 -0
  185. package/components/article/index.tsx +320 -0
  186. package/components/common/BackToHomepage.tsx +37 -0
  187. package/components/common/NavLink.tsx +26 -0
  188. package/components/common/NewskitLogo.tsx +52 -0
  189. package/components/common/NewskitLogoShort.tsx +48 -0
  190. package/components/common/SectionTitleBar.tsx +57 -0
  191. package/components/common/ShareButton.tsx +18 -0
  192. package/components/common/ViewMoreButton.tsx +20 -0
  193. package/components/common/iconNavLink/IconNavLink.tsx +70 -0
  194. package/components/common/iconNavLink/__tests__/IconNavLink.test.tsx +35 -0
  195. package/components/common/iconNavLink/index.ts +3 -0
  196. package/components/common/iconNavLink/types.ts +24 -0
  197. package/components/common/icons/IconAccount.tsx +13 -0
  198. package/components/common/icons/IconFilledTwitter.tsx +14 -0
  199. package/components/common/icons/StyledIconFilledChevronRight.tsx +12 -0
  200. package/components/footer/__snapshots__/index.test.tsx.snap +192 -0
  201. package/components/footer/index.test.tsx +13 -0
  202. package/components/footer/index.tsx +151 -0
  203. package/components/header/banner-messages.ts +39 -0
  204. package/components/header/index.tsx +52 -0
  205. package/components/header/navigation-links.ts +20 -0
  206. package/components/layout/Gutter.tsx +11 -0
  207. package/components/layout/LayoutTemplate.tsx +26 -0
  208. package/components/layout/MainGrid.tsx +66 -0
  209. package/components/layout/index.tsx +3 -0
  210. package/components/layout/layoutTypes.ts +13 -0
  211. package/components/section/ArticleSlice.tsx +35 -0
  212. package/components/section/CollectionBlock.tsx +54 -0
  213. package/components/section/SectionContext.tsx +9 -0
  214. package/components/section/__tests__/ArticleSlice.test.tsx +128 -0
  215. package/components/section/__tests__/CollectionBlock.test.tsx +83 -0
  216. package/components/section/__tests__/pageBlock.test.tsx +24 -0
  217. package/components/section/__tests__/sectionUtils.test.ts +94 -0
  218. package/components/section/index.tsx +39 -0
  219. package/components/section/layouts/Block.tsx +79 -0
  220. package/components/section/layouts/FallBack.tsx +24 -0
  221. package/components/section/layouts/Lead.tsx +43 -0
  222. package/components/section/layouts/Rows.tsx +82 -0
  223. package/components/section/layouts/SectionTitle.tsx +57 -0
  224. package/components/section/layouts/__tests__/Lead.test.tsx +37 -0
  225. package/components/section/layouts/__tests__/Rows.test.tsx +40 -0
  226. package/components/section/layouts/__tests__/SectionTitle.test.tsx +37 -0
  227. package/components/section/layouts/__tests__/__snapshots__/Lead.test.tsx.snap +180 -0
  228. package/components/section/layouts/__tests__/__snapshots__/SectionTitle.test.tsx.snap +354 -0
  229. package/components/section/layouts/gridUtils.ts +21 -0
  230. package/components/section/layouts/index.tsx +4 -0
  231. package/components/section/layouts/types.ts +13 -0
  232. package/components/section/pageBlock.tsx +23 -0
  233. package/components/section/sectionUtils.ts +61 -0
  234. package/components/teaser/index.test.tsx +50 -0
  235. package/components/teaser/index.tsx +64 -0
  236. package/components/teaser/teaserVariants.ts +33 -0
  237. package/components/teaser/variant-types.ts +49 -0
  238. package/components/teaser/variants/featureVariant.ts +42 -0
  239. package/components/teaser/variants/horizontal.ts +14 -0
  240. package/components/teaser/variants/titleTeaserVariant.ts +32 -0
  241. package/components/teaser/variants/titleVertical.ts +24 -0
  242. package/components/utils/index.test.ts +12 -0
  243. package/components/utils/index.ts +6 -0
  244. package/config/__tests__/index.test.ts +54 -0
  245. package/config/environment.ts +80 -0
  246. package/config/index.ts +2 -0
  247. package/config/multiTenancy.ts +11 -0
  248. package/constants/index.ts +2 -0
  249. package/context/app-context/AppContext.test.tsx +56 -0
  250. package/context/app-context/holidayStopListContextOverrides.ts +18 -0
  251. package/context/app-context/index.tsx +34 -0
  252. package/context/app-context/paymentMethodContext.tsx +70 -0
  253. package/context/index.tsx +2 -0
  254. package/context/multi-tenancy/MultiTenancy.test.tsx +48 -0
  255. package/context/multi-tenancy/index.tsx +32 -0
  256. package/css/index.ts +15 -0
  257. package/cypress/axe/terminal-log.js +19 -0
  258. package/cypress/config/visual.config.ts +11 -0
  259. package/cypress/config/visual.skip.config.ts +10 -0
  260. package/cypress/e2e/account/accessibility.cy.js +88 -0
  261. package/cypress/e2e/account/banners.cy.js +169 -0
  262. package/cypress/e2e/account/cancellation.cy.js +219 -0
  263. package/cypress/e2e/account/holiday-stops.cy.js +109 -0
  264. package/cypress/e2e/account/main-api.cy.js +102 -0
  265. package/cypress/e2e/account/newsletters-and-alerts.cy.js +149 -0
  266. package/cypress/e2e/account/newsletters-page.cy.js +69 -0
  267. package/cypress/e2e/account/payment-page.cy.js +98 -0
  268. package/cypress/e2e/account/personal-details.cy.js +1161 -0
  269. package/cypress/e2e/account/subscription-and-billing.cy.js +434 -0
  270. package/cypress/e2e/checkout/account-creation.cy.js +8 -0
  271. package/cypress/e2e/checkout/payment-details.cy.js +19 -0
  272. package/cypress/e2e/core/home-page.cy.js +6 -0
  273. package/cypress/e2e/help-hub/accessibility.cy.js +35 -0
  274. package/cypress/e2e/help-hub/article-page.cy.js +115 -0
  275. package/cypress/e2e/help-hub/landing-page.cy.js +94 -0
  276. package/cypress/e2e/help-hub/result-page.cy.js +117 -0
  277. package/cypress/fixtures/empty.html +10 -0
  278. package/cypress/fixtures/example.json +5 -0
  279. package/cypress/fixtures/holiday-stops.json +40 -0
  280. package/cypress/fixtures/paymentFailure.js +24 -0
  281. package/cypress/fixtures/testDates.js +26 -0
  282. package/cypress/support/commands.js +125 -0
  283. package/cypress/support/consentValues.js +5839 -0
  284. package/cypress/support/e2e.js +22 -0
  285. package/cypress/support/users.js +58 -0
  286. package/cypress/visual/account/print-visual-regression.cy.js +9 -0
  287. package/cypress/visual/account/visual-regression.cy.js +40 -0
  288. package/cypress/visual/empty/empty.cy.js +5 -0
  289. package/cypress.config.ts +38 -0
  290. package/helpers/__tests__/getUser.test.ts +45 -0
  291. package/helpers/__tests__/logger.test.ts +62 -0
  292. package/helpers/__tests__/useThemeDropdownObject.test.ts +49 -0
  293. package/helpers/a11y.ts +7 -0
  294. package/helpers/addCacheHeaders.ts +8 -0
  295. package/helpers/getUser.ts +37 -0
  296. package/helpers/getYear.ts +1 -0
  297. package/helpers/global-types.ts +186 -0
  298. package/helpers/logger/getWinstonLogger.ts +19 -0
  299. package/helpers/logger/index.ts +20 -0
  300. package/helpers/logger/replaceConsoleWithLogger.ts +14 -0
  301. package/helpers/mocks/articleMock.ts +50 -0
  302. package/helpers/mocks/getPageMock.ts +369 -0
  303. package/helpers/mocks/getRadioPostMock.ts +71 -0
  304. package/helpers/mocks/getRadioPostsMock.ts +26 -0
  305. package/helpers/mocks/getRecommendationsMock.ts +20 -0
  306. package/helpers/mocks/index.ts +7 -0
  307. package/helpers/multiTenancy.ts +19 -0
  308. package/helpers/setupTests.ts +5 -0
  309. package/helpers/test-utils.tsx +33 -0
  310. package/helpers/useThemeDropdownObject.tsx +73 -0
  311. package/infrastructure/.circleci/config.yml +1187 -0
  312. package/infrastructure/INFRASTRUCTURE.md +142 -0
  313. package/infrastructure/build_and_deploy.png +0 -0
  314. package/infrastructure/helm/Chart.yaml +21 -0
  315. package/infrastructure/helm/templates/_helpers.tpl +87 -0
  316. package/infrastructure/helm/templates/deployment.yaml +45 -0
  317. package/infrastructure/helm/templates/horizontalpodautoscaler.yaml +21 -0
  318. package/infrastructure/helm/templates/ingress.yaml +31 -0
  319. package/infrastructure/helm/templates/networkpolicy.yaml +26 -0
  320. package/infrastructure/helm/templates/secret.yaml +12 -0
  321. package/infrastructure/helm/templates/service.yaml +15 -0
  322. package/infrastructure/helm/values-dev.yaml +39 -0
  323. package/infrastructure/helm/values-local.yaml +30 -0
  324. package/infrastructure/helm/values-pr.yaml +40 -0
  325. package/infrastructure/helm/values-prod.yaml +39 -0
  326. package/infrastructure/helm/values-staging.yaml +39 -0
  327. package/infrastructure/helm/values.yaml +44 -0
  328. package/infrastructure/pull_request.png +0 -0
  329. package/infrastructure/release-documentation-cli.config.json +18 -0
  330. package/infrastructure/remove_pr.png +0 -0
  331. package/infrastructure/terraform-ecr/dev.tfvars +3 -0
  332. package/infrastructure/terraform-ecr/ecr.tf +69 -0
  333. package/infrastructure/terraform-ecr/main.tf +3 -0
  334. package/infrastructure/terraform-ecr/variables.tf +19 -0
  335. package/infrastructure/terraform-ecr/versions.tf +12 -0
  336. package/infrastructure/terraform-newrelic/alerts.tf +290 -0
  337. package/infrastructure/terraform-newrelic/dashboards.tf +347 -0
  338. package/infrastructure/terraform-newrelic/dev.tfvars +3 -0
  339. package/infrastructure/terraform-newrelic/lighthouse-script-dev.js +53 -0
  340. package/infrastructure/terraform-newrelic/lighthouse-script-prod.js +54 -0
  341. package/infrastructure/terraform-newrelic/lighthouse-script-stage.js +54 -0
  342. package/infrastructure/terraform-newrelic/main.tf +11 -0
  343. package/infrastructure/terraform-newrelic/monitors.tf +11 -0
  344. package/infrastructure/terraform-newrelic/prod.tfvars +3 -0
  345. package/infrastructure/terraform-newrelic/staging.tfvars +3 -0
  346. package/infrastructure/terraform-newrelic/variables.tf +33 -0
  347. package/infrastructure/terraform-newrelic/versions.tf +18 -0
  348. package/jest.config.js +40 -0
  349. package/jest.config.pact.js +18 -0
  350. package/newrelic.js +86 -0
  351. package/next-env.d.ts +5 -0
  352. package/next.config.js +95 -0
  353. package/package.json +127 -0
  354. package/pages/_app.tsx +117 -0
  355. package/pages/_document.tsx +112 -0
  356. package/pages/_error.tsx +68 -0
  357. package/pages/account/add/[field].tsx +34 -0
  358. package/pages/account/cancellation/index.tsx +22 -0
  359. package/pages/account/delete/confirm/index.tsx +16 -0
  360. package/pages/account/delete/error/index.tsx +16 -0
  361. package/pages/account/delete/index.tsx +16 -0
  362. package/pages/account/delete/success/index.tsx +16 -0
  363. package/pages/account/dream-team/index.tsx +19 -0
  364. package/pages/account/edit/[field].tsx +45 -0
  365. package/pages/account/family/index.tsx +19 -0
  366. package/pages/account/holiday-stop/index.tsx +19 -0
  367. package/pages/account/holiday-stop/previous-holiday-stops.tsx +19 -0
  368. package/pages/account/holiday-stop/upcoming-holiday-stops.tsx +19 -0
  369. package/pages/account/index.tsx +33 -0
  370. package/pages/account/newsletters/index.tsx +19 -0
  371. package/pages/account/newsletters-and-alerts/index.tsx +24 -0
  372. package/pages/account/payment/[paymentMethod].tsx +37 -0
  373. package/pages/account/payment/index.tsx +23 -0
  374. package/pages/account/payment-methods/index.tsx +21 -0
  375. package/pages/account/subscription-and-billing/index.tsx +29 -0
  376. package/pages/api/account/get-session/index.ts +33 -0
  377. package/pages/api/account/invoice/index.ts +35 -0
  378. package/pages/api/account/mutate/index.ts +8 -0
  379. package/pages/api/account/query/index.ts +7 -0
  380. package/pages/api/auth/[...nextauth].ts +11 -0
  381. package/pages/api/competitions-sitemap.ts +19 -0
  382. package/pages/api/feed.ts +23 -0
  383. package/pages/api/health-check.ts +7 -0
  384. package/pages/api/news-sitemap.ts +18 -0
  385. package/pages/api/pages-sitemap.ts +27 -0
  386. package/pages/api/robots.ts +19 -0
  387. package/pages/api/sitemap.ts +24 -0
  388. package/pages/checkout/account-creation/index.tsx +13 -0
  389. package/pages/checkout/payment-details/index.tsx +10 -0
  390. package/pages/empty.tsx +6 -0
  391. package/pages/help-hub/article/[title]/index.tsx +25 -0
  392. package/pages/help-hub/error.tsx +23 -0
  393. package/pages/help-hub/index.tsx +51 -0
  394. package/pages/help-hub/results.tsx +25 -0
  395. package/pages/index.tsx +19 -0
  396. package/pages/player/brightcove.tsx +19 -0
  397. package/pages/rss/create/index.tsx +18 -0
  398. package/pages/rss/feed/[feedId]/index.tsx +21 -0
  399. package/pages/rss/index.tsx +18 -0
  400. package/public/ads.min.js +8175 -0
  401. package/public/assets/display-base-header.svg +25 -0
  402. package/public/assets/display-personalDetails-header-sun.svg +55 -0
  403. package/public/assets/display-personalDetails-header-times.svg +90 -0
  404. package/public/assets/display-personalDetails-header-vr.svg +46 -0
  405. package/public/assets/dream-team.svg +10 -0
  406. package/public/assets/navigationPrimary-brandMark-sun.svg +9 -0
  407. package/public/assets/navigationPrimary-brandMark-times.svg +18 -0
  408. package/public/assets/navigationPrimary-brandMark-vr.svg +10 -0
  409. package/public/assets/newsletters/bestoftimes-101.png +0 -0
  410. package/public/assets/newsletters/books-115.png +0 -0
  411. package/public/assets/newsletters/environment-140.png +0 -0
  412. package/public/assets/newsletters/fashion-108.png +0 -0
  413. package/public/assets/newsletters/food-106.png +0 -0
  414. package/public/assets/newsletters/football-111.png +0 -0
  415. package/public/assets/newsletters/ireland-152.png +0 -0
  416. package/public/assets/newsletters/law-121.png +0 -0
  417. package/public/assets/newsletters/money-107.png +0 -0
  418. package/public/assets/newsletters/moneymentor-150.png +0 -0
  419. package/public/assets/newsletters/placeholder.png +0 -0
  420. package/public/assets/newsletters/politics-119.png +0 -0
  421. package/public/assets/newsletters/property-113.png +0 -0
  422. package/public/assets/newsletters/puzzles-125.png +0 -0
  423. package/public/assets/newsletters/scotland-134.png +0 -0
  424. package/public/assets/newsletters/theatre-127.png +0 -0
  425. package/public/assets/newsletters/travel-149.png +0 -0
  426. package/public/assets/newsletters/tv-133.png +0 -0
  427. package/public/assets/newsletters/us-153.png +0 -0
  428. package/public/assets/no-subscription.svg +15 -0
  429. package/public/assets/pending-activation.svg +16 -0
  430. package/public/assets/personal-details-header.svg +91 -0
  431. package/public/assets/plchldr150x100.png +0 -0
  432. package/public/assets/previous-subscription.svg +15 -0
  433. package/public/assets/primary-navigation-logo-white.svg +6 -0
  434. package/public/assets/primary-navigation-logo.svg +6 -0
  435. package/public/assets/tls-logo-white.svg +3 -0
  436. package/public/favicon.ico +0 -0
  437. package/public/icon.png +0 -0
  438. package/public/prebid.min.js +1 -0
  439. package/queries/getPage.ts +100 -0
  440. package/queries/getRadioPost.ts +61 -0
  441. package/queries/getRadioPosts.ts +16 -0
  442. package/queries/getUserSubscription.ts +9 -0
  443. package/queries/index.ts +3 -0
  444. package/scripts/k8s-local.sh +137 -0
  445. package/scripts/nr-exports.sh +7 -0
  446. package/temp/_app.tsx +15 -0
  447. package/temp/_document.tsx +61 -0
  448. package/temp/app-context-test.tsx +51 -0
  449. package/temp/app-context.tsx +25 -0
  450. package/temp/config-index.ts +1 -0
  451. package/temp/context-index.tsx +1 -0
  452. package/temp/header.tsx +45 -0
  453. package/temp/next.config.js +56 -0
  454. package/tsconfig.json +51 -0
  455. package/tsconfig.test.json +6 -0
  456. package/validation/index.tsx +24 -0
@@ -0,0 +1,48 @@
1
+ import React from 'react'
2
+ // eslint-disable-next-line react/no-deprecated
3
+ import { render } from 'react-dom'
4
+ import { timesTheme } from '@newskit-render/shared-components'
5
+ import { Publisher } from '@newskit-render/api'
6
+ import { AppContext, AppContextProvider } from '../app-context'
7
+ import MultiTenancyProvider, { useMultiTenancy } from '.'
8
+
9
+ const container = document.createElement('div')
10
+ document.body.appendChild(container)
11
+
12
+ const TestComponent = ({ mockFunction }) => {
13
+ const accountData = useMultiTenancy()
14
+ mockFunction(accountData)
15
+ return null
16
+ }
17
+
18
+ describe('Multi-Tenancy context', () => {
19
+ it('should select the correct theme based on the tenant', () => {
20
+ render(
21
+ <MultiTenancyProvider tenant={Publisher.TIMES}>
22
+ <AppContextProvider>
23
+ <AppContext.Consumer>
24
+ {({ theme }) => <div>{JSON.stringify(theme)}</div>}
25
+ </AppContext.Consumer>
26
+ </AppContextProvider>
27
+ </MultiTenancyProvider>,
28
+ container
29
+ )
30
+
31
+ expect(container.textContent).toBe(JSON.stringify(timesTheme))
32
+ })
33
+
34
+ it('should receive proper context', () => {
35
+ const mockFunction = jest.fn()
36
+ render(
37
+ <MultiTenancyProvider tenant={Publisher.DEMO}>
38
+ <TestComponent mockFunction={mockFunction} />
39
+ </MultiTenancyProvider>,
40
+ container
41
+ )
42
+
43
+ expect(mockFunction).toHaveBeenCalledWith({
44
+ tenant: Publisher.DEMO,
45
+ getTenantString: expect.any(Function),
46
+ })
47
+ })
48
+ })
@@ -0,0 +1,32 @@
1
+ import React, { createContext, useContext } from 'react'
2
+ import get from 'lodash.get'
3
+ import { Publisher } from '@newskit-render/api'
4
+ import { translationsMap } from '../../config'
5
+
6
+ export interface MultiTenancyContextProps {
7
+ children?: React.ReactNode
8
+ tenant?: Publisher
9
+ getTenantString?: (key: string, defaultValue?: string) => string
10
+ }
11
+
12
+ const MultiTenancyContext = createContext<MultiTenancyContextProps>({})
13
+
14
+ const MultiTenancyProvider: React.FC<MultiTenancyContextProps> = ({
15
+ tenant,
16
+ children,
17
+ }) => (
18
+ <MultiTenancyContext.Provider
19
+ value={{
20
+ tenant,
21
+ getTenantString: (key: string, defaultValue: string = ''): string => {
22
+ const data = translationsMap[tenant]
23
+ return get(data, key, defaultValue)
24
+ },
25
+ }}
26
+ >
27
+ {children}
28
+ </MultiTenancyContext.Provider>
29
+ )
30
+
31
+ export const useMultiTenancy = () => useContext(MultiTenancyContext)
32
+ export default MultiTenancyProvider
package/css/index.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { css } from 'newskit'
2
+
3
+ export const GlobalStyling = css`
4
+ html,
5
+ body {
6
+ padding: 0;
7
+ margin: 0;
8
+ font-family: 'Noto Sans', -apple-system, BlinkMacSystemFont, Segoe UI,
9
+ Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue,
10
+ sans-serif;
11
+ }
12
+ * {
13
+ box-sizing: border-box;
14
+ }
15
+ `
@@ -0,0 +1,19 @@
1
+ export const terminalLog = (violations) => {
2
+ cy.task(
3
+ 'log',
4
+ `${violations.length} accessibility violation${
5
+ violations.length === 1 ? '' : 's'
6
+ } ${violations.length === 1 ? 'was' : 'were'} detected`
7
+ )
8
+ // pluck specific keys to keep the table readable
9
+ const violationData = violations.map(
10
+ ({ id, impact, description, nodes }) => ({
11
+ id,
12
+ impact,
13
+ description,
14
+ nodes: nodes.length,
15
+ })
16
+ )
17
+
18
+ cy.task('table', violationData)
19
+ }
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from 'cypress'
2
+
3
+ export default defineConfig({
4
+ e2e: {
5
+ defaultCommandTimeout: 20000,
6
+ baseUrl: 'http://localhost:3000',
7
+ video: false,
8
+ screenshotOnRunFailure: false,
9
+ specPattern: './cypress/visual/**/*.cy.{js,jsx,ts,tsx}',
10
+ },
11
+ })
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'cypress'
2
+
3
+ export default defineConfig({
4
+ e2e: {
5
+ baseUrl: null,
6
+ video: false,
7
+ screenshotOnRunFailure: false,
8
+ specPattern: './cypress/visual/empty/**/*.cy.{js,jsx,ts,tsx}',
9
+ },
10
+ })
@@ -0,0 +1,88 @@
1
+ import { terminalLog } from '../../axe/terminal-log'
2
+
3
+ const pages = [
4
+ { url: '/account', name: 'Personal Details' },
5
+ { url: '/account/edit/name', name: 'Name form' },
6
+ { url: '/account/edit/displayName', name: 'Display Name form' },
7
+ { url: '/account/edit/email', name: 'Email form' },
8
+ { url: '/account/edit/password', name: 'Password form' },
9
+ { url: '/account/edit/mobile', name: 'Mobile phone form' },
10
+ { url: '/account/edit/landline', name: 'Landline phone form' },
11
+ {
12
+ url: '/account/edit/address',
13
+ name: 'Address form',
14
+ },
15
+ {
16
+ url: '/account/subscription-and-billing',
17
+ name: 'Subscription and Billing',
18
+ },
19
+ // TODO: Urgerntly find the reason why this state is failing! Uncomment immediately after that
20
+ // {
21
+ // url: '/account/payment',
22
+ // name: 'Payment form',
23
+ // skip: { skipFailures: true },
24
+ // error being caused by Stripe, ignore at this time
25
+ // },
26
+ {
27
+ url: '/account/newsletters-and-alerts',
28
+ name: 'Newsletters and Alerts',
29
+ },
30
+ {
31
+ url: '/account/edit/commenting-notifications',
32
+ name: 'Commenting Notifications form',
33
+ },
34
+ {
35
+ url: '/account/edit/delivery-instructions',
36
+ name: 'Delivery Instructions form',
37
+ },
38
+ {
39
+ url: '/account/holiday-stop',
40
+ name: 'Holiday Stops',
41
+ },
42
+ ]
43
+
44
+ describe('Page accessibility', () => {
45
+ pages.forEach(({ url, name, skip }) => {
46
+ it(`Should pass a11y tests: ${name}`, () => {
47
+ cy.mockConsentAndVisit(url)
48
+ const isAddressForm = name === 'Address form'
49
+ if (isAddressForm) {
50
+ cy.get('[data-testid="full-address"]').should('be.visible')
51
+ }
52
+ cy.injectAxe()
53
+ if (isAddressForm) {
54
+ cy.configureAxe({
55
+ rules: [
56
+ {
57
+ id: 'autocomplete-valid',
58
+ enabled: false,
59
+ },
60
+ ],
61
+ })
62
+ }
63
+ cy.checkA11y(
64
+ {
65
+ exclude: ['meta[name="viewport"]'],
66
+ },
67
+ null,
68
+ terminalLog,
69
+ skip
70
+ )
71
+ })
72
+ })
73
+ })
74
+
75
+ describe('SkipToContent', () => {
76
+ beforeEach(() => {
77
+ cy.GetAcsSession()
78
+ cy.mockConsentAndVisit('/account')
79
+ })
80
+
81
+ it('should take the user to the beginning of the main area', () => {
82
+ cy.get('[data-testid="skip-to-content"]').as('skiptocontentBtn')
83
+ cy.get('@skiptocontentBtn').focus()
84
+ cy.get('@skiptocontentBtn').should('contain', 'Skip to main content')
85
+ cy.get('@skiptocontentBtn').click()
86
+ cy.focused().should('have.id', 'main')
87
+ })
88
+ })
@@ -0,0 +1,169 @@
1
+ import {
2
+ paymentFailureFirstNotice,
3
+ paymentFailureSecondNotice,
4
+ paymentFailureTerminatedNotice,
5
+ serviceCancellationDate,
6
+ } from '../../fixtures/paymentFailure'
7
+
8
+ describe('Banners', () => {
9
+ it('Should show first notice', () => {
10
+ cy.GetTnlAcsSession('tnl')
11
+ cy.mockConsent()
12
+ cy.visitAndOverrideNextData(
13
+ '/account',
14
+ 'props.pageProps.user.paymentFailure',
15
+ paymentFailureFirstNotice
16
+ )
17
+
18
+ cy.get('[data-testid="past-due-banner"]').should('be.visible')
19
+ cy.contains("We haven't been able to take payment")
20
+ cy.contains(
21
+ 'You may need to update your payment details to keep your subscription.'
22
+ )
23
+ cy.get('[data-testid="buttonLink"]').should('be.visible')
24
+ cy.get('[data-testid="banner-close-button"]').should('be.visible')
25
+ cy.contains('Update payment details')
26
+ })
27
+
28
+ it('Should show second notice', () => {
29
+ cy.GetTnlAcsSession('tnl')
30
+ cy.mockConsent()
31
+ cy.visitAndOverrideNextData(
32
+ '/account',
33
+ 'props.pageProps.user.paymentFailure',
34
+ paymentFailureSecondNotice
35
+ )
36
+
37
+ cy.get('[data-testid="past-due-banner"]').should('be.visible')
38
+ cy.contains('Act now to keep your subscription')
39
+ cy.contains(
40
+ 'We’ve tried several times, but haven’t been able to take payment. Please update your payment details to keep your subscription.'
41
+ )
42
+ cy.get('[data-testid="buttonLink"]').should('be.visible')
43
+ cy.get('[data-testid="banner-close-button"]').should('be.visible')
44
+ cy.contains('Update payment details')
45
+ })
46
+
47
+ it('Should show terminated notice', () => {
48
+ cy.GetTnlAcsSession('tnl')
49
+ cy.mockConsent()
50
+ cy.visitAndOverrideNextData(
51
+ '/account',
52
+ 'props.pageProps.user.paymentFailure',
53
+ paymentFailureTerminatedNotice
54
+ )
55
+
56
+ cy.get('[data-testid="past-due-banner"]').should('be.visible')
57
+ cy.contains('Your subscription has been terminated')
58
+ cy.contains(
59
+ 'We didn’t receive payment for your subscription. To reactivate it, please call XXXX-XXX-XXXX. Or click here.'
60
+ )
61
+ cy.get('[data-testid="banner-close-button"]').should('be.visible')
62
+ })
63
+
64
+ it('Should show to be cancelled notice', () => {
65
+ cy.GetTnlAcsSession('tnl')
66
+ cy.mockConsent()
67
+ cy.visitAndOverrideNextData(
68
+ '/account',
69
+ 'props.pageProps.user.subscriptions[0].serviceCancellationDate',
70
+ serviceCancellationDate
71
+ )
72
+
73
+ cy.get('[data-testid="past-due-banner"]').should('be.visible')
74
+ cy.contains('Your subscription will end soon.')
75
+ cy.contains(
76
+ `You have cancelled your subscription and will lose access to all benefits on ${new Date(
77
+ serviceCancellationDate
78
+ ).getDate()}/${
79
+ new Date(serviceCancellationDate).getMonth() + 1
80
+ }/${new Date(
81
+ serviceCancellationDate
82
+ ).getFullYear()}. To re-activate your subscription call XXXX-XXX-XXXX.`
83
+ )
84
+ cy.get('[data-testid="banner-close-button"]').should('be.visible')
85
+ })
86
+
87
+ it('Should show expired subscription view with terminated notice', () => {
88
+ cy.GetAcsSession('expiredSub')
89
+ cy.mockConsent()
90
+ const date = new Date(Date.now() - 24 * 35 * 3600 * 1000)
91
+ cy.visit('/account/subscription-and-billing', {
92
+ onBeforeLoad: (win) => {
93
+ let nextData
94
+
95
+ Object.defineProperty(win, '__NEXT_DATA__', {
96
+ set(o) {
97
+ // eslint-disable-next-line no-param-reassign
98
+ o.props.pageProps.user.paymentFailure = {
99
+ active: true,
100
+ startDate: date.toISOString(),
101
+ }
102
+ // eslint-disable-next-line no-param-reassign
103
+ o.props.pageProps.user.subscriptions[0].serviceCancellationDate =
104
+ date.toISOString()
105
+ nextData = o
106
+ },
107
+ get() {
108
+ return nextData
109
+ },
110
+ })
111
+ },
112
+ })
113
+ Cypress.on('uncaught:exception', (err, runnable) => {
114
+ return false
115
+ })
116
+
117
+ cy.contains('You’ve previously had a subscription that has now expired')
118
+
119
+ cy.contains(
120
+ `On ${date.toLocaleDateString(
121
+ 'en-GB'
122
+ )} your Digital subscription expired. You can re-subscribe by confirming a few details or view other packages that are available.`
123
+ )
124
+ cy.get('[data-testid="primary-button"]')
125
+ .should('have.attr', 'href', '/title-re-subscribe')
126
+ .contains('Re-subscribe')
127
+ cy.get('[data-testid="secondary-button"]')
128
+ .should('have.attr', 'href', '/title-subscription-options')
129
+ .contains('View subscription options')
130
+ })
131
+
132
+ it('Should not show banner once it has been dismissed', () => {
133
+ cy.GetAcsSession('withSub')
134
+ cy.mockConsent()
135
+ const date = new Date(Date.now() + 20 * 30 * 3600 * 1000)
136
+
137
+ cy.visitAndOverrideNextData(
138
+ '/account/subscription-and-billing',
139
+ 'props.pageProps.user.subscriptions[0].serviceCancellationDate',
140
+ date.toISOString()
141
+ )
142
+ cy.get('[data-testid="past-due-banner"]').should('be.visible')
143
+ cy.contains('Your subscription will end soon.').should('exist')
144
+ cy.get('[data-testid="banner-close-button"]').filter(':visible').click()
145
+ cy.reload()
146
+ cy.get('[data-testid="past-due-banner"]').should('not.exist')
147
+ })
148
+
149
+ it('Should not display banner if set date is passed current date', () => {
150
+ cy.GetAcsSession('withSub')
151
+ cy.mockConsent()
152
+ const date = new Date(Date.now() - 24 * 35 * 3600 * 1000)
153
+ cy.visitAndOverrideNextData(
154
+ '/account/subscription-and-billing',
155
+ 'props.pageProps.user.subscriptions[0].serviceCancellationDate',
156
+ date.toISOString()
157
+ )
158
+ cy.get('[data-testid="past-due-banner"]').should('be.visible')
159
+ cy.contains('Your subscription has been cancelled.').then(() => {
160
+ window.localStorage.setItem(
161
+ 'cancelledBanner',
162
+ new Date(new Date().setDate(new Date().getDate() - 7)).toDateString()
163
+ )
164
+ })
165
+ cy.reload()
166
+ cy.get('[data-testid="past-due-banner"]').should('not.exist')
167
+ cy.clearLocalStorage()
168
+ })
169
+ })
@@ -0,0 +1,219 @@
1
+ import { paymentFailure } from '../../fixtures/paymentFailure'
2
+
3
+ import { serviceCancellationDate } from '../../fixtures/paymentFailure'
4
+
5
+ describe('Cancellation page', () => {
6
+ describe('Find a better name', () => {
7
+ beforeEach(() => {
8
+ cy.GetTnlAcsSession('digitalCard')
9
+ cy.mockConsentAndVisit('/account/cancellation')
10
+ })
11
+
12
+ it('Should show user cancellation reasons', () => {
13
+ cy.contains('Please tell us why you want to cancel')
14
+ cy.contains(
15
+ 'We value your opinion and this information will help us understand how we can improve our product and the services we provide.'
16
+ )
17
+
18
+ cy.contains('Select one option from the list below.')
19
+
20
+ cy.contains(
21
+ 'label',
22
+ 'I only subscribed to read a specific article/redeem an offer'
23
+ )
24
+ cy.contains(
25
+ 'label',
26
+ 'I cannot justify paying for my current subscription'
27
+ )
28
+ cy.contains(
29
+ 'label',
30
+ 'I do not have enough time to read the edition everyday'
31
+ )
32
+ cy.contains('label', 'I prefer another news subscription service')
33
+ cy.contains('label', 'I do not enjoy the journalism')
34
+ cy.contains('label', 'I am travelling or moving abroad')
35
+ cy.contains('label', 'Other')
36
+ cy.contains('label', 'Prefer not to say')
37
+
38
+ cy.contains('[data-testid="primary-button"]', 'Continue cancellation')
39
+ cy.contains('[data-testid="secondary-button"]', 'Keep subscription')
40
+
41
+ //Redirects to Subscription and Billing page
42
+ cy.get('[data-testid="secondary-button"]').click()
43
+ cy.location().should((loc) => {
44
+ expect(loc.pathname).to.eq('/account/subscription-and-billing')
45
+ })
46
+ })
47
+ it('Should show user text field when selecting Other as option', () => {
48
+ cy.contains('Select one option from the list below.')
49
+ cy.get('[type="radio"]').check('Other')
50
+ cy.contains('(please state)')
51
+
52
+ cy.get('textarea[name="other"]').clear()
53
+ cy.get('textarea[name="other"]').type('Thanks my business') //There is no validation for the text area
54
+ cy.get('[data-testid="primary-button"]').click()
55
+
56
+ cy.contains('Are you sure you want to cancel your subscription?')
57
+ cy.contains('Cancel subscription')
58
+ })
59
+ it('Should cancel user subscription', () => {
60
+ cy.contains('Select one option from the list below.')
61
+ cy.contains(
62
+ 'I only subscribed to read a specific article/redeem an offer'
63
+ ).click()
64
+ cy.get('[data-testid="primary-button"]').click()
65
+
66
+ //Cancelation confirmation modal
67
+ cy.contains('Are you sure you want to cancel your subscription?')
68
+ cy.contains(
69
+ 'You will lose unlimited access to exclusive content and benefits.'
70
+ )
71
+ cy.contains('Please note:')
72
+ cy.contains(
73
+ 'Your subscription full access will be revoked by the end of your current bill cycle.'
74
+ )
75
+ cy.contains('Cancel subscription')
76
+ cy.contains('Keep subscription')
77
+
78
+ cy.intercept('POST', '/api/account/mutate', {
79
+ statusCode: 200,
80
+ body: { data: { cancelSubscription: { success: true } } },
81
+ })
82
+ cy.get('[data-testid="primary-button"]').click()
83
+
84
+ cy.contains(
85
+ '[data-testid="toast-container"]',
86
+ 'Cancelling your subscription'
87
+ )
88
+
89
+ // Post-cancellation modal
90
+ cy.get('[data-testid="dialog-content"]').should('be.visible')
91
+ cy.contains('Subscription cancelled')
92
+ cy.contains(
93
+ 'Sorry to see you go, cancellation will be effective immediately and any current subscription benefits will be removed from your account.'
94
+ )
95
+ cy.contains(
96
+ 'If you change your mind or cancelled by mistake, speak to one of our advisors on XXXX-XXX-XXXX.'
97
+ )
98
+ cy.contains('Back to account')
99
+
100
+ cy.get('[data-testid="centered-button"]').click()
101
+
102
+ cy.location().should((loc) => {
103
+ expect(loc.pathname).to.eq('/account/subscription-and-billing')
104
+ })
105
+
106
+ cy.visitAndOverrideNextData(
107
+ '/account/subscription-and-billing',
108
+ 'props.pageProps.user.subscriptions[0].serviceCancellationDate',
109
+ serviceCancellationDate
110
+ )
111
+
112
+ cy.get('[data-testid="inline-message"]').contains(
113
+ 'Your subscription will end soon'
114
+ )
115
+ cy.get('[data-testid="inline-message"]').contains(
116
+ 'If you change your mind or cancelled by mistake, speak to one of our advisors on XXXX-XXX-XXXX.'
117
+ )
118
+ cy.get('a[href="/account/cancellation"]').should('not.exist')
119
+ })
120
+ it('Should show validation error messages when user tries to cancell subscription without selecting a cancellation reason', () => {
121
+ cy.contains('Select one option from the list below.')
122
+ cy.get('[data-testid="primary-button"]').click()
123
+ cy.contains('Please, select one option')
124
+ })
125
+ it('Should select only one cancellation reason', () => {
126
+ cy.contains('Select one option from the list below.')
127
+
128
+ cy.contains(
129
+ 'I only subscribed to read a specific article/redeem an offer'
130
+ ).click()
131
+
132
+ cy.get(
133
+ 'input[value="I only subscribed to read a specific article/redeem an offer"]'
134
+ ).should('be.checked')
135
+ cy.get(
136
+ 'input[value="I cannot justify paying for my current subscription"]'
137
+ ).should('not.be.checked')
138
+ cy.get(
139
+ 'input[value="I do not have enough time to read the edition everyday"]'
140
+ ).should('not.be.checked')
141
+ cy.get(
142
+ 'input[value="I prefer another news subscription service"]'
143
+ ).should('not.be.checked')
144
+ cy.get('input[value="I do not enjoy the journalism"]').should(
145
+ 'not.be.checked'
146
+ )
147
+ cy.get('input[value="I am travelling or moving abroad"]').should(
148
+ 'not.be.checked'
149
+ )
150
+ cy.get('input[value="Other"]').should('not.be.checked')
151
+ cy.get('input[value="Prefer not to say"]').should('not.be.checked')
152
+
153
+ cy.contains('I prefer another news subscription service').click()
154
+
155
+ cy.get(
156
+ 'input[value="I only subscribed to read a specific article/redeem an offer"]'
157
+ ).should('not.be.checked')
158
+ cy.get(
159
+ 'input[value="I cannot justify paying for my current subscription"]'
160
+ ).should('not.be.checked')
161
+ cy.get(
162
+ 'input[value="I do not have enough time to read the edition everyday"]'
163
+ ).should('not.be.checked')
164
+ cy.get(
165
+ 'input[value="I prefer another news subscription service"]'
166
+ ).should('be.checked')
167
+ cy.get('input[value="I do not enjoy the journalism"]').should(
168
+ 'not.be.checked'
169
+ )
170
+ cy.get('input[value="I am travelling or moving abroad"]').should(
171
+ 'not.be.checked'
172
+ )
173
+ cy.get('input[value="Other"]').should('not.be.checked')
174
+ cy.get('input[value="Prefer not to say"]').should('not.be.checked')
175
+ })
176
+ it('Should show error toast when canceling subscription if internal server error occures', () => {
177
+ cy.intercept('POST', '/api/account/mutate', { statusCode: 500 })
178
+
179
+ cy.contains('Select one option from the list below.')
180
+ cy.contains('I am travelling or moving abroad').click()
181
+ cy.get('[data-testid="primary-button"]').click()
182
+
183
+ cy.contains(
184
+ 'You will lose unlimited access to exclusive content and benefits.'
185
+ )
186
+ cy.get('[data-testid="primary-button"]').click()
187
+
188
+ cy.contains(
189
+ '[data-testid="toast-container"]',
190
+ "Sorry, we're unable to save your cancellation right now. Please try again or come back later."
191
+ )
192
+ })
193
+ })
194
+
195
+ it('Should show outstanding payment modal for user in payment failure', () => {
196
+ cy.GetTnlAcsSession('digitalCard')
197
+ cy.mockConsent()
198
+ cy.visitAndOverrideNextData(
199
+ '/account/subscription-and-billing',
200
+ 'props.pageProps.user.paymentFailure',
201
+ paymentFailure
202
+ )
203
+
204
+ cy.get('a[href="/account/cancellation"]').should('be.visible').click()
205
+
206
+ cy.contains('You have outstanding payments')
207
+ cy.contains(
208
+ 'If you wish to cancel your subscription update your payment details or speak to one of our advisors on XXXX-XXX-XXXX.'
209
+ )
210
+ cy.contains('Update payment details')
211
+
212
+ cy.get('[data-testid="centered-button"]').click()
213
+
214
+ cy.location().should((loc) => {
215
+ expect(loc.pathname).to.eq('/account/payment')
216
+ })
217
+ cy.contains('Edit your payment method')
218
+ })
219
+ })