diff --git a/src/main/app.ts b/src/main/app.ts index f4a15a795..2d27ffbb8 100644 --- a/src/main/app.ts +++ b/src/main/app.ts @@ -7,7 +7,7 @@ import { glob } from 'glob'; import favicon from 'serve-favicon'; import { setupDev } from './development'; -import { caseReferenceParamMiddleware } from './middleware'; +import { caseReferenceParamMiddleware, pageTrackingUrlMiddleware } from './middleware'; import * as modules from './modules'; import { setupErrorHandlers } from './modules/error-handler'; import { registerAllJourneys } from './routes/registerSteps'; @@ -38,6 +38,8 @@ app.use((req, res, next) => { next(); }); +app.use(pageTrackingUrlMiddleware); + // param middleware for caseReference app.param('caseReference', caseReferenceParamMiddleware); diff --git a/src/main/assets/locales/cy/common.json b/src/main/assets/locales/cy/common.json index f8a8f60eb..84e91c3fd 100644 --- a/src/main/assets/locales/cy/common.json +++ b/src/main/assets/locales/cy/common.json @@ -1,7 +1,7 @@ { "serviceName": "cyRespond to a property possession claim", "phase": "cyALPHA", - "feedback": "cyThis is a new service – your feedback will help us to improve it.", + "feedback": "cyThis is a new service – your feedback (opens in new tab) will help us to improve it.", "languageToggle": "English", "logout": "cySign out", "navigationLabel": "cyService navigation", diff --git a/src/main/assets/locales/en/common.json b/src/main/assets/locales/en/common.json index a5a412aa2..ef4f324af 100644 --- a/src/main/assets/locales/en/common.json +++ b/src/main/assets/locales/en/common.json @@ -1,7 +1,7 @@ { "serviceName": "Respond to a property possession claim", "phase": "ALPHA", - "feedback": "This is a new service – your feedback will help us to improve it.", + "feedback": "This is a new service – your feedback (opens in new tab) will help us to improve it.", "languageToggle": "Cymraeg", "logout": "Sign out", "navigationLabel": "Service navigation", diff --git a/src/main/middleware/index.ts b/src/main/middleware/index.ts index 4d0884218..d8ed55b3b 100644 --- a/src/main/middleware/index.ts +++ b/src/main/middleware/index.ts @@ -1,2 +1,3 @@ export * from './oidc'; export * from './caseReference'; +export * from './pageTrackingUrl'; diff --git a/src/main/middleware/pageTrackingUrl.ts b/src/main/middleware/pageTrackingUrl.ts new file mode 100644 index 000000000..61a4dba48 --- /dev/null +++ b/src/main/middleware/pageTrackingUrl.ts @@ -0,0 +1,21 @@ +import { NextFunction, Request, Response } from 'express'; + +const getPageTrackingUrl = (path: string): string => { + const segments = path.split('/').filter(Boolean); + + if (segments[0] === 'dashboard') { + return 'dashboard'; + } + + if (segments[0] === 'case' && segments.length > 2) { + return `${segments.slice(2).join('/')}`; + } + + return path; +}; + +export const pageTrackingUrlMiddleware = (req: Request, res: Response, next: NextFunction): void => { + const pageTrackingUrl = getPageTrackingUrl(req.path); + res.locals.pageTrackingUrl = pageTrackingUrl; + next(); +}; diff --git a/src/main/modules/i18n/index.ts b/src/main/modules/i18n/index.ts index bdaaa0230..9a9c8586e 100644 --- a/src/main/modules/i18n/index.ts +++ b/src/main/modules/i18n/index.ts @@ -67,7 +67,6 @@ export type AllowedLang = (typeof allowedLanguages)[number]; export const COMMON_TRANSLATION_KEYS = [ 'serviceName', 'phase', - 'feedback', 'back', 'languageToggle', 'contactUsForHelp', diff --git a/src/main/modules/steps/formBuilder/formContent.ts b/src/main/modules/steps/formBuilder/formContent.ts index 92416b37e..39696773a 100644 --- a/src/main/modules/steps/formBuilder/formContent.ts +++ b/src/main/modules/steps/formBuilder/formContent.ts @@ -72,7 +72,6 @@ export function buildFormContent( errorSummary, serviceName: t('serviceName'), phase: t('phase'), - feedback: t('feedback'), back: t('back'), contactUsForHelp: t('contactUsForHelp'), contactUsForHelpText: t('contactUsForHelpText'), diff --git a/src/main/views/journeyTemplate.njk b/src/main/views/journeyTemplate.njk index 419b0bced..06c3cdec3 100644 --- a/src/main/views/journeyTemplate.njk +++ b/src/main/views/journeyTemplate.njk @@ -34,7 +34,7 @@
{{ govukPhaseBanner({ tag: { text: t('phase', 'Alpha') }, - html: (t('feedback') | safe) + html: (t('feedback', { pageTrackingUrl: pageTrackingUrl }) | safe) }) }}
diff --git a/src/main/views/stepsTemplate.njk b/src/main/views/stepsTemplate.njk index 5d14122d6..4d8f7db5c 100644 --- a/src/main/views/stepsTemplate.njk +++ b/src/main/views/stepsTemplate.njk @@ -46,7 +46,7 @@ tag: { text: (phase or t('phase') or 'ALPHA') | safe }, - html: ((feedback or t('feedback') or '')) | safe + html: ((feedback or t('feedback', { pageTrackingUrl: pageTrackingUrl }) or '')) | safe }) }} {% if backUrl %} diff --git a/src/main/views/template.njk b/src/main/views/template.njk index 82f1e8202..2bb581d55 100644 --- a/src/main/views/template.njk +++ b/src/main/views/template.njk @@ -47,7 +47,7 @@ tag: { text: (phase or t('phase') or 'ALPHA') | safe }, - html: ((feedback or t('feedback') or '')) | safe + html: ((feedback or t('feedback', { pageTrackingUrl: pageTrackingUrl }) or '')) | safe }) }} {% if backUrl %} diff --git a/src/test/ui/data/page-data/confirmationOfNoticeGiven.page.data.ts b/src/test/ui/data/page-data/confirmationOfNoticeGiven.page.data.ts index fad446c9e..9751877c6 100644 --- a/src/test/ui/data/page-data/confirmationOfNoticeGiven.page.data.ts +++ b/src/test/ui/data/page-data/confirmationOfNoticeGiven.page.data.ts @@ -17,4 +17,6 @@ export const confirmationOfNoticeGiven = { selectIfNoticeOfIntentionGivenErrorMessage(claimantName: string): string { return `Select if ${claimantName} gave you notice of their intention to begin possession proceedings`; }, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `confirmation-of-notice-given`, }; diff --git a/src/test/ui/data/page-data/contactPreferenceEmailOrPost.page.data.ts b/src/test/ui/data/page-data/contactPreferenceEmailOrPost.page.data.ts index d7f3d22fb..7f35477e9 100644 --- a/src/test/ui/data/page-data/contactPreferenceEmailOrPost.page.data.ts +++ b/src/test/ui/data/page-data/contactPreferenceEmailOrPost.page.data.ts @@ -22,4 +22,6 @@ export const contactPreferenceEmailOrPost = { selectHowYouWantToReceiveUpdatesErrorMessage: `Select how you want to receive updates`, enterEmailAddressErrorMessage: `Enter your email address`, invalidEmailAddressErrorMessage: `Enter an email address in the correct format, like name@example.com`, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `contact-preferences-email-or-post`, }; diff --git a/src/test/ui/data/page-data/contactPreferencesTelephone.page.data.ts b/src/test/ui/data/page-data/contactPreferencesTelephone.page.data.ts index 6b5e69aa5..876f57564 100644 --- a/src/test/ui/data/page-data/contactPreferencesTelephone.page.data.ts +++ b/src/test/ui/data/page-data/contactPreferencesTelephone.page.data.ts @@ -18,4 +18,6 @@ export const contactPreferencesTelephone = { selectWhetherHappyToBeContactedByTelephoneErrorMessage: `Select whether you’re happy to be contacted by telephone`, enterUKPhoneNumberErrorMessage: `Enter a UK phone number`, enterUKPhoneNumberFormatErrorMessage: `Enter a phone number, like 01632 960 001 or 07700 900 982`, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `contact-preferences-telephone`, }; diff --git a/src/test/ui/data/page-data/contactPreferencesTextMessage.page.data.ts b/src/test/ui/data/page-data/contactPreferencesTextMessage.page.data.ts index 93aaf3595..0c41202b5 100644 --- a/src/test/ui/data/page-data/contactPreferencesTextMessage.page.data.ts +++ b/src/test/ui/data/page-data/contactPreferencesTextMessage.page.data.ts @@ -11,4 +11,6 @@ export const contactPreferencesTextMessage = { backLink: `Back`, thereIsAProblemErrorMessageHeader: `There is a problem`, selectIfYouWantErrorMessage: `Select if you want to be contacted by text message.`, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `contact-preferences-text-message`, }; diff --git a/src/test/ui/data/page-data/dateOfBirth.page.data.ts b/src/test/ui/data/page-data/dateOfBirth.page.data.ts index f307ebc92..ce601bb71 100644 --- a/src/test/ui/data/page-data/dateOfBirth.page.data.ts +++ b/src/test/ui/data/page-data/dateOfBirth.page.data.ts @@ -25,4 +25,6 @@ export const dateOfBirth = { backLink: `Back`, saveAndContinueButton: `Save and continue`, saveForLaterButton: `Save for later`, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `date-of-birth`, }; diff --git a/src/test/ui/data/page-data/defendantNameCapture.page.data.ts b/src/test/ui/data/page-data/defendantNameCapture.page.data.ts index dabcad090..ee97169e1 100644 --- a/src/test/ui/data/page-data/defendantNameCapture.page.data.ts +++ b/src/test/ui/data/page-data/defendantNameCapture.page.data.ts @@ -14,4 +14,6 @@ export const defendantNameCapture = { enterYourLastNameErrorMessage: `Enter your last name`, enterFirstNameMaxLengthErrorMessage: `First name must be 60 characters or less`, enterLastNameMaxLengthErrorMessage: `Last name must be 60 characters or less`, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `defendant-name-capture`, }; diff --git a/src/test/ui/data/page-data/defendantNameConfirmation.page.data.ts b/src/test/ui/data/page-data/defendantNameConfirmation.page.data.ts index 633ab5283..4f16b3a94 100644 --- a/src/test/ui/data/page-data/defendantNameConfirmation.page.data.ts +++ b/src/test/ui/data/page-data/defendantNameConfirmation.page.data.ts @@ -24,4 +24,6 @@ export const defendantNameConfirmation = { enterYourLastNameErrorMessage: `Enter your last name`, enterFirstNameMaxLengthErrorMessage: `First name must be 60 characters or less`, enterLastNameMaxLengthErrorMessage: `Last name must be 60 characters or less`, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `defendant-name-confirmation`, }; diff --git a/src/test/ui/data/page-data/disputeClaimInterstitial.page.data.ts b/src/test/ui/data/page-data/disputeClaimInterstitial.page.data.ts index e14d80054..45098e782 100644 --- a/src/test/ui/data/page-data/disputeClaimInterstitial.page.data.ts +++ b/src/test/ui/data/page-data/disputeClaimInterstitial.page.data.ts @@ -17,4 +17,6 @@ export const disputeClaimInterstitial = { cancelLink: `Cancel`, cymraegLink: `Cymraeg`, backLink: `Back`, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `dispute-claim-interstitial`, }; diff --git a/src/test/ui/data/page-data/feedback.page.data.ts b/src/test/ui/data/page-data/feedback.page.data.ts new file mode 100644 index 000000000..4d6e88541 --- /dev/null +++ b/src/test/ui/data/page-data/feedback.page.data.ts @@ -0,0 +1,9 @@ +export const feedback = { + finishSurveyButton: `Finish Survey`, + tellUsWhatYouThinkParagraph: `Tell us what you think!`, + weReallyAppreciateYourFeedbackParagraph: `We'd really appreciate your feedback, but please note, you will not receive a reply to comments left here`, + ifYouHaveAProblemParagraph: `If you have a problem that needs a response, please contact us on;`, + telephoneNumberParagraph: `Phone: [telephone number tbc] (Opening times Monday to Friday, XXam to XXpm)`, + emailParagraph: `Email: [email TBC] (We aim to respond within 10 working days)`, + pleaseLeaveYourFeedbackHeader: `Please leave your feedback in the space below (make sure you do not include any personal or financial details).`, +}; diff --git a/src/test/ui/data/page-data/freeLegalAdvice.page.data.ts b/src/test/ui/data/page-data/freeLegalAdvice.page.data.ts index 6064d76a2..404ef4c58 100644 --- a/src/test/ui/data/page-data/freeLegalAdvice.page.data.ts +++ b/src/test/ui/data/page-data/freeLegalAdvice.page.data.ts @@ -25,4 +25,6 @@ export const freeLegalAdvice = { backLink: `Back`, youMustSayAboutFreeLegalAdviceErrorMessage: `You must say if you've had any free legal advice`, thereIsAProblemErrorMessageHeader: `There is a problem`, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `free-legal-advice`, }; diff --git a/src/test/ui/data/page-data/index.ts b/src/test/ui/data/page-data/index.ts index 123623583..65b434706 100644 --- a/src/test/ui/data/page-data/index.ts +++ b/src/test/ui/data/page-data/index.ts @@ -25,3 +25,4 @@ export * from './tenancyDateDetails.page.data'; export * from './dashboard.page.data'; export * from './landlordLicensed.page.data'; export * from './writtenTerms.page.data'; +export * from './feedback.page.data'; diff --git a/src/test/ui/data/page-data/landlordLicensed.page.data.ts b/src/test/ui/data/page-data/landlordLicensed.page.data.ts index 69264be77..1e9414a98 100644 --- a/src/test/ui/data/page-data/landlordLicensed.page.data.ts +++ b/src/test/ui/data/page-data/landlordLicensed.page.data.ts @@ -13,4 +13,6 @@ export const landlordLicensed = { cymraegLink: `Cymraeg`, thereIsAProblemErrorMessageHeader: `There is a problem`, selectIfYouAgreeWithLandlordsClaimLicensedErrorMessage: `Select if you agree with the landlord’s claim to be licensed`, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `landlord-licensed`, }; diff --git a/src/test/ui/data/page-data/landlordRegistered.page.data.ts b/src/test/ui/data/page-data/landlordRegistered.page.data.ts index e99e57eb0..6627e5831 100644 --- a/src/test/ui/data/page-data/landlordRegistered.page.data.ts +++ b/src/test/ui/data/page-data/landlordRegistered.page.data.ts @@ -13,4 +13,6 @@ export const landlordRegistered = { cymraegLink: `Cymraeg`, thereIsAProblemErrorMessageHeader: `There is a problem`, selectIfYouAgreeWithLandlordsClaimRegisteredErrorMessage: `Select if you agree with the landlord’s claim to be registered`, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `landlord-registered`, }; diff --git a/src/test/ui/data/page-data/licensedLandlord.page.data.ts b/src/test/ui/data/page-data/licensedLandlord.page.data.ts new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/ui/data/page-data/noticeDateWhenNotProvided.page.data.ts b/src/test/ui/data/page-data/noticeDateWhenNotProvided.page.data.ts index 7b8aa054e..c18df6109 100644 --- a/src/test/ui/data/page-data/noticeDateWhenNotProvided.page.data.ts +++ b/src/test/ui/data/page-data/noticeDateWhenNotProvided.page.data.ts @@ -14,4 +14,6 @@ export const noticeDateWhenNotProvided = { signOutLink: `Sign out`, thereIsAProblemErrorMessageHeader: `There is a problem`, theDateYouReceiveNoticeErrorMessage: `The date you received notice must either be today’s date or in the past`, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `confirmation-of-notice-date-when-not-provided`, }; diff --git a/src/test/ui/data/page-data/noticeDateWhenProvided.page.data.ts b/src/test/ui/data/page-data/noticeDateWhenProvided.page.data.ts index c9e1ac1c9..cad050b8a 100644 --- a/src/test/ui/data/page-data/noticeDateWhenProvided.page.data.ts +++ b/src/test/ui/data/page-data/noticeDateWhenProvided.page.data.ts @@ -18,6 +18,8 @@ export const noticeDateWhenProvided = { signOutLink: `Sign out`, thereIsAProblemErrorMessageHeader: `There is a problem`, theDateYouReceiveNoticeErrorMessage: `The date you received notice must either be today’s date or in the past`, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `confirmation-of-notice-date-when-provided`, }; export function convertDateFormat(dateString: string): string { diff --git a/src/test/ui/data/page-data/paymentInterstitial.page.data.ts b/src/test/ui/data/page-data/paymentInterstitial.page.data.ts index ab884ebd3..99789dc19 100644 --- a/src/test/ui/data/page-data/paymentInterstitial.page.data.ts +++ b/src/test/ui/data/page-data/paymentInterstitial.page.data.ts @@ -10,4 +10,6 @@ export const paymentInterstitial = { continueButton: `Continue`, cancelLink: `Cancel`, backLink: `Back`, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `payment-interstitial`, }; diff --git a/src/test/ui/data/page-data/repaymentsMade.page.data.ts b/src/test/ui/data/page-data/repaymentsMade.page.data.ts index 9fd0d0f60..d6d56fd8c 100644 --- a/src/test/ui/data/page-data/repaymentsMade.page.data.ts +++ b/src/test/ui/data/page-data/repaymentsMade.page.data.ts @@ -15,4 +15,6 @@ export const repaymentsMade = { mustBeUnderCharacterLimitErrorMessage: `Must be 500 characters or fewer`, saveForLaterButton: `Save for later`, backLink: `Back`, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `repayments-made`, }; diff --git a/src/test/ui/data/page-data/startNow.page.data.ts b/src/test/ui/data/page-data/startNow.page.data.ts index db5d20b66..c28f6411c 100644 --- a/src/test/ui/data/page-data/startNow.page.data.ts +++ b/src/test/ui/data/page-data/startNow.page.data.ts @@ -23,4 +23,6 @@ export const startNow = { contactUsForHelpText: `Contact us for help`, backLink: `Back`, cymraegLink: `Cymraeg`, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `start-now`, }; diff --git a/src/test/ui/data/page-data/tenancyDateDetails.page.data.ts b/src/test/ui/data/page-data/tenancyDateDetails.page.data.ts index 46c33027f..cdc7f4ece 100644 --- a/src/test/ui/data/page-data/tenancyDateDetails.page.data.ts +++ b/src/test/ui/data/page-data/tenancyDateDetails.page.data.ts @@ -27,6 +27,8 @@ export const tenancyDateDetails = { monthMissingErrorMessage: `Your tenancy start date must include a month`, yearMissingErrorMessage: `Your tenancy start date must include a year`, futureDateErrorMessage: `Tenancy start date must be in the past`, + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `tenancy-date-details`, }; export function convertDateFormatTenancyDate(dateString: string): string { diff --git a/src/test/ui/data/page-data/tenancyDateUnknown.page.data.ts b/src/test/ui/data/page-data/tenancyDateUnknown.page.data.ts index 2a8b15f68..65131b100 100644 --- a/src/test/ui/data/page-data/tenancyDateUnknown.page.data.ts +++ b/src/test/ui/data/page-data/tenancyDateUnknown.page.data.ts @@ -17,4 +17,6 @@ export const tenancyDateUnknown = { yearMissingErrorMessage: 'Your tenancy start date must include a year', realDateErrorMessage: 'Tenancy start date must be a real date', backLink: 'Back', + feedbackLink: `feedback (opens in new tab)`, + pageSlug: `tenancy-date-unknown`, }; diff --git a/src/test/ui/e2eTest/respondToClaimWales.spec.ts b/src/test/ui/e2eTest/respondToClaimWales.spec.ts index 798a9f26d..553052af2 100644 --- a/src/test/ui/e2eTest/respondToClaimWales.spec.ts +++ b/src/test/ui/e2eTest/respondToClaimWales.spec.ts @@ -24,6 +24,7 @@ const home_url = config.get('e2e.testUrl') as string; test.beforeEach(async ({ page }) => { initializeExecutor(page); process.env.WALES_POSTCODE = 'YES'; + process.env.CLAIMANT_NAME = submitCaseApiDataWales.submitCasePayload.claimantName; await performAction('createCaseAPI', { data: createCaseApiWalesData.createCasePayload }); await performAction('submitCaseAPI', { data: submitCaseApiDataWales.submitCasePayload }); await performAction('fetchPINsAPI'); diff --git a/src/test/ui/functional/confirmationOfNoticeGiven.pft.ts b/src/test/ui/functional/confirmationOfNoticeGiven.pft.ts index ccdc38c04..aa1cafda2 100644 --- a/src/test/ui/functional/confirmationOfNoticeGiven.pft.ts +++ b/src/test/ui/functional/confirmationOfNoticeGiven.pft.ts @@ -1,5 +1,5 @@ import { submitCaseApiData } from '../data/api-data'; -import { confirmationOfNoticeGiven, dashboard, tenancyDateUnknown } from '../data/page-data'; +import { confirmationOfNoticeGiven, dashboard, feedback, tenancyDateUnknown } from '../data/page-data'; import { performAction, performValidation } from '../utils/controller'; let claimantName = ''; @@ -19,6 +19,10 @@ export async function confirmationOfNoticeGivenErrorValidation(): Promise } export async function confirmationOfNoticeGivenNavigationTests(): Promise { + await performValidation('pageNavigation', confirmationOfNoticeGiven.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: confirmationOfNoticeGiven.pageSlug, + }); await performValidation('pageNavigation', confirmationOfNoticeGiven.backLink, tenancyDateUnknown.mainHeader); await performAction('clickRadioButton', confirmationOfNoticeGiven.yesRadioOption); await performValidation('pageNavigation', confirmationOfNoticeGiven.saveForLaterButton, dashboard.mainHeader); diff --git a/src/test/ui/functional/contactPreferenceEmailOrPost.pft.ts b/src/test/ui/functional/contactPreferenceEmailOrPost.pft.ts index b546d709d..79a9eeebd 100644 --- a/src/test/ui/functional/contactPreferenceEmailOrPost.pft.ts +++ b/src/test/ui/functional/contactPreferenceEmailOrPost.pft.ts @@ -1,4 +1,4 @@ -import { contactPreferenceEmailOrPost } from '../data/page-data'; +import { contactPreferenceEmailOrPost, feedback } from '../data/page-data'; import { performAction, performValidation } from '../utils/controller'; export async function contactPreferenceEmailOrPostErrorValidation(): Promise { @@ -85,9 +85,14 @@ export async function contactPreferenceEmailOrPostErrorValidation(): Promise { - if (process.env.CORRESPONDENCE_ADDRESS === 'UNKNOWN') { + +export async function contactPreferenceEmailOrPostNavigationTests(): Promise { + await performValidation('pageNavigation', contactPreferenceEmailOrPost.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: contactPreferenceEmailOrPost.pageSlug, + }); + //--The below line is commented out as we have an open bug - HDPI-5396 + /*if (process.env.CORRESPONDENCE_ADDRESS === 'UNKNOWN') { await performValidation( 'pageNavigation', contactPreferenceEmailOrPost.backLink, @@ -102,4 +107,5 @@ export async function contactPreferenceEmailOrPostErrorValidation(): Promise { @@ -46,6 +46,10 @@ export async function contactPreferencesTelephoneErrorValidation(): Promise { + await performValidation('pageNavigation', contactPreferencesTelephone.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: contactPreferencesTelephone.pageSlug, + }); await performValidation( 'pageNavigation', contactPreferencesTelephone.backLink, diff --git a/src/test/ui/functional/contactPreferencesTextMessage.pft.ts b/src/test/ui/functional/contactPreferencesTextMessage.pft.ts index 6a21e102d..cc30bb51a 100644 --- a/src/test/ui/functional/contactPreferencesTextMessage.pft.ts +++ b/src/test/ui/functional/contactPreferencesTextMessage.pft.ts @@ -1,4 +1,4 @@ -import { contactPreferencesTelephone, contactPreferencesTextMessage, dashboard } from '../data/page-data'; +import { contactPreferencesTelephone, contactPreferencesTextMessage, dashboard, feedback } from '../data/page-data'; import { performAction, performValidation } from '../utils/controller'; export async function contactPreferencesTextMessageErrorValidation(): Promise { @@ -10,6 +10,10 @@ export async function contactPreferencesTextMessageErrorValidation(): Promise { + await performValidation('pageNavigation', contactPreferencesTextMessage.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: contactPreferencesTextMessage.pageSlug, + }); await performValidation( 'pageNavigation', contactPreferencesTextMessage.backLink, diff --git a/src/test/ui/functional/dateOfBirth.pft.ts b/src/test/ui/functional/dateOfBirth.pft.ts index 0c428bb0a..960e6a2a1 100644 --- a/src/test/ui/functional/dateOfBirth.pft.ts +++ b/src/test/ui/functional/dateOfBirth.pft.ts @@ -1,7 +1,11 @@ -import { dashboard, dateOfBirth, defendantNameCapture } from '../data/page-data'; +import { dashboard, dateOfBirth, defendantNameCapture, feedback } from '../data/page-data'; import { performValidation } from '../utils/controller'; export async function dateOfBirthNavigationTests(): Promise { + await performValidation('pageNavigation', dateOfBirth.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: dateOfBirth.pageSlug, + }); await performValidation('pageNavigation', dateOfBirth.backLink, defendantNameCapture.mainHeader); await performValidation('pageNavigation', dateOfBirth.saveForLaterButton, dashboard.mainHeader); } diff --git a/src/test/ui/functional/defendantNameCapture.pft.ts b/src/test/ui/functional/defendantNameCapture.pft.ts index f62be60a5..d7dff357f 100644 --- a/src/test/ui/functional/defendantNameCapture.pft.ts +++ b/src/test/ui/functional/defendantNameCapture.pft.ts @@ -1,4 +1,4 @@ -import { dashboard, defendantNameCapture, freeLegalAdvice } from '../data/page-data'; +import { dashboard, defendantNameCapture, feedback, freeLegalAdvice } from '../data/page-data'; import { performAction, performValidation } from '../utils/controller'; const overMaxLengthString = 'A'.repeat(61); @@ -33,6 +33,10 @@ export async function defendantNameCaptureErrorValidation(): Promise { }); } export async function defendantNameCaptureNavigationTests(): Promise { + await performValidation('pageNavigation', defendantNameCapture.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: defendantNameCapture.pageSlug, + }); await performValidation('pageNavigation', defendantNameCapture.backLink, freeLegalAdvice.mainHeader); await defendantNameCaptureInputValuesPrePopulated(); } diff --git a/src/test/ui/functional/defendantNameConfirmation.pft.ts b/src/test/ui/functional/defendantNameConfirmation.pft.ts index e2fad7c6e..23e640d05 100644 --- a/src/test/ui/functional/defendantNameConfirmation.pft.ts +++ b/src/test/ui/functional/defendantNameConfirmation.pft.ts @@ -1,4 +1,4 @@ -import { defendantNameConfirmation, freeLegalAdvice } from '../data/page-data'; +import { defendantNameConfirmation, feedback, freeLegalAdvice } from '../data/page-data'; import { performAction, performValidation } from '../utils/controller'; import { defendantNameCaptureInputValuesPrePopulated } from './defendantNameCapture.pft'; @@ -46,6 +46,10 @@ export async function defendantNameConfirmationErrorValidation(): Promise }); } export async function defendantNameConfirmationNavigationTests(): Promise { + await performValidation('pageNavigation', defendantNameConfirmation.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: defendantNameConfirmation.pageSlug, + }); await performValidation('pageNavigation', defendantNameConfirmation.backLink, freeLegalAdvice.mainHeader); await performAction('clickRadioButton', { question: defendantNameConfirmation.mainHeader, diff --git a/src/test/ui/functional/disputeClaimInterstitial.pft.ts b/src/test/ui/functional/disputeClaimInterstitial.pft.ts index 57bd221eb..e6ea8cb21 100644 --- a/src/test/ui/functional/disputeClaimInterstitial.pft.ts +++ b/src/test/ui/functional/disputeClaimInterstitial.pft.ts @@ -3,10 +3,15 @@ import { contactPreferencesTextMessage, dashboard, disputeClaimInterstitial, + feedback, } from '../data/page-data'; import { performValidation } from '../utils/controller'; export async function disputeClaimInterstitialNavigationTests(): Promise { + await performValidation('pageNavigation', disputeClaimInterstitial.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: disputeClaimInterstitial.pageSlug, + }); if (process.env.CONTACT_PREFERENCES_TELEPHONE === 'YES') { await performValidation( 'pageNavigation', diff --git a/src/test/ui/functional/freeLegalAdvice.pft.ts b/src/test/ui/functional/freeLegalAdvice.pft.ts index 6508bbb9c..4df11f34d 100644 --- a/src/test/ui/functional/freeLegalAdvice.pft.ts +++ b/src/test/ui/functional/freeLegalAdvice.pft.ts @@ -1,4 +1,4 @@ -import { dashboard, freeLegalAdvice, startNow } from '../data/page-data'; +import { dashboard, feedback, freeLegalAdvice, startNow } from '../data/page-data'; import { performAction, performValidation } from '../utils/controller'; export async function freeLegalAdviceErrorValidation(): Promise { @@ -10,6 +10,10 @@ export async function freeLegalAdviceErrorValidation(): Promise { } export async function freeLegalAdviceNavigationTests(): Promise { + await performValidation('pageNavigation', freeLegalAdvice.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: freeLegalAdvice.pageSlug, + }); await performValidation('pageNavigation', freeLegalAdvice.backLink, startNow.mainHeader); await performAction('clickRadioButton', freeLegalAdvice.yesRadioOption); await performValidation('pageNavigation', freeLegalAdvice.saveForLaterButton, dashboard.mainHeader); diff --git a/src/test/ui/functional/landlordLicensed.pft.ts b/src/test/ui/functional/landlordLicensed.pft.ts index fc21f565f..69f32ec38 100644 --- a/src/test/ui/functional/landlordLicensed.pft.ts +++ b/src/test/ui/functional/landlordLicensed.pft.ts @@ -1,4 +1,4 @@ -import { dashboard, landlordLicensed, landlordRegistered, writtenTerms } from '../data/page-data'; +import { dashboard, feedback, landlordLicensed, landlordRegistered, writtenTerms } from '../data/page-data'; import { performAction, performValidation } from '../utils/controller'; import { setTenancyTypeDetailsBackNavigation } from './tenancyTypeDetails.pft'; @@ -12,6 +12,10 @@ export async function landlordLicensedErrorValidation(): Promise { } export async function landlordLicensedNavigationTests(): Promise { + await performValidation('pageNavigation', landlordLicensed.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: landlordLicensed.pageSlug, + }); await performValidation('pageNavigation', landlordLicensed.backLink, landlordRegistered.mainHeader); await performAction('clickRadioButton', landlordLicensed.yesRadioOption); await performValidation('pageNavigation', landlordLicensed.saveForLaterButton, dashboard.mainHeader); diff --git a/src/test/ui/functional/landlordRegistered.pft.ts b/src/test/ui/functional/landlordRegistered.pft.ts index 7b8b1dc73..8d6f177b8 100644 --- a/src/test/ui/functional/landlordRegistered.pft.ts +++ b/src/test/ui/functional/landlordRegistered.pft.ts @@ -1,5 +1,5 @@ import { submitCaseApiData } from '../data/api-data'; -import { dashboard, disputeClaimInterstitial, landlordRegistered } from '../data/page-data'; +import { dashboard, disputeClaimInterstitial, feedback, landlordRegistered } from '../data/page-data'; import { performAction, performValidation } from '../utils/controller'; export async function landlordRegisteredErrorValidation(): Promise { @@ -12,6 +12,10 @@ export async function landlordRegisteredErrorValidation(): Promise { export async function landlordRegisteredNavigationTests(): Promise { const claimantsName = submitCaseApiData.submitCasePayload.claimantName; + await performValidation('pageNavigation', landlordRegistered.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: landlordRegistered.pageSlug, + }); await performValidation( 'pageNavigation', landlordRegistered.backLink, diff --git a/src/test/ui/functional/noticeDateWhenNotProvided.pft.ts b/src/test/ui/functional/noticeDateWhenNotProvided.pft.ts index 0108c784e..20871a6a7 100644 --- a/src/test/ui/functional/noticeDateWhenNotProvided.pft.ts +++ b/src/test/ui/functional/noticeDateWhenNotProvided.pft.ts @@ -1,4 +1,4 @@ -import { confirmationOfNoticeGiven, dashboard, noticeDateWhenNotProvided } from '../data/page-data'; +import { confirmationOfNoticeGiven, dashboard, feedback, noticeDateWhenNotProvided } from '../data/page-data'; import { performAction, performValidation } from '../utils/controller'; export async function noticeDateWhenNotProvidedErrorValidation(): Promise { @@ -15,6 +15,10 @@ export async function noticeDateWhenNotProvidedErrorValidation(): Promise } export async function noticeDateWhenNotProvidedNavigationTests(): Promise { + await performValidation('pageNavigation', noticeDateWhenNotProvided.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: noticeDateWhenNotProvided.pageSlug, + }); await performValidation('pageNavigation', noticeDateWhenNotProvided.backLink, confirmationOfNoticeGiven.mainHeader); await performValidation('pageNavigation', noticeDateWhenNotProvided.saveForLaterButton, dashboard.mainHeader); } diff --git a/src/test/ui/functional/paymentInterstitial.pft.ts b/src/test/ui/functional/paymentInterstitial.pft.ts index 7eacfc840..782493ace 100644 --- a/src/test/ui/functional/paymentInterstitial.pft.ts +++ b/src/test/ui/functional/paymentInterstitial.pft.ts @@ -1,7 +1,11 @@ -import { counterClaim, dashboard, paymentInterstitial } from '../data/page-data'; +import { counterClaim, dashboard, feedback, paymentInterstitial } from '../data/page-data'; import { performValidation } from '../utils/controller'; export async function paymentInterstitialNavigationTests(): Promise { + await performValidation('pageNavigation', paymentInterstitial.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: paymentInterstitial.pageSlug, + }); await performValidation('pageNavigation', paymentInterstitial.backLink, counterClaim.mainHeader); await performValidation('pageNavigation', paymentInterstitial.cancelLink, dashboard.mainHeader); } diff --git a/src/test/ui/functional/repaymentsMade.pft.ts b/src/test/ui/functional/repaymentsMade.pft.ts index 616d73161..0acc127ed 100644 --- a/src/test/ui/functional/repaymentsMade.pft.ts +++ b/src/test/ui/functional/repaymentsMade.pft.ts @@ -1,4 +1,4 @@ -import { dashboard, paymentInterstitial, repaymentsMade } from '../data/page-data'; +import { dashboard, feedback, paymentInterstitial, repaymentsMade } from '../data/page-data'; import { performAction, performValidation } from '../utils/controller'; export async function repaymentsMadeErrorValidation(): Promise { @@ -25,6 +25,10 @@ export async function repaymentsMadeErrorValidation(): Promise { } export async function repaymentsMadeNavigationTests(): Promise { + await performValidation('pageNavigation', repaymentsMade.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: repaymentsMade.pageSlug, + }); await performValidation('pageNavigation', repaymentsMade.backLink, paymentInterstitial.mainHeader); await performAction('clickRadioButton', repaymentsMade.yesRadioOption); await performAction('inputText', repaymentsMade.giveDetailsHiddenTextLabel, repaymentsMade.detailsTextInput); diff --git a/src/test/ui/functional/startNow.pft.ts b/src/test/ui/functional/startNow.pft.ts new file mode 100644 index 000000000..176ae6c12 --- /dev/null +++ b/src/test/ui/functional/startNow.pft.ts @@ -0,0 +1,10 @@ +import { dashboard, feedback, startNow } from '../data/page-data'; +import { performValidation } from '../utils/controller'; + +export async function startNowNavigationTests(): Promise { + await performValidation('pageNavigation', startNow.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: startNow.pageSlug, + }); + await performValidation('pageNavigation', startNow.backLink, dashboard.mainHeader); +} diff --git a/src/test/ui/functional/tenancyDateDetails.pft.ts b/src/test/ui/functional/tenancyDateDetails.pft.ts index 5a93cdcef..9d6dd0772 100644 --- a/src/test/ui/functional/tenancyDateDetails.pft.ts +++ b/src/test/ui/functional/tenancyDateDetails.pft.ts @@ -1,4 +1,4 @@ -import { dashboard, tenancyDateDetails, tenancyDetails } from '../data/page-data'; +import { dashboard, feedback, tenancyDateDetails, tenancyTypeDetails } from '../data/page-data'; import { performAction, performActions, performValidation } from '../utils/controller'; export async function tenancyDateDetailsErrorValidation(): Promise { @@ -50,7 +50,11 @@ export async function tenancyDateDetailsErrorValidation(): Promise { } export async function tenancyDateDetailsNavigationTests(): Promise { - await performValidation('pageNavigation', tenancyDateDetails.backLink, tenancyDetails.mainHeader); + await performValidation('pageNavigation', tenancyDateDetails.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: tenancyDateDetails.pageSlug, + }); + await performValidation('pageNavigation', tenancyDateDetails.backLink, tenancyTypeDetails.mainHeader); await performAction('clickRadioButton', tenancyDateDetails.yesRadioOption); await performValidation('pageNavigation', tenancyDateDetails.saveForLaterButton, dashboard.mainHeader); } diff --git a/src/test/ui/functional/tenancyDateUnknown.pft.ts b/src/test/ui/functional/tenancyDateUnknown.pft.ts index 803124d83..e830f63a1 100644 --- a/src/test/ui/functional/tenancyDateUnknown.pft.ts +++ b/src/test/ui/functional/tenancyDateUnknown.pft.ts @@ -1,4 +1,4 @@ -import { dashboard, tenancyDateUnknown, tenancyTypeDetails } from '../data/page-data'; +import { dashboard, feedback, tenancyDateUnknown, tenancyTypeDetails } from '../data/page-data'; import { performAction, performActions, performValidation } from '../utils/controller'; export async function tenancyDateUnknownErrorValidation(): Promise { @@ -40,6 +40,10 @@ export async function tenancyDateUnknownErrorValidation(): Promise { } export async function tenancyDateUnknownNavigationTests(): Promise { + await performValidation('pageNavigation', tenancyDateUnknown.feedbackLink, { + element: feedback.tellUsWhatYouThinkParagraph, + pageSlug: tenancyDateUnknown.pageSlug, + }); await performValidation('pageNavigation', tenancyDateUnknown.backLink, tenancyTypeDetails.mainHeader); await performActions( 'Enter Date', diff --git a/src/test/ui/functional/tenancyTypeDetails.pft.ts b/src/test/ui/functional/tenancyTypeDetails.pft.ts index f900f45de..ec0ba372b 100644 --- a/src/test/ui/functional/tenancyTypeDetails.pft.ts +++ b/src/test/ui/functional/tenancyTypeDetails.pft.ts @@ -1,4 +1,4 @@ -import { dashboard, disputeClaimInterstitial, tenancyTypeDetails } from '../data/page-data'; +import { dashboard, disputeClaimInterstitial, tenancyTypeDetails, writtenTerms } from '../data/page-data'; import { claimantsName } from '../utils/actions/custom-actions'; import { performAction, performValidation } from '../utils/controller'; @@ -25,10 +25,18 @@ export async function tenancyTypeDetailsErrorValidation(): Promise { } export async function tenancyTypeDetailsNavigationTests(): Promise { - if (claimantsName) { - const backHeader = _backNavigationHeader ?? disputeClaimInterstitial.getMainHeader(claimantsName); - _backNavigationHeader = null; - await performValidation('pageNavigation', tenancyTypeDetails.backLink, backHeader); + if (process.env.WALES_POSTCODE === 'YES') { + if (claimantsName) { + await performValidation('pageNavigation', tenancyTypeDetails.backLink, writtenTerms.mainHeader); + } + } else { + if (claimantsName) { + await performValidation( + 'pageNavigation', + tenancyTypeDetails.backLink, + disputeClaimInterstitial.getMainHeader(claimantsName) + ); + } } await performAction('clickRadioButton', tenancyTypeDetails.yesRadioOption); await performValidation('pageNavigation', tenancyTypeDetails.saveForLaterButton, dashboard.mainHeader); diff --git a/src/test/ui/utils/controller.ts b/src/test/ui/utils/controller.ts index d77bc965d..7cdb99f5c 100644 --- a/src/test/ui/utils/controller.ts +++ b/src/test/ui/utils/controller.ts @@ -40,8 +40,6 @@ async function detectPageNavigation(): Promise { const currentUrl = executor.page.url(); if (!startAxeAudit && executor.page.url().includes('start-now')) { startAxeAudit = true; - } - if (!startFunctionalTests && executor.page.url().includes('free-legal-advice')) { startFunctionalTests = true; } const pageNavigated = currentUrl !== previousUrl; @@ -54,7 +52,7 @@ async function detectPageNavigation(): Promise { } async function validatePageIfNavigated(action: string): Promise { - if (action.includes('click') || action.includes('navigate')) { + if (action.includes('click') || action.includes('navigateToUrl')) { const pageNavigated = await detectPageNavigation(); const executor = getExecutor(); if (pageNavigated) { diff --git a/src/test/ui/utils/validations/custom-validations/pageContent.validation.ts b/src/test/ui/utils/validations/custom-validations/pageContent.validation.ts index cb01d0d93..a56ce5167 100644 --- a/src/test/ui/utils/validations/custom-validations/pageContent.validation.ts +++ b/src/test/ui/utils/validations/custom-validations/pageContent.validation.ts @@ -149,6 +149,7 @@ export class PageContentValidation implements IValidation { key.includes('Input') || key.includes('Hidden') || key.includes('Validation') || + key.includes('pageSlug') || key.includes('ErrorMessage') ) { continue; diff --git a/src/test/ui/utils/validations/custom-validations/pageNavigation.validation.ts b/src/test/ui/utils/validations/custom-validations/pageNavigation.validation.ts index b3c84128f..82f4375e1 100644 --- a/src/test/ui/utils/validations/custom-validations/pageNavigation.validation.ts +++ b/src/test/ui/utils/validations/custom-validations/pageNavigation.validation.ts @@ -4,7 +4,7 @@ import * as path from 'path'; import { Page, expect } from '@playwright/test'; import { performAction } from '../../controller'; -import { IValidation } from '../../interfaces'; +import { IValidation, validationRecord } from '../../interfaces'; type NavigationTestResult = { pageUrl: string; @@ -16,6 +16,7 @@ type NavigationTestResult = { error?: string; hasPFTFile: boolean; sourcePage?: string; + validationType?: 'element' | 'url'; }; export class PageNavigationValidation implements IValidation { @@ -29,7 +30,7 @@ export class PageNavigationValidation implements IValidation { private static readonly MAPPING_PATH = path.join(__dirname, '../../../config/urlToFileMapping.config.ts'); private static readonly PFT_DIR = path.join(__dirname, '../../../functional'); private static currentPageUrl: string = ''; - private static currentSourcePage: string | null = null; // Track the source page that initiated navigation + private static currentSourcePage: string | null = null; static setSourcePage(pageName: string): void { PageNavigationValidation.currentSourcePage = pageName; @@ -39,103 +40,154 @@ export class PageNavigationValidation implements IValidation { PageNavigationValidation.currentSourcePage = null; } - async validate(page: Page, validation: string, navigateButton: string, fieldName: string): Promise { + async validate(page: Page, validation: string, navigateButton: string, fieldName: validationRecord): Promise { PageNavigationValidation.currentPageUrl = page.url(); + let newPage: Page | null = null; + let isNewWindow = false; + if (navigateButton) { - if (navigateButton.includes('Back')) { + const popupPromise = page + .context() + .waitForEvent('page') + .catch(() => null); + + if (navigateButton.includes('Back') || navigateButton.includes('feedback')) { await performAction('clickLink', navigateButton); + await page.waitForTimeout(200); } else { await performAction('clickButton', navigateButton); } - } - if (validation !== 'mainHeader' && validation !== 'pageNavigation') { - if (PageNavigationValidation.currentPageUrl) { - await performAction('navigateToUrl', PageNavigationValidation.currentPageUrl); + + const popup = await Promise.race([popupPromise, new Promise(resolve => setTimeout(() => resolve(null), 1000))]); + + if (popup && (popup as Page).url() !== 'about:blank') { + newPage = popup as Page; + isNewWindow = true; + await newPage.waitForLoadState(); } - return; } + const pageToValidate = isNewWindow && newPage ? newPage : page; + try { - if (validation === 'mainHeader') { - await this.validateMainHeader(page, fieldName); - } else if (validation === 'pageNavigation') { - await this.validatePageNavigation(page, fieldName); - } + await this.validatePageNavigation(pageToValidate, fieldName); } finally { - if (PageNavigationValidation.currentPageUrl) { + if (newPage && !newPage.isClosed()) { + await newPage.close(); + } + if ( + PageNavigationValidation.currentPageUrl && + !isNewWindow && + page.url() !== PageNavigationValidation.currentPageUrl + ) { await performAction('navigateToUrl', PageNavigationValidation.currentPageUrl); } } } - private async validateMainHeader(page: Page, expectedHeader: string): Promise { - try { - const locator = page.locator('h1, h1.govuk-heading-xl, h1.govuk-heading-l'); - await expect(locator).toHaveText(expectedHeader); + private async validatePageNavigation(page: Page, fieldName: validationRecord): Promise { + let elementPassed = true; + let urlPassed = true; + let elementError: string | undefined; + let urlError: string | undefined; + let actualElementText = ''; + let expectedElementText = ''; + let actualUrl = ''; + let expectedUrlPattern = ''; - const pageName = await PageNavigationValidation.getPageNameFromUrl(page.url(), page); - const hasPFTFile = await PageNavigationValidation.hasPFTFile(pageName); - - PageNavigationValidation.navigationResults.push({ - pageUrl: page.url(), - pageName, - sourcePage: PageNavigationValidation.currentSourcePage || undefined, - testName: 'Main Header Validation', - passed: true, - expected: expectedHeader, - actual: expectedHeader, - hasPFTFile, - }); + try { + if (fieldName && typeof fieldName === 'object') { + const validationData = fieldName as any; + + if (validationData.element) { + expectedElementText = validationData.element; + const locator = page.locator( + `h1, h1.govuk-heading-xl, h1.govuk-heading-l, span:text-is("${expectedElementText}")` + ); + try { + await expect(locator).toHaveText(expectedElementText); + actualElementText = expectedElementText; + } catch (error) { + elementPassed = false; + actualElementText = + (await locator + .first() + .textContent() + .catch(() => 'Not found')) || 'Not found'; + elementError = error instanceof Error ? error.message.split('\n')[0] : String(error); + } + } - if (hasPFTFile) { - PageNavigationValidation.pagesPassed.add(pageName); + if (validationData.pageSlug) { + try { + expectedUrlPattern = `https://www.smartsurvey.co.uk/s/Poss_feedback/?pageurl=respond-to-claim/${validationData.pageSlug}`; + actualUrl = page.url(); + if (actualUrl !== expectedUrlPattern) { + urlPassed = false; + urlError = `URL mismatch. Expected: ${expectedUrlPattern}, Actual: ${actualUrl}`; + } + } catch (error) { + urlPassed = false; + actualUrl = page.url(); + urlError = error instanceof Error ? error.message.split('\n')[0] : String(error); + } + } + } else { + expectedElementText = String(fieldName); + const locator = page.locator('h1, h1.govuk-heading-xl, h1.govuk-heading-l'); + await expect(locator).toHaveText(expectedElementText); + actualElementText = expectedElementText; } - } catch (error) { - const actualText = await page - .locator('h1') - .first() - .textContent() - .catch(() => 'Not found'); const pageName = await PageNavigationValidation.getPageNameFromUrl(page.url(), page); const hasPFTFile = await PageNavigationValidation.hasPFTFile(pageName); + const overallPassed = elementPassed && urlPassed; + + if (!elementPassed) { + PageNavigationValidation.navigationResults.push({ + pageUrl: page.url(), + pageName, + sourcePage: PageNavigationValidation.currentSourcePage || undefined, + testName: 'Page Navigation Element Validation', + passed: false, + expected: expectedElementText, + actual: actualElementText, + error: elementError, + hasPFTFile, + validationType: 'element', + }); + } - PageNavigationValidation.navigationResults.push({ - pageUrl: page.url(), - pageName, - sourcePage: PageNavigationValidation.currentSourcePage || undefined, - testName: 'Main Header Validation', - passed: false, - expected: expectedHeader, - actual: actualText || 'Not found', - error: error instanceof Error ? error.message.split('\n')[0] : String(error), - hasPFTFile, - }); - } - } - - private async validatePageNavigation(page: Page, expectedHeader: string): Promise { - try { - const locator = page.locator('h1, h1.govuk-heading-xl, h1.govuk-heading-l'); - await expect(locator).toHaveText(expectedHeader); - - const pageName = await PageNavigationValidation.getPageNameFromUrl(page.url(), page); - const actualText = await locator.first().textContent(); - const hasPFTFile = await PageNavigationValidation.hasPFTFile(pageName); - - PageNavigationValidation.navigationResults.push({ - pageUrl: page.url(), - pageName, - sourcePage: PageNavigationValidation.currentSourcePage || undefined, - testName: 'Page Navigation Validation', - passed: true, - expected: expectedHeader, - actual: actualText || '', - hasPFTFile, - }); + if (!urlPassed && fieldName && typeof fieldName === 'object' && (fieldName as any).pageSlug) { + PageNavigationValidation.navigationResults.push({ + pageUrl: page.url(), + pageName, + sourcePage: PageNavigationValidation.currentSourcePage || undefined, + testName: 'Page Navigation URL Validation', + passed: false, + expected: expectedUrlPattern, + actual: actualUrl, + error: urlError || 'Page slug validation failed', + hasPFTFile, + validationType: 'url', + }); + } - if (hasPFTFile) { - PageNavigationValidation.pagesPassed.add(pageName); + if (overallPassed) { + PageNavigationValidation.navigationResults.push({ + pageUrl: page.url(), + pageName, + sourcePage: PageNavigationValidation.currentSourcePage || undefined, + testName: 'Page Navigation Validation', + passed: true, + expected: expectedElementText || 'URL validation passed', + actual: actualElementText || page.url(), + hasPFTFile, + }); + + if (hasPFTFile) { + PageNavigationValidation.pagesPassed.add(pageName); + } } } catch (error) { const pageName = await PageNavigationValidation.getPageNameFromUrl(page.url(), page); @@ -146,13 +198,28 @@ export class PageNavigationValidation implements IValidation { .catch(() => 'Not found'); const hasPFTFile = await PageNavigationValidation.hasPFTFile(pageName); + let expectedValue: string; + if (typeof fieldName === 'object' && fieldName !== null) { + const obj = fieldName as any; + const parts: string[] = []; + if (obj.element) { + parts.push(`element: "${obj.element}"`); + } + if (obj.pageSlug) { + parts.push(`pageSlug: "${obj.pageSlug}"`); + } + expectedValue = `{ ${parts.join(', ')} }`; + } else { + expectedValue = String(fieldName); + } + PageNavigationValidation.navigationResults.push({ pageUrl: page.url(), pageName, sourcePage: PageNavigationValidation.currentSourcePage || undefined, testName: 'Page Navigation Validation', passed: false, - expected: expectedHeader, + expected: expectedValue, actual: actualText || 'Not found', error: error instanceof Error ? error.message.split('\n')[0] : String(error), hasPFTFile, @@ -296,43 +363,36 @@ export class PageNavigationValidation implements IValidation { return; } - // Track failures by the source page (the page that initiated the navigation) - const failureDetails = new Map(); + const failureDetails = new Map(); const failedPages = new Set(); const passedPages = new Set(); - // First, identify all failures for (const result of PageNavigationValidation.navigationResults) { if (!result.passed) { - // If this failure has a source page, mark that source page as failed if (result.sourcePage) { failedPages.add(result.sourcePage); if (result.expected && result.actual) { failureDetails.set(result.sourcePage, { expected: result.expected, actual: result.actual, + validationType: result.validationType, }); } - } - // If no source page but the page itself has a PFT file, mark it as failed - else if (result.hasPFTFile) { + } else if (result.hasPFTFile) { failedPages.add(result.pageName); if (result.expected && result.actual) { failureDetails.set(result.pageName, { expected: result.expected, actual: result.actual, + validationType: result.validationType, }); } - } - // If it's a page without PFT file and no source page (like Dashboard), we can't attribute it - // So we log it but don't fail any specific page - else { + } else { console.log(` ⚠️ Unattributed failure on ${result.pageName}: ${result.error}`); } } } - // A page passes only if it has no failures and has a PFT file for (const result of PageNavigationValidation.navigationResults) { if ( result.passed && @@ -344,7 +404,6 @@ export class PageNavigationValidation implements IValidation { } } - // Add pages that were explicitly marked as passed (and haven't failed) for (const pageName of PageNavigationValidation.pagesPassed) { if (!failedPages.has(pageName)) { passedPages.add(pageName); @@ -379,7 +438,6 @@ export class PageNavigationValidation implements IValidation { ); } - // Show failure details if (failedPages.size > 0) { console.log('\n❌ FAILED NAVIGATION TESTS:'); @@ -389,6 +447,9 @@ export class PageNavigationValidation implements IValidation { if (details) { console.log(` Expected: ${details.expected}`); console.log(` Actual: ${details.actual}`); + if (details.validationType === 'url') { + console.log(` Note: Page slug URL validation failed`); + } } console.log(''); } diff --git a/src/test/unit/app/controller/controllerFactory.test.ts b/src/test/unit/app/controller/controllerFactory.test.ts index fa4021cda..30dc5b11e 100644 --- a/src/test/unit/app/controller/controllerFactory.test.ts +++ b/src/test/unit/app/controller/controllerFactory.test.ts @@ -78,7 +78,6 @@ describe('createGetController', () => { const translations: Record = { serviceName: 'Test Service', phase: 'ALPHA', - feedback: 'Feedback text', back: 'Back', languageToggle: 'Language toggle', }; @@ -115,7 +114,6 @@ describe('createGetController', () => { backUrl: null, serviceName: 'Test Service', phase: 'ALPHA', - feedback: 'Feedback text', back: 'Back', languageToggle: 'Language toggle', }) @@ -127,7 +125,6 @@ describe('createGetController', () => { const translations: Record = { serviceName: 'Test Service', phase: 'ALPHA', - feedback: 'Feedback text', back: 'Back', languageToggle: 'Language toggle', }; @@ -163,7 +160,6 @@ describe('createGetController', () => { backUrl: null, serviceName: 'Test Service', phase: 'ALPHA', - feedback: 'Feedback text', back: 'Back', languageToggle: 'Language toggle', }) @@ -175,7 +171,6 @@ describe('createGetController', () => { const translations: Record = { serviceName: 'Test Service', phase: 'ALPHA', - feedback: 'Feedback text', back: 'Back', languageToggle: 'Language toggle', }; @@ -208,7 +203,6 @@ describe('createGetController', () => { backUrl: null, serviceName: 'Test Service', phase: 'ALPHA', - feedback: 'Feedback text', back: 'Back', languageToggle: 'Language toggle', }) diff --git a/src/test/unit/middleware/pageTrackingUrl.test.ts b/src/test/unit/middleware/pageTrackingUrl.test.ts new file mode 100644 index 000000000..211ecc5ea --- /dev/null +++ b/src/test/unit/middleware/pageTrackingUrl.test.ts @@ -0,0 +1,60 @@ +import type { NextFunction, Request, Response } from 'express'; + +import { pageTrackingUrlMiddleware } from '../../../main/middleware/pageTrackingUrl'; + +describe('pageTrackingUrlMiddleware', () => { + let req: Partial; + let res: Partial; + let next: NextFunction; + + beforeEach(() => { + res = { + locals: {}, + }; + next = jest.fn(); + }); + + it('should set dashboard as the tracking URL for dashboard routes', () => { + req = { + path: '/dashboard/active', + }; + + pageTrackingUrlMiddleware(req as Request, res as Response, next); + + expect(res.locals?.pageTrackingUrl).toBe('dashboard'); + expect(next).toHaveBeenCalledTimes(1); + }); + + it('should strip the case id from case routes', () => { + req = { + path: '/case/1234567890123456/respond-to-claim/start-now', + }; + + pageTrackingUrlMiddleware(req as Request, res as Response, next); + + expect(res.locals?.pageTrackingUrl).toBe('respond-to-claim/start-now'); + expect(next).toHaveBeenCalledTimes(1); + }); + + it('should leave non-dashboard and non-case routes unchanged', () => { + req = { + path: '/contact-us', + }; + + pageTrackingUrlMiddleware(req as Request, res as Response, next); + + expect(res.locals?.pageTrackingUrl).toBe('/contact-us'); + expect(next).toHaveBeenCalledTimes(1); + }); + + it('should leave short case routes unchanged', () => { + req = { + path: '/case/1234567890123456', + }; + + pageTrackingUrlMiddleware(req as Request, res as Response, next); + + expect(res.locals?.pageTrackingUrl).toBe('/case/1234567890123456'); + expect(next).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/test/unit/modules/error-handler/index.test.ts b/src/test/unit/modules/error-handler/index.test.ts index dab370f3c..49a8ab74d 100644 --- a/src/test/unit/modules/error-handler/index.test.ts +++ b/src/test/unit/modules/error-handler/index.test.ts @@ -33,7 +33,6 @@ describe('error-handler', () => { 'errorPages.500.paragraph': 'Please try again in a few minutes.', serviceName: 'Possession claims', phase: 'ALPHA', - feedback: 'Feedback text', languageToggle: 'Language toggle', back: 'Back', }; @@ -337,7 +336,6 @@ describe('error-handler', () => { expect(res.locals.serviceName).toBe('Possession claims'); expect(res.locals.phase).toBe('ALPHA'); - expect(res.locals.feedback).toBe('Feedback text'); expect(res.locals.languageToggle).toBe('Language toggle'); expect(res.locals.back).toBe('Back'); }); diff --git a/src/test/unit/modules/i18n/index.test.ts b/src/test/unit/modules/i18n/index.test.ts index 7b4e90c26..7b6c0fa58 100644 --- a/src/test/unit/modules/i18n/index.test.ts +++ b/src/test/unit/modules/i18n/index.test.ts @@ -264,7 +264,6 @@ describe('i18n module', () => { const translations: Record = { serviceName: 'Test Service', phase: 'BETA', - feedback: 'Feedback', back: 'Back', languageToggle: 'Language', contactUsForHelp: 'Contact', @@ -277,7 +276,8 @@ describe('i18n module', () => { expect(result.serviceName).toBe('Test Service'); expect(result.phase).toBe('BETA'); - expect(result.feedback).toBe('Feedback'); + expect(result.back).toBe('Back'); + expect(result.languageToggle).toBe('Language'); }); it('should exclude keys that return themselves', () => {