diff --git a/playwright.config.ts b/playwright.config.ts index da025f06b..63cb90f70 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -19,6 +19,7 @@ export const enable_content_validation = process.env.ENABLE_CONTENT_VALIDATION | export const enable_error_message_validation = process.env.ENABLE_ERROR_MESSAGES_VALIDATION || 'false'; export const enable_navigation_tests = process.env.ENABLE_NAVIGATION_TESTS || 'false'; export const enable_axe_audit = process.env.ENABLE_AXE_AUDIT || 'true'; +export const enable_pft_debug_log = process.env.ENABLE_PFT_DEBUG_LOG || 'true'; export default defineConfig({ testDir: './src/test/ui', 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 45098e782..556856643 100644 --- a/src/test/ui/data/page-data/disputeClaimInterstitial.page.data.ts +++ b/src/test/ui/data/page-data/disputeClaimInterstitial.page.data.ts @@ -1,7 +1,7 @@ export const disputeClaimInterstitial = { getMainHeader: (claimantName: string): string => { const nameClaimant = - claimantName.substring(claimantName.length - 1) === 's' ? `${claimantName}'` : `${claimantName}’s`; + claimantName.substring(claimantName.length - 1) === 's' ? `${claimantName}'` : `${claimantName}’ss`; return nameClaimant + ' claim'; }, getWhenTheyMadeTheirClaimParagraph: (claimantName: string): string => { 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 6627e5831..5eea2c58c 100644 --- a/src/test/ui/data/page-data/landlordRegistered.page.data.ts +++ b/src/test/ui/data/page-data/landlordRegistered.page.data.ts @@ -12,7 +12,7 @@ export const landlordRegistered = { saveForLaterButton: `Save for later`, cymraegLink: `Cymraeg`, thereIsAProblemErrorMessageHeader: `There is a problem`, - selectIfYouAgreeWithLandlordsClaimRegisteredErrorMessage: `Select if you agree with the landlord’s claim to be registered`, + selectIfYouAgreeWithLandlordsClaimRegisteredErrorMessage: `Select incorrect 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/e2eTest/dashboard.spec.ts b/src/test/ui/e2eTest/dashboard.spec.ts index 82cbf9402..11daa4f40 100644 --- a/src/test/ui/e2eTest/dashboard.spec.ts +++ b/src/test/ui/e2eTest/dashboard.spec.ts @@ -3,11 +3,13 @@ import config from 'config'; import { createCaseApiData, submitCaseApiData } from '../data/api-data'; import { dashboard } from '../data/page-data'; +import { captureProcessEnvBeforeBeforeEach, logTestBeforeEachContext } from '../utils/common/pft-debug-log'; import { initializeExecutor, performAction, performActions, performValidation } from '../utils/controller'; const home_url = config.get('e2e.testUrl') as string; test.beforeEach(async ({ page }) => { + captureProcessEnvBeforeBeforeEach(); initializeExecutor(page); process.env.NOTICE_SERVED = 'NO'; process.env.TENANCY_TYPE = 'INTRODUCTORY_TENANCY'; @@ -20,6 +22,7 @@ test.beforeEach(async ({ page }) => { await performAction('navigateToUrl', home_url); await performAction('login'); await performAction('navigateToUrl', home_url + `/dashboard/${process.env.CASE_NUMBER}`); + logTestBeforeEachContext(); }); test.describe('Dashboard - e2e Journey @nightly', async () => { diff --git a/src/test/ui/e2eTest/pageNotFound.spec.ts b/src/test/ui/e2eTest/pageNotFound.spec.ts index c7ceb836f..8d5dd6daa 100644 --- a/src/test/ui/e2eTest/pageNotFound.spec.ts +++ b/src/test/ui/e2eTest/pageNotFound.spec.ts @@ -2,15 +2,18 @@ import { test } from '@playwright/test'; import config from 'config'; import { createCaseApiData, submitCaseApiData } from '../data/api-data'; +import { captureProcessEnvBeforeBeforeEach, logTestBeforeEachContext } from '../utils/common/pft-debug-log'; import { finaliseAllValidations, initializeExecutor, performAction, performValidation } from '../utils/controller'; const home_url = config.get('e2e.testUrl') as string; test.beforeEach(async ({ page }) => { + captureProcessEnvBeforeBeforeEach(); initializeExecutor(page); await performAction('navigateToUrl', home_url); await performAction('createUser', 'citizen', ['citizen']); await performAction('login'); + logTestBeforeEachContext(); }); test.afterEach(async () => { diff --git a/src/test/ui/e2eTest/respondToAClaim.spec.ts b/src/test/ui/e2eTest/respondToAClaim.spec.ts index f3a5adfe9..329fc74bb 100644 --- a/src/test/ui/e2eTest/respondToAClaim.spec.ts +++ b/src/test/ui/e2eTest/respondToAClaim.spec.ts @@ -21,11 +21,13 @@ import { tenancyDateDetails, tenancyTypeDetails, } from '../data/page-data'; +import { captureProcessEnvBeforeBeforeEach, logTestBeforeEachContext } from '../utils/common/pft-debug-log'; import { finaliseAllValidations, initializeExecutor, performAction, performValidation } from '../utils/controller'; const home_url = config.get('e2e.testUrl') as string; test.beforeEach(async ({ page }, testInfo) => { + captureProcessEnvBeforeBeforeEach(); initializeExecutor(page); process.env.CLAIMANT_NAME = submitCaseApiData.submitCasePayload.claimantName; if (testInfo.title.includes('NoticeServed - No')) { @@ -93,6 +95,7 @@ test.beforeEach(async ({ page }, testInfo) => { await performAction('login'); await performAction('navigateToUrl', home_url + `/case/${process.env.CASE_NUMBER}/respond-to-claim/start-now`); await performAction('clickButton', startNow.startNowButton); + logTestBeforeEachContext(); }); test.afterEach(async () => { @@ -103,7 +106,7 @@ test.afterEach(async () => { //All defendant details known pages and Rent-arrears routing is covered in submitCasePayload //Mix and match of testcases needs to updated in e2etests once complete routing is implemented. ex: (Tendency type HDPI-3316 etc.) test.describe('Respond to a claim - e2e Journey @nightly', async () => { - test('Respond to a claim @noDefendants @regression @accessibility', async () => { + test('Respond to a claim @PR @noDefendants @regression @accessibility', async () => { await performAction('selectLegalAdvice', freeLegalAdvice.yesRadioOption); await performAction('inputDefendantDetails', { fName: defendantNameCapture.firstNameTextInput, diff --git a/src/test/ui/e2eTest/respondToClaimWales.spec.ts b/src/test/ui/e2eTest/respondToClaimWales.spec.ts index 3342e9b61..486c4ebf1 100644 --- a/src/test/ui/e2eTest/respondToClaimWales.spec.ts +++ b/src/test/ui/e2eTest/respondToClaimWales.spec.ts @@ -17,11 +17,13 @@ import { tenancyTypeDetails, writtenTerms, } from '../data/page-data'; +import { captureProcessEnvBeforeBeforeEach, logTestBeforeEachContext } from '../utils/common/pft-debug-log'; import { finaliseAllValidations, initializeExecutor, performAction, performValidation } from '../utils/controller'; const home_url = config.get('e2e.testUrl') as string; test.beforeEach(async ({ page }) => { + captureProcessEnvBeforeBeforeEach(); initializeExecutor(page); process.env.WALES_POSTCODE = 'YES'; process.env.CLAIMANT_NAME = submitCaseApiDataWales.submitCasePayload.claimantName; @@ -34,6 +36,7 @@ test.beforeEach(async ({ page }) => { await performAction('login'); await performAction('navigateToUrl', home_url + `/case/${process.env.CASE_NUMBER}/respond-to-claim/start-now`); await performAction('clickButton', startNow.startNowButton); + logTestBeforeEachContext(); }); test.afterEach(async () => { diff --git a/src/test/ui/functional/correspondenceAddress.spec.ts b/src/test/ui/functional/correspondenceAddress.spec.ts index 6b84d8b74..dec1d55da 100644 --- a/src/test/ui/functional/correspondenceAddress.spec.ts +++ b/src/test/ui/functional/correspondenceAddress.spec.ts @@ -10,11 +10,13 @@ import { freeLegalAdvice, startNow, } from '../data/page-data'; +import { captureProcessEnvBeforeBeforeEach, logTestBeforeEachContext } from '../utils/common/pft-debug-log'; import { initializeExecutor, performAction, performValidation } from '../utils/controller'; const home_url = config.get('e2e.testUrl') as string; test.beforeEach(async ({ page }, testInfo) => { + captureProcessEnvBeforeBeforeEach(); initializeExecutor(page); process.env.TENANCY_TYPE = 'INTRODUCTORY_TENANCY'; process.env.GROUNDS = 'RENT_ARREARS_GROUND10'; @@ -38,6 +40,7 @@ test.beforeEach(async ({ page }, testInfo) => { await performAction('navigateToUrl', home_url + `/case/${process.env.CASE_NUMBER}/respond-to-claim/start-now`); console.log('caseId', process.env.CASE_NUMBER); await performAction('clickButton', startNow.startNowButton); + logTestBeforeEachContext(); }); //This test case will be deleted once correspondence address functional tests automatically handle page routing - will be implemented in a new story diff --git a/src/test/ui/functional/freeLegalAdvice.pft.ts b/src/test/ui/functional/freeLegalAdvice.pft.ts index 4df11f34d..75a90aefe 100644 --- a/src/test/ui/functional/freeLegalAdvice.pft.ts +++ b/src/test/ui/functional/freeLegalAdvice.pft.ts @@ -1,4 +1,4 @@ -import { dashboard, feedback, freeLegalAdvice, startNow } from '../data/page-data'; +import { dashboard, feedback, freeLegalAdvice } from '../data/page-data'; import { performAction, performValidation } from '../utils/controller'; export async function freeLegalAdviceErrorValidation(): Promise { @@ -14,7 +14,7 @@ export async function freeLegalAdviceNavigationTests(): Promise { element: feedback.tellUsWhatYouThinkParagraph, pageSlug: freeLegalAdvice.pageSlug, }); - await performValidation('pageNavigation', freeLegalAdvice.backLink, startNow.mainHeader); + await performValidation('pageNavigation', freeLegalAdvice.backLink, dashboard.mainHeader); await performAction('clickRadioButton', freeLegalAdvice.yesRadioOption); await performValidation('pageNavigation', freeLegalAdvice.saveForLaterButton, dashboard.mainHeader); } diff --git a/src/test/ui/functional/landlordRegistered.pft.ts b/src/test/ui/functional/landlordRegistered.pft.ts index 8d6f177b8..270b8a667 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, feedback, landlordRegistered } from '../data/page-data'; +import { disputeClaimInterstitial, feedback, landlordRegistered } from '../data/page-data'; import { performAction, performValidation } from '../utils/controller'; export async function landlordRegisteredErrorValidation(): Promise { @@ -24,5 +24,5 @@ export async function landlordRegisteredNavigationTests(): Promise { // --skipping this below line until pageNavigation validation supports to window handling-- story created HDPI-5329 in QA improvements board. //await performValidation('pageNavigation', registeredLandlord.publicRegisterLink,'Public Register'); await performAction('clickRadioButton', landlordRegistered.yesRadioOption); - await performValidation('pageNavigation', landlordRegistered.saveForLaterButton, dashboard.mainHeader); + await performValidation('pageNavigation', landlordRegistered.saveForLaterButton, landlordRegistered.mainHeader); } diff --git a/src/test/ui/test-README.md b/src/test/ui/test-README.md index 377e68f57..f70b7b6d0 100644 --- a/src/test/ui/test-README.md +++ b/src/test/ui/test-README.md @@ -30,6 +30,7 @@ ui/ │ ├── registry/ # Component registration │ │ ├── action.registry.ts # Action registry │ │ └── validation.registry.ts # Validation registry +│ ├── pft-debug-log.ts # Optional [PFT] debug logging (ENABLE_PFT_DEBUG_LOG) │ └── controller.ts # Controls the usage of actions and validations ├── testREADME.md # Framework documentation └── update-testReadMe.ts # Documentation auto-update script @@ -63,6 +64,18 @@ The framework's modular design consists of these key layers: Playwright 1.30+ | TypeScript 4.9+ ``` +### PFT debug logging (optional) + +When debugging **page navigation**, **error message**, or **page content** flows, enable structured console lines prefixed with `[PFT check: …]`: + +- Set **`ENABLE_PFT_DEBUG_LOG=true`** (e.g. in `.env`, or in CI for a single run). Value must be exactly **`true`** or **`false`**. +- Default is **off** (no extra console output). +- Implementation: `utils/common/pft-debug-log.ts`. Lines include **test title**, **page label**, **URL**, **expected**, and **actual** (long strings truncated). +- **Failure screenshots** (`test.info().attach` → HTML / Allure) are **not** controlled by `ENABLE_PFT_DEBUG_LOG`; they attach when a validation reports a failure and requests a screenshot. +- **`captureProcessEnvBeforeBeforeEach()`** (first line of `test.beforeEach`) and **`logTestBeforeEachContext()`** (end of the same hook): when `ENABLE_PFT_DEBUG_LOG=true`, prints one block with the test title and every **non-empty** `process.env` key whose value **changed** during that `beforeEach` (compared to the snapshot at the start). No allowlist to maintain. + +`playwright.config.ts` exports **`enable_pft_debug_log`** alongside other `ENABLE_*` flags. + ## 4. Actions and Validations ### Actions are listed in `src/test/ui/utils/registry/action.registry.ts` diff --git a/src/test/ui/utils/actions/custom-actions/triggerPageFunctionalTests.action.ts b/src/test/ui/utils/actions/custom-actions/triggerPageFunctionalTests.action.ts index 4b28c4e24..0554a51f6 100644 --- a/src/test/ui/utils/actions/custom-actions/triggerPageFunctionalTests.action.ts +++ b/src/test/ui/utils/actions/custom-actions/triggerPageFunctionalTests.action.ts @@ -7,7 +7,10 @@ import { enable_content_validation, enable_error_message_validation, enable_navigation_tests, + enable_pft_debug_log, } from '../../../../../../playwright.config'; +import { logUnmappedPftUrl } from '../../common/pft-debug-log'; +import { shortUrl } from '../../common/string.utils'; import { IAction } from '../../interfaces'; import { ErrorMessageValidation, @@ -34,7 +37,11 @@ export class TriggerPageFunctionalTestsAction implements IAction { private async triggerPageFunctionalTests(page: Page): Promise { const pageName = await this.getFileNameForPage(page); + if (enable_pft_debug_log === 'true') { + console.log(`[triggerFunctionalTests] entered url=${shortUrl(page.url())} page=${pageName ?? '(unmapped)'}`); + } if (!pageName) { + logUnmappedPftUrl(page, shortUrl(page.url())); return; } diff --git a/src/test/ui/utils/common/pft-debug-log.ts b/src/test/ui/utils/common/pft-debug-log.ts new file mode 100644 index 000000000..617f9a9ea --- /dev/null +++ b/src/test/ui/utils/common/pft-debug-log.ts @@ -0,0 +1,128 @@ +import * as fs from 'fs/promises'; +import * as path from 'path'; + +import type { Page } from '@playwright/test'; +import { test } from '@playwright/test'; + +import { enable_pft_debug_log } from '../../../../../playwright.config'; + +import { shortUrl, truncateForLog } from './string.utils'; + +// PFT: optional console when ENABLE_PFT_DEBUG_LOG; screenshots always on failure paths. + +let envBeforeBeforeEach: NodeJS.ProcessEnv | null = null; + +export function captureProcessEnvBeforeBeforeEach(): void { + envBeforeBeforeEach = { ...process.env }; +} + +export function logTestBeforeEachContext(): void { + if (enable_pft_debug_log !== 'true') { + return; + } + + const before = envBeforeBeforeEach; + const changed = before + ? Object.keys(process.env) + .filter(k => { + const v = process.env[k]; + return v !== undefined && v !== '' && before[k] !== v; + }) + .sort() + : []; + + const lines = changed.map(k => ` ${k}=${process.env[k]}`); + const body = + lines.length > 0 + ? lines.join('\n') + : before + ? ' (no process.env keys were added or changed during this beforeEach)' + : ' (call captureProcessEnvBeforeBeforeEach() at the start of beforeEach to log env changes)'; + + const { title } = test.info(); + console.log(['[PFT debug: beforeEach context]', ` test: ${truncateForLog(title, 200)}`, body].join('\n')); +} + +export type ValidationFailureCategory = 'page-content' | 'error-messages' | 'page-navigation'; + +const categoryLabel: Record = { + 'page-content': 'page content', + 'error-messages': 'error messages', + 'page-navigation': 'page navigation', +}; + +export async function attachValidationFailureScreenshot( + page: Page, + category: ValidationFailureCategory, + pageLabel: string +): Promise { + const label = categoryLabel[category]; + try { + const safe = (pageLabel.trim() || 'page').slice(0, 80); + const out = test.info().outputPath('validation-failures', `failure-${category}-${safe}-${Date.now()}.png`); + await fs.mkdir(path.dirname(out), { recursive: true }); + await page.screenshot({ path: out, fullPage: true }); + await test.info().attach(`Validation failure (${label}): ${safe}`, { + path: out, + contentType: 'image/png', + }); + } catch (err) { + console.warn( + `[pft-debug-log] screenshot (${category}, ${pageLabel}):`, + err instanceof Error ? err.message : String(err) + ); + } +} + +export async function reportValidationFailure( + page: Page, + category: ValidationFailureCategory, + pageLabel: string, + expected: string, + actual: string, + attachScreenshot: boolean +): Promise { + if (attachScreenshot) { + await attachValidationFailureScreenshot(page, category, pageLabel); + } + pftDebugReport({ + page, + pageLabel, + category: categoryLabel[category], + expected, + actual, + }); +} + +function formatPftCheck(page: Page, pageLabel: string, category: string, expected: string, actual: string): string { + const tag = `[PFT check: ${category}]`; + const testTitle = test.info().title; + return [ + `${tag} test: ${truncateForLog(testTitle, 200)}`, + `${tag} page: ${truncateForLog(pageLabel, 200)}`, + `${tag} url: ${shortUrl(page.url())}`, + `${tag} expected: ${truncateForLog(expected)}`, + `${tag} actual: ${truncateForLog(actual)}`, + ].join('\n'); +} + +export function pftDebugReport(options: { + page: Page; + pageLabel: string; + category: string; + expected: string; + actual: string; +}): void { + if (enable_pft_debug_log !== 'true') { + return; + } + const { page, pageLabel, category, expected, actual } = options; + console.log(formatPftCheck(page, pageLabel, category, expected, actual)); +} + +const UNMAPPED_EXPECTED = 'A matching key in urlToFileMapping.config.ts'; +const UNMAPPED_ACTUAL = 'No matching key — PFT skipped'; + +export function logUnmappedPftUrl(page: Page, pageLabel: string): void { + console.log(formatPftCheck(page, pageLabel, 'page functional tests', UNMAPPED_EXPECTED, UNMAPPED_ACTUAL)); +} diff --git a/src/test/ui/utils/common/string.utils.ts b/src/test/ui/utils/common/string.utils.ts index 82061ac85..ff617a1de 100644 --- a/src/test/ui/utils/common/string.utils.ts +++ b/src/test/ui/utils/common/string.utils.ts @@ -9,3 +9,16 @@ export function exactTextWithOptionalWhitespaceRegex(text: string): RegExp { export function formatTextToLowercaseSeparatedBySpace(value: string): string { return value.toLowerCase().replace(/_/g, ' ').trim(); } + +export function truncate(s: string, max: number, trim?: boolean): string { + const t = trim ? s.trim() : s; + return t.length <= max ? t : `${t.slice(0, max - 1)}…`; +} + +export function shortUrl(u: string, max = 88): string { + return truncate(u, max); +} + +export function truncateForLog(s: string, max = 800): string { + return truncate(s, max, true); +} diff --git a/src/test/ui/utils/controller.ts b/src/test/ui/utils/controller.ts index 7cdb99f5c..457347d23 100644 --- a/src/test/ui/utils/controller.ts +++ b/src/test/ui/utils/controller.ts @@ -88,7 +88,7 @@ export async function performAction( value?: actionData | actionRecord ): Promise { const executor = getExecutor(); - await validatePageIfNavigated(action); + //await validatePageIfNavigated(action); const actionInstance = ActionRegistry.getAction(action); let displayFieldName = fieldName; diff --git a/src/test/ui/utils/validations/custom-validations/error-message.validation.ts b/src/test/ui/utils/validations/custom-validations/error-message.validation.ts index 3c09c9564..e651aa9ee 100644 --- a/src/test/ui/utils/validations/custom-validations/error-message.validation.ts +++ b/src/test/ui/utils/validations/custom-validations/error-message.validation.ts @@ -3,6 +3,7 @@ import * as path from 'path'; import { Page } from '@playwright/test'; +import { reportValidationFailure } from '../../common/pft-debug-log'; import { IValidation, validationData, validationRecord } from '../../interfaces'; type ValidationResult = { @@ -104,6 +105,8 @@ export class ErrorMessageValidation implements IValidation { const pageUrl = page.url(); const pageName = await ErrorMessageValidation.getPageNameFromUrl(pageUrl, page); + await reportValidationFailure(page, 'error-messages', pageName, expected, actualText || 'Not found', !passed); + ErrorMessageValidation.results.push({ pageUrl, pageName, 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 a56ce5167..ca96487bd 100644 --- a/src/test/ui/utils/validations/custom-validations/pageContent.validation.ts +++ b/src/test/ui/utils/validations/custom-validations/pageContent.validation.ts @@ -3,6 +3,7 @@ import * as path from 'path'; import { Page } from '@playwright/test'; +import { attachValidationFailureScreenshot, reportValidationFailure } from '../../common/pft-debug-log'; import { escapeForRegex, exactTextWithOptionalWhitespaceRegex } from '../../common/string.utils'; import { IValidation } from '../../interfaces'; @@ -139,6 +140,17 @@ export class PageContentValidation implements IValidation { const pageData = await this.getPageData(pageName); if (!pageData) { + const pageDataPath = path.join(__dirname, '../../../data/page-data', `${pageName}.page.data.ts`); + if (fs.existsSync(pageDataPath)) { + await reportValidationFailure( + page, + 'page-content', + pageName, + `Page data file should export default or a named export (data/page-data/${pageName}.page.data.ts)`, + 'Failed to load or parse the file (syntax error or missing export)', + true + ); + } return; } @@ -162,6 +174,10 @@ export class PageContentValidation implements IValidation { } PageContentValidation.validationResults.set(pageUrl, pageResults); + + if (pageResults.some(r => r.status === 'fail')) { + await attachValidationFailureScreenshot(page, 'page-content', pageName); + } } private async getPageData(pageName: string): Promise { @@ -284,7 +300,7 @@ export class PageContentValidation implements IValidation { } if (failedPages.size > 0) { - console.log('\n❌ VALIDATION FAILED:'); + console.log('\n❌ FAILED PAGE CONTENT VALIDATION:'); for (const [pageName, pageFailures] of failedPages) { console.log(` Page: ${pageName}`); let pageFailureCount = 0; 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 82f4375e1..d2de7984f 100644 --- a/src/test/ui/utils/validations/custom-validations/pageNavigation.validation.ts +++ b/src/test/ui/utils/validations/custom-validations/pageNavigation.validation.ts @@ -3,6 +3,7 @@ import * as path from 'path'; import { Page, expect } from '@playwright/test'; +import { reportValidationFailure } from '../../common/pft-debug-log'; import { performAction } from '../../controller'; import { IValidation, validationRecord } from '../../interfaces'; @@ -189,6 +190,25 @@ export class PageNavigationValidation implements IValidation { PageNavigationValidation.pagesPassed.add(pageName); } } + + const expectedNavLog = [ + expectedElementText && `element: "${expectedElementText}"`, + expectedUrlPattern && `url: "${expectedUrlPattern}"`, + ] + .filter(Boolean) + .join('; '); + const actualNavLog = [actualElementText && `element: "${actualElementText}"`, `url: "${actualUrl || page.url()}"`] + .filter(Boolean) + .join('; '); + + await reportValidationFailure( + page, + 'page-navigation', + pageName, + expectedNavLog || 'page navigation validation', + actualNavLog, + !overallPassed + ); } catch (error) { const pageName = await PageNavigationValidation.getPageNameFromUrl(page.url(), page); const actualText = await page @@ -224,6 +244,15 @@ export class PageNavigationValidation implements IValidation { error: error instanceof Error ? error.message.split('\n')[0] : String(error), hasPFTFile, }); + + await reportValidationFailure( + page, + 'page-navigation', + pageName, + `h1 after navigation: "${expectedValue}"`, + `h1: "${(actualText || 'Not found').trim()}"`, + true + ); } }