diff --git a/e2e/pages/filing-app/complete-user-profile/VerifyLinkTargets.spec.ts b/e2e/pages/filing-app/complete-user-profile/VerifyLinkTargets.spec.ts new file mode 100644 index 000000000..a18929f76 --- /dev/null +++ b/e2e/pages/filing-app/complete-user-profile/VerifyLinkTargets.spec.ts @@ -0,0 +1,25 @@ +import { test } from '../../../fixtures/testFixture'; +import { expectLinkOpensSameTab } from '../../../utils/openLink'; +import { + SelectorLinkText, + expectAll, + selectCrumbtrailLink, + selectLinks, +} from '../../../utils/verifyLinkTargets'; + +test('Complete user profile: Verify link targets', async ({ page }) => { + const unauthenticatedHome = selectCrumbtrailLink( + page, + SelectorLinkText.crumbtrail.home, + ); + + const linksByText = selectLinks(page, [ + SelectorLinkText.gleif.long, + SelectorLinkText.privacyNotice, + ]); + + await expectAll( + [unauthenticatedHome, ...linksByText], + expectLinkOpensSameTab, + ); +}); diff --git a/e2e/pages/filing-app/filing-home/VerifyLinkTargets.spec.ts b/e2e/pages/filing-app/filing-home/VerifyLinkTargets.spec.ts new file mode 100644 index 000000000..0d6621554 --- /dev/null +++ b/e2e/pages/filing-app/filing-home/VerifyLinkTargets.spec.ts @@ -0,0 +1,169 @@ +import type { Page } from '@playwright/test'; +import { expect } from '@playwright/test'; +import { InstitutionCardTitle } from 'pages/Filing/FilingApp/InstitutionCard.helpers'; +import { test } from '../../../fixtures/testFixture'; +import { expectLinkOpensNewTab } from '../../../utils/openLink'; +import { + SelectorLinkText, + expectAll, + selectLinks, +} from '../../../utils/verifyLinkTargets'; + +/** + * Helpers + * */ + +// Verify a button with the given label is visible +const expectButtonVisible = async (page: Page, name: string) => { + const button = page.getByRole('button', { name }); + await expect(button).toBeVisible(); +}; + +// Labels for primary home page actions +const ActionLabel = { + startFiling: 'Start filing', + continueFiling: 'Continue filing', +}; + +// Verify we've navigated to the Filing homepage +const gotoFilingHome = async (page: Page) => { + await page.goto('/filing'); + + await expect(page.locator('h1')).toContainText( + 'File your small business lending data', + ); +}; + +/** + * Tests - Verify the state of the Filing homepage at each stage of the Filing process + * */ + +test('Start filing', async ({ page, navigateToFilingHome }) => { + navigateToFilingHome; + + await expect(page.getByText(InstitutionCardTitle.start)).toBeVisible(); + + const links = selectLinks(page, [ + SelectorLinkText.fig.long, + SelectorLinkText.fig.readAboutFiling, + ]); + + await expectAll(links, expectLinkOpensNewTab); + + await expectButtonVisible(page, ActionLabel.startFiling); +}); + +test('Provide type of financial institution', async ({ + page, + navigateToProvideTypeOfFinancialInstitution, +}) => { + navigateToProvideTypeOfFinancialInstitution; + await gotoFilingHome(page); + + await expect(page.getByText(InstitutionCardTitle.start)).toBeVisible(); + + const links = selectLinks(page, [ + SelectorLinkText.fig.long, + SelectorLinkText.fig.readAboutFiling, + ]); + + await expectAll(links, expectLinkOpensNewTab); +}); + +test('Upload file', async ({ page, navigateToUploadFile }) => { + navigateToUploadFile; + await gotoFilingHome(page); + + await expect(page.getByText(InstitutionCardTitle.upload)).toBeVisible(); + await expectButtonVisible(page, ActionLabel.continueFiling); + + const links = selectLinks(page, [ + SelectorLinkText.fig.long, + SelectorLinkText.fig.readAboutFiling, + ]); + + await expectAll(links, expectLinkOpensNewTab); +}); + +test('Logic errors', async ({ + page, + navigateToLogicErrorsAfterLogicErrorsUpload, +}) => { + navigateToLogicErrorsAfterLogicErrorsUpload; + await gotoFilingHome(page); + + await expect(page.getByText(InstitutionCardTitle.errors)).toBeVisible(); + await expectButtonVisible(page, ActionLabel.continueFiling); + + const links = selectLinks(page, [ + SelectorLinkText.fig.long, + SelectorLinkText.fig.readAboutValidations, + ]); + + await expectAll(links, expectLinkOpensNewTab); +}); + +test('Syntax errors', async ({ + page, + navigateToSyntaxErrorsAfterSyntaxErrorsUpload, +}) => { + navigateToSyntaxErrorsAfterSyntaxErrorsUpload; + await gotoFilingHome(page); + + await expect(page.getByText(InstitutionCardTitle.errors)).toBeVisible(); + await expectButtonVisible(page, ActionLabel.continueFiling); + + const links = selectLinks(page, [ + SelectorLinkText.fig.long, + SelectorLinkText.fig.readAboutValidations, + ]); + + await expectAll(links, expectLinkOpensNewTab); +}); + +test('Warnings', async ({ + page, + navigateToReviewWarningsAfterOnlyWarningsUpload, +}) => { + navigateToReviewWarningsAfterOnlyWarningsUpload; + await gotoFilingHome(page); + + await expect(page.getByText(InstitutionCardTitle.warnings)).toBeVisible(); + await expectButtonVisible(page, ActionLabel.continueFiling); + + const links = selectLinks(page, [ + SelectorLinkText.fig.long, + SelectorLinkText.fig.readAboutValidations, + ]); + + await expectAll(links, expectLinkOpensNewTab); +}); + +test('Provide filing details', async ({ + page, + navigateToProvideFilingDetails, +}) => { + navigateToProvideFilingDetails; + await gotoFilingHome(page); + + await expect( + page.getByText(InstitutionCardTitle.provideDetails), + ).toBeVisible(); + await expectButtonVisible(page, ActionLabel.continueFiling); + + const links = selectLinks(page, [SelectorLinkText.fig.long]); + + await expectAll(links, expectLinkOpensNewTab); +}); + +test('Sign and submit', async ({ page, navigateToSignAndSubmit }) => { + navigateToSignAndSubmit; + await gotoFilingHome(page); + + await expect(page.getByText(InstitutionCardTitle.signSubmit)).toBeVisible(); + await expectButtonVisible(page, ActionLabel.continueFiling); + + const links = selectLinks(page, [SelectorLinkText.fig.long]); + + await expectAll(links, expectLinkOpensNewTab); +}); diff --git a/e2e/pages/filing-app/filing-step-routing/errorsLogic.spec.ts b/e2e/pages/filing-app/filing-step-routing/errorsLogic.spec.ts index 330af8488..ee50f4ea2 100644 --- a/e2e/pages/filing-app/filing-step-routing/errorsLogic.spec.ts +++ b/e2e/pages/filing-app/filing-step-routing/errorsLogic.spec.ts @@ -1,5 +1,17 @@ +import { expect } from '@playwright/test'; import { test } from '../../../fixtures/testFixture'; +import { + expectLinkOpensNewTab, + expectLinkOpensSameTab, +} from '../../../utils/openLink'; import { checkSnapshot } from '../../../utils/snapshotTesting'; +import { + SelectorLinkText, + expectAll, + expectLogoutButtonVisible, + selectAllNavLinks, + selectLink, +} from '../../../utils/verifyLinkTargets'; import { verifyRedirects } from './_shared'; const testLabel = 'Filing step routing (Errors: Logic)'; @@ -28,3 +40,36 @@ test( await checkSnapshot(page); }, ); + +test('Verify link targets (Errors: Logic)', async ({ + page, + navigateToLogicErrorsAfterLogicErrorsUpload, +}) => { + navigateToLogicErrorsAfterLogicErrorsUpload; + + // Same tab + const navlinks = await selectAllNavLinks(page); + expect(navlinks.length).toEqual(3); + await expectAll(navlinks, expectLinkOpensSameTab); + + await expectLogoutButtonVisible(page); + + const uploadNewFile = selectLink(page, SelectorLinkText.upload.aNewFile); + await expectLinkOpensSameTab(uploadNewFile); + + // New tab + const figErrorDefinitions = await page + .locator('.validation-info-section a') + .all(); + for (const figPage of figErrorDefinitions) { + // eslint-disable-next-line no-await-in-loop + const errorLabel = await figPage.textContent(); + // eslint-disable-next-line no-await-in-loop + await test.step(`FIG error description link opens new tab: ${errorLabel}`, async () => { + await expectLinkOpensNewTab(figPage); + }); + } + + const figDataValidation = selectLink(page, SelectorLinkText.fig.section4); + await expectLinkOpensNewTab(figDataValidation); +}); diff --git a/e2e/pages/filing-app/filing-step-routing/errorsSyntax.spec.ts b/e2e/pages/filing-app/filing-step-routing/errorsSyntax.spec.ts index 08b6da380..ba89ce6c6 100644 --- a/e2e/pages/filing-app/filing-step-routing/errorsSyntax.spec.ts +++ b/e2e/pages/filing-app/filing-step-routing/errorsSyntax.spec.ts @@ -1,5 +1,17 @@ +import { expect } from '@playwright/test'; import { test } from '../../../fixtures/testFixture'; +import { + expectLinkOpensNewTab, + expectLinkOpensSameTab, +} from '../../../utils/openLink'; import { checkSnapshot } from '../../../utils/snapshotTesting'; +import { + SelectorLinkText, + expectAll, + expectLogoutButtonVisible, + selectAllNavLinks, + selectLink, +} from '../../../utils/verifyLinkTargets'; import { verifyRedirects } from './_shared'; const testLabel = 'Filing step routing (Errors: Syntax)'; @@ -33,3 +45,36 @@ test( await checkSnapshot(page); }, ); + +test('Verify link targets (Errors: Syntax)', async ({ + page, + navigateToSyntaxErrorsAfterSyntaxErrorsUpload, +}) => { + navigateToSyntaxErrorsAfterSyntaxErrorsUpload; + + // Same tab + const navlinks = await selectAllNavLinks(page); + expect(navlinks.length).toEqual(3); + await expectAll(navlinks, expectLinkOpensSameTab); + + await expectLogoutButtonVisible(page); + + const uploadNewFile = selectLink(page, SelectorLinkText.upload.aNewFile); + await expectLinkOpensSameTab(uploadNewFile); + + // New tab + const figErrorDefinitions = await page + .locator('.validation-info-section a') + .all(); + for (const figPage of figErrorDefinitions) { + // eslint-disable-next-line no-await-in-loop + const errorLabel = await figPage.textContent(); + // eslint-disable-next-line no-await-in-loop + await test.step(`FIG error description link opens new tab: ${errorLabel}`, async () => { + await expectLinkOpensNewTab(figPage); + }); + } + + const figDataValidation = selectLink(page, SelectorLinkText.fig.section4); + await expectLinkOpensNewTab(figDataValidation); +}); diff --git a/e2e/pages/filing-app/filing-step-routing/pointOfContact.spec.ts b/e2e/pages/filing-app/filing-step-routing/pointOfContact.spec.ts index 95f04bf79..84debc5e6 100644 --- a/e2e/pages/filing-app/filing-step-routing/pointOfContact.spec.ts +++ b/e2e/pages/filing-app/filing-step-routing/pointOfContact.spec.ts @@ -1,5 +1,17 @@ +import { expect } from '@playwright/test'; import { test } from '../../../fixtures/testFixture'; +import { + expectLinkOpensNewTab, + expectLinkOpensSameTab, +} from '../../../utils/openLink'; import { checkSnapshot } from '../../../utils/snapshotTesting'; +import { + SelectorLinkText, + expectAll, + expectLogoutButtonVisible, + selectAllNavLinks, + selectLink, +} from '../../../utils/verifyLinkTargets'; import { verifyRedirects } from './_shared'; const testLabel = 'Filing step routing (Point of Contact)'; @@ -25,3 +37,32 @@ test(testLabel, async ({ page, navigateToProvideFilingDetails }) => { }); await checkSnapshot(page); }); + +test('Verify link targets (Provide filing details)', async ({ + page, + navigateToProvideFilingDetails, +}) => { + navigateToProvideFilingDetails; + + // Same tab + const navlinks = await selectAllNavLinks(page); + expect(navlinks.length).toEqual(3); + await expectAll(navlinks, expectLinkOpensSameTab); + + await expectLogoutButtonVisible(page); + + // New tab + const indexRegLinks = await page.locator('a:has-text("§")').all(); + + for (const indexRegPage of indexRegLinks) { + // eslint-disable-next-line no-await-in-loop + const title = await indexRegPage.textContent(); + // eslint-disable-next-line no-await-in-loop + await test.step(`iRegs link opens new tab: ${title}`, async () => { + await expectLinkOpensNewTab(indexRegPage); + }); + } + + const privacyNotice = selectLink(page, SelectorLinkText.privacyNotice); + await expectLinkOpensNewTab(privacyNotice); +}); diff --git a/e2e/pages/filing-app/filing-step-routing/warnings.spec.ts b/e2e/pages/filing-app/filing-step-routing/warnings.spec.ts index 40c7260b2..494827579 100644 --- a/e2e/pages/filing-app/filing-step-routing/warnings.spec.ts +++ b/e2e/pages/filing-app/filing-step-routing/warnings.spec.ts @@ -1,5 +1,17 @@ +import { expect } from '@playwright/test'; import { test } from '../../../fixtures/testFixture'; +import { + expectLinkOpensNewTab, + expectLinkOpensSameTab, +} from '../../../utils/openLink'; import { checkSnapshot } from '../../../utils/snapshotTesting'; +import { + SelectorLinkText, + expectAll, + expectLogoutButtonVisible, + selectAllNavLinks, + selectLink, +} from '../../../utils/verifyLinkTargets'; import { verifyRedirects } from './_shared'; const testLabel = 'Filing step routing (Warnings)'; @@ -28,3 +40,47 @@ test( await checkSnapshot(page); }, ); + +test('Verify link targets (Warnings)', async ({ + page, + navigateToReviewWarningsAfterOnlyWarningsUpload, +}) => { + navigateToReviewWarningsAfterOnlyWarningsUpload; + + await test.step('Open in same tab', async () => { + const navlinks = await selectAllNavLinks(page); + expect(navlinks.length).toEqual(3); + await expectAll(navlinks, expectLinkOpensSameTab); + + await expectLogoutButtonVisible(page); + + const uploadNewFile = selectLink(page, SelectorLinkText.upload.aNewFile); + await expectLinkOpensSameTab(uploadNewFile); + }); + + await test.step('Open in new tab', async () => { + const figWarningsSingle = await page + .locator('#single-field-warnings a') + .all(); + + const figWarningMulti = await page.locator('#multi-field-warnings a').all(); + + for (const [index, figPage] of [ + ...figWarningsSingle.entries(), + ...figWarningMulti.entries(), + ]) { + // eslint-disable-next-line no-await-in-loop + const warningID = await figPage.textContent(); + // eslint-disable-next-line no-await-in-loop + await test.step(`FIG warning description link #${ + index + 1 + }: ${warningID}`, async () => { + await expectLinkOpensNewTab(figPage); + }); + } + + const figDataValidation = selectLink(page, SelectorLinkText.fig.section4); + + await expectLinkOpensNewTab(figDataValidation); + }); +}); diff --git a/e2e/pages/filing-app/landing-page/VerifyLinkTargets.spec.ts b/e2e/pages/filing-app/landing-page/VerifyLinkTargets.spec.ts new file mode 100644 index 000000000..a32c19f4e --- /dev/null +++ b/e2e/pages/filing-app/landing-page/VerifyLinkTargets.spec.ts @@ -0,0 +1,51 @@ +import { expect } from '@playwright/test'; +import { test } from '../../../fixtures/testFixture'; +import { + expectLinkOpensNewTab, + expectLinkOpensSameTab, +} from '../../../utils/openLink'; +import { + SelectorLinkText, + expectAll, + expectLogoutButtonVisible, + selectAllNavLinks, + selectLink, +} from '../../../utils/verifyLinkTargets'; + +test('Authenticated homepage: Verify link targets', async ({ + page, + navigateToAuthenticatedHomePage, +}) => { + navigateToAuthenticatedHomePage; + + await test.step('Open in same tab', async () => { + const navlinks = await selectAllNavLinks(page); + expect(navlinks.length).toEqual(3); + await expectAll(navlinks, expectLinkOpensSameTab); + + await expectLogoutButtonVisible(page); + + const institutionProfile = await page + .locator('h3', { hasText: 'Review your financial institution profile' }) + .locator('..') + .getByRole('link'); + await expectLinkOpensSameTab(institutionProfile); + }); + + await test.step('Open in new tab', async () => { + const finalRule = selectLink(page, SelectorLinkText.sidebar.finalRule); + const filingInstructionGuide = selectLink( + page, + SelectorLinkText.sidebar.collection, + ); + const collectionReportingReqs = selectLink( + page, + SelectorLinkText.sidebar.collection, + ); + + await expectAll( + [finalRule, filingInstructionGuide, collectionReportingReqs], + expectLinkOpensNewTab, + ); + }); +}); diff --git a/e2e/pages/filing-app/provide-type-of-financial-institution/VerifyLinkTargets.spec.ts b/e2e/pages/filing-app/provide-type-of-financial-institution/VerifyLinkTargets.spec.ts new file mode 100644 index 000000000..fcf24ff2a --- /dev/null +++ b/e2e/pages/filing-app/provide-type-of-financial-institution/VerifyLinkTargets.spec.ts @@ -0,0 +1,58 @@ +import { expect } from '@playwright/test'; +import { test } from '../../../fixtures/testFixture'; +import { + expectLinkOpensNewTab, + expectLinkOpensSameTab, +} from '../../../utils/openLink'; +import { + expectAll, + expectLogoutButtonVisible, + selectAllNavLinks, +} from '../../../utils/verifyLinkTargets'; + +test('Verify link targets: Provide Type of Financial Institution', async ({ + page, + navigateToProvideTypeOfFinancialInstitution, +}) => { + navigateToProvideTypeOfFinancialInstitution; + // New tab + await test.step(`New tab: iRegs links`, async () => { + const indexRegLinks = await page.locator('a:has-text("§")').all(); + + for (const [index, indexRegPage] of indexRegLinks.entries()) { + // eslint-disable-next-line no-await-in-loop + const title = await indexRegPage.textContent(); + // eslint-disable-next-line no-await-in-loop + await test.step(`iRegs link #${index + 1}: ${title}`, async () => { + await expectLinkOpensNewTab(indexRegPage); + }); + } + }); + + // Same tab + await test.step(`Same tab: Nav bar links`, async () => { + const navlinks = await selectAllNavLinks(page); + expect(navlinks.length).toEqual(3); + await expectAll(navlinks, expectLinkOpensSameTab); + + await expectLogoutButtonVisible(page); + }); + + await test.step(`Same tab: Update financial profile links`, async () => { + const updateFinancialProfileLinks = await page + .getByRole('link', { + name: 'update your financial institution profile', + }) + .all(); + + for (const [ + index, + updateProfile, + ] of updateFinancialProfileLinks.entries()) { + // eslint-disable-next-line no-await-in-loop + await test.step(`Update financial profile #${index + 1}`, async () => { + await expectLinkOpensSameTab(updateProfile); + }); + } + }); +}); diff --git a/e2e/pages/filing-app/sign-and-submit/completeFilingFlowWithOnlyWarnings.spec.ts b/e2e/pages/filing-app/sign-and-submit/completeFilingFlowWithOnlyWarnings.spec.ts index 678a5c123..7f5db6008 100644 --- a/e2e/pages/filing-app/sign-and-submit/completeFilingFlowWithOnlyWarnings.spec.ts +++ b/e2e/pages/filing-app/sign-and-submit/completeFilingFlowWithOnlyWarnings.spec.ts @@ -1,6 +1,15 @@ import { expect } from '@playwright/test'; import { test } from '../../../fixtures/testFixture'; +import { + expectLinkOpensNewTab, + expectLinkOpensSameTab, +} from '../../../utils/openLink'; import { checkSnapshot } from '../../../utils/snapshotTesting'; +import { + expectAll, + expectLogoutButtonVisible, + selectAllNavLinks, +} from '../../../utils/verifyLinkTargets'; test('Sign and submit: complete filing flow with only warnings', async ({ page, @@ -16,4 +25,77 @@ test('Sign and submit: complete filing flow with only warnings', async ({ ).toBeVisible(); await checkSnapshot(page); }); + + await test.step('Sign and submit: verify link targets', async () => { + // New tab + await test.step(`New tab: iRegs links`, async () => { + const indexRegLinks = await page.locator('a:has-text("§")').all(); + + for (const [index, indexRegPage] of indexRegLinks.entries()) { + // eslint-disable-next-line no-await-in-loop + const title = await indexRegPage.textContent(); + // eslint-disable-next-line no-await-in-loop + await test.step(`iRegs link #${index + 1}: ${title}`, async () => { + await expectLinkOpensNewTab(indexRegPage); + }); + } + }); + + // Same tab + await test.step(`Same tab: Nav bar links`, async () => { + const navlinks = await selectAllNavLinks(page); + expect(navlinks.length).toEqual(3); + await expectAll(navlinks, expectLinkOpensSameTab); + + await expectLogoutButtonVisible(page); + }); + + await test.step(`Same tab: Update financial profile links`, async () => { + const updateFinancialProfileLinks = await page + .getByRole('link', { + name: 'update your financial institution profile', + }) + .all(); + + for (const [ + index, + updateProfile, + ] of updateFinancialProfileLinks.entries()) { + // eslint-disable-next-line no-await-in-loop + await test.step(`Update financial profile #${index + 1}`, async () => { + await expectLinkOpensSameTab(updateProfile); + }); + } + }); + + await test.step(`Same tab: Update filing details links`, async () => { + const updateFilingDetailsLinks = await page + .getByRole('button', { + name: 'update your filing details', + }) + .all(); + + for (const [index, updateDetails] of updateFilingDetailsLinks.entries()) { + // eslint-disable-next-line no-await-in-loop + await test.step(`Update financial details #${index + 1}`, async () => { + await expectLinkOpensSameTab(updateDetails); + }); + } + }); + + await test.step(`Same tab: Upload a new file links`, async () => { + const uploadANewFileLinks = await page + .getByRole('link', { + name: 'upload a new file', + }) + .all(); + + for (const [index, uploadFileLink] of uploadANewFileLinks.entries()) { + // eslint-disable-next-line no-await-in-loop + await test.step(`Upload a new file #${index + 1}`, async () => { + await expectLinkOpensSameTab(uploadFileLink); + }); + } + }); + }); }); diff --git a/e2e/pages/filing-app/uploadFile/VerifyLinkTargets.spec.ts b/e2e/pages/filing-app/uploadFile/VerifyLinkTargets.spec.ts new file mode 100644 index 000000000..7ad2755f5 --- /dev/null +++ b/e2e/pages/filing-app/uploadFile/VerifyLinkTargets.spec.ts @@ -0,0 +1,29 @@ +import { expect } from '@playwright/test'; +import { test } from '../../../fixtures/testFixture'; +import { + expectLinkOpensNewTab, + expectLinkOpensSameTab, +} from '../../../utils/openLink'; +import { + SelectorLinkText, + expectAll, + expectLogoutButtonVisible, + selectAllNavLinks, + selectLinks, +} from '../../../utils/verifyLinkTargets'; + +test('Verify link targets', async ({ page, navigateToUploadFile }) => { + await navigateToUploadFile; + + // New tab + const figLinks = selectLinks(page, [SelectorLinkText.fig.long]); + + await expectAll(figLinks, expectLinkOpensNewTab); + + // Same tab + const navlinks = await selectAllNavLinks(page); + expect(navlinks.length).toEqual(3); + await expectAll(navlinks, expectLinkOpensSameTab); + + await expectLogoutButtonVisible(page); +}); diff --git a/e2e/pages/shared-lending-platform/InstitutionProfile.spec.ts b/e2e/pages/shared-lending-platform/InstitutionProfile.spec.ts index 7c6648a03..bf3265f68 100644 --- a/e2e/pages/shared-lending-platform/InstitutionProfile.spec.ts +++ b/e2e/pages/shared-lending-platform/InstitutionProfile.spec.ts @@ -1,6 +1,6 @@ import { expect } from '@playwright/test'; import { test } from '../../fixtures/testFixture'; -import { clickLinkWithRetry } from '../../utils/clickExternalLinkWithRetry'; +import { openLinkNewTab, openLinkSameTab } from '../../utils/openLink'; import { checkSnapshot } from '../../utils/snapshotTesting'; test('Institution Profile Page', async ({ page, navigateToFilingHome }) => { @@ -272,20 +272,24 @@ test('Institution Profile Page', async ({ page, navigateToFilingHome }) => { // GLEIF link await test.step('GLEIF links', async () => { - await clickLinkWithRetry({ + const link = await page.getByRole('link', { + name: 'GLEIF', + exact: true, + }); + + const newTab = await openLinkNewTab({ page, - target: page.getByRole('link', { - name: 'GLEIF', - exact: true, - }), + target: link, }); - await expect(page).toHaveURL( + await expect(newTab).toHaveURL( 'https://www.gleif.org/en/about-lei/get-an-lei-find-lei-issuing-organizations', ); - await expect(page).toHaveTitle( + await expect(newTab).toHaveTitle( 'Get an LEI: Find LEI Issuing Organizations - LEI – GLEIF', ); - await page.goBack(); + await newTab.close(); + + // Verify initial tab is correct await expect(page.locator('h1'), 'h1 is correct').toContainText( 'View your financial institution profile', ); @@ -302,7 +306,7 @@ test('Institution Profile Page', async ({ page, navigateToFilingHome }) => { // eslint-disable-next-line no-await-in-loop await test.step(`fipLink: ${index + 1}`, async () => { await test.step('Click: link', async () => { - await clickLinkWithRetry({ + await openLinkSameTab({ page, target: fipLink, }); @@ -328,16 +332,16 @@ test('Institution Profile Page', async ({ page, navigateToFilingHome }) => { for (const [index, frbLink] of frbLinks.entries()) { // eslint-disable-next-line no-await-in-loop await test.step(`frbLink: ${index + 1}`, async () => { - await test.step('Click: link', async () => { - await clickLinkWithRetry({ - page, - target: frbLink, - }); + const newTab = await openLinkNewTab({ + page, + target: frbLink, }); - await expect(page, 'Resolves correctly').toHaveURL( + await expect(newTab, 'Resolves correctly').toHaveURL( 'https://www.federalreserve.gov/apps/reportingforms/Report/Index/FR_Y-10', ); - await page.goBack(); + await newTab.close(); + + // Verify initial tab is correct await expect(page.locator('h1'), 'h1 is correct').toContainText( 'View your financial institution profile', ); diff --git a/e2e/pages/shared-lending-platform/Navigation.spec.ts b/e2e/pages/shared-lending-platform/Navigation.spec.ts index db063f69b..fe08ffed9 100644 --- a/e2e/pages/shared-lending-platform/Navigation.spec.ts +++ b/e2e/pages/shared-lending-platform/Navigation.spec.ts @@ -1,6 +1,6 @@ import { expect } from '@playwright/test'; import { test } from '../../fixtures/testFixture'; -import { clickLinkWithRetry } from '../../utils/clickExternalLinkWithRetry'; +import { openLinkSameTab } from '../../utils/openLink'; import { checkSnapshot } from '../../utils/snapshotTesting'; // Note: Skipped snapshot tests for pages outside of the SBL site @@ -66,7 +66,7 @@ test('Navigation', async ({ page, navigateToFilingHome }) => { }); await test.step('Footer Navigation', async () => { - await clickLinkWithRetry({ + await openLinkSameTab({ page, target: page .locator('.o-footer') @@ -224,14 +224,14 @@ test('Navigation', async ({ page, navigateToFilingHome }) => { ); await page.goBack(); - await clickLinkWithRetry({ + await openLinkSameTab({ page, target: page.locator('.o-footer').getByRole('link', { name: 'USA.gov' }), }); await expect(page).toHaveURL(/.*usa.gov/); await page.goBack(); - await clickLinkWithRetry({ + await openLinkSameTab({ page, target: page .locator('.o-footer') @@ -255,7 +255,7 @@ test('Navigation', async ({ page, navigateToFilingHome }) => { await checkSnapshot(page); // Test CFPB Logo Link - await clickLinkWithRetry({ page, target: page.getByLabel('Home') }); + await openLinkSameTab({ page, target: page.getByLabel('Home') }); await expect(page).toHaveURL('https://www.consumerfinance.gov/'); }); }); diff --git a/e2e/pages/shared-lending-platform/UpdateInstitutionProfile.spec.ts b/e2e/pages/shared-lending-platform/UpdateInstitutionProfile.spec.ts index eed29514f..3e994bead 100644 --- a/e2e/pages/shared-lending-platform/UpdateInstitutionProfile.spec.ts +++ b/e2e/pages/shared-lending-platform/UpdateInstitutionProfile.spec.ts @@ -2,8 +2,18 @@ import { expect } from '@playwright/test'; import { DefaultInputCharLimit } from 'utils/constants'; import { test } from '../../fixtures/testFixture'; import { assertTextInput } from '../../utils/inputValidators'; +import { + expectLinkOpensNewTab, + expectLinkOpensSameTab, +} from '../../utils/openLink'; import { checkSnapshot } from '../../utils/snapshotTesting'; import { controlUnicode } from '../../utils/unicodeConstants'; +import { + SelectorLinkText, + expectAll, + selectAllNavLinks, + selectLink, +} from '../../utils/verifyLinkTargets'; test('Update Institution Profile Page', async ({ page, @@ -308,3 +318,48 @@ test('Update Institution Profile Page: Check Character Limits', async ({ await checkSnapshot(page); }); }); + +test('Update Institution Profile Page: Verify link targets', async ({ + page, + navigateToFilingHome, +}) => { + await test.step('Visit User Profile Page', async () => { + navigateToFilingHome; + await page.goto('/profile/view'); + }); + + await test.step('Visit Institution Profile page', async () => { + await page + .getByRole('link', { name: 'RegTech Regional Reserve - ' }) + .click(); + await page + .getByRole('link', { + name: 'Update your financial institution profile', + }) + .first() + .click(); + }); + + await test.step('Verify link targets', async () => { + await test.step('Opens same tab', async () => { + const viewInstitutionProfile = await page.getByRole('link', { + name: 'View your financial institution profile', + }); + await expectLinkOpensSameTab(viewInstitutionProfile); + + const navlinks = await selectAllNavLinks(page); + expect(navlinks.length).toEqual(3); + await expectAll(navlinks, expectLinkOpensSameTab); + }); + + await test.step('Opens new tab', async () => { + const gleif = selectLink(page, SelectorLinkText.gleif.short); + await expectLinkOpensNewTab(gleif); + + const federalReserveBoard = await page.getByRole('link', { + name: 'Federal Reserve Board', + }); + await expectLinkOpensNewTab(federalReserveBoard); + }); + }); +}); diff --git a/e2e/pages/shared-lending-platform/UserProfile.spec.ts b/e2e/pages/shared-lending-platform/UserProfile.spec.ts index ef8da2fc5..29399dff7 100644 --- a/e2e/pages/shared-lending-platform/UserProfile.spec.ts +++ b/e2e/pages/shared-lending-platform/UserProfile.spec.ts @@ -1,6 +1,13 @@ import { expect } from '@playwright/test'; import { test } from '../../fixtures/testFixture'; +import { expectLinkOpensSameTab } from '../../utils/openLink'; import { checkSnapshot } from '../../utils/snapshotTesting'; +import { + expectAll, + expectLogoutButtonVisible, + selectAllNavLinks, + selectCrumbtrailLink, +} from '../../utils/verifyLinkTargets'; test('User Profile Page', async ({ page, navigateToFilingHome }) => { // Go to Profile page @@ -56,4 +63,30 @@ test('User Profile Page', async ({ page, navigateToFilingHome }) => { .click(); await expect(page.locator('h1')).toContainText('View your user profile'); }); + + await test.step('Verify link targets', async () => { + await test.step('Nav links: same tab', async () => { + const navlinks = await selectAllNavLinks(page); + expect(navlinks.length).toEqual(3); + await expectAll(navlinks, expectLinkOpensSameTab); + + await expectLogoutButtonVisible(page); + }); + + await test.step('Crumbtrail: same tab', async () => { + await expectLinkOpensSameTab(selectCrumbtrailLink(page, 'Home')); + }); + + await test.step('Body links: same tab', async () => { + const visitLoginGovAccount = page.getByRole('link', { + name: 'visit your Login.gov account page', + }); + await expectLinkOpensSameTab(visitLoginGovAccount); + + const visitInstitutionProfile = page + .locator('.associated-institutions .m-list_item') + .getByRole('link'); + await expectLinkOpensSameTab(visitInstitutionProfile); + }); + }); }); diff --git a/e2e/pages/shared-lending-platform/unauthenticated-homepage/VerifyLinkTargets.spec.ts b/e2e/pages/shared-lending-platform/unauthenticated-homepage/VerifyLinkTargets.spec.ts new file mode 100644 index 000000000..653ac93bb --- /dev/null +++ b/e2e/pages/shared-lending-platform/unauthenticated-homepage/VerifyLinkTargets.spec.ts @@ -0,0 +1,71 @@ +import { test } from '@playwright/test'; +import { expectLinkOpensSameTab } from '../../../utils/openLink'; +import { + SelectorLinkText, + expectAll, + selectLink, +} from '../../../utils/verifyLinkTargets'; + +test('Unauthenticated homepage: Verify links open in expected target', async ({ + page, +}) => { + await page.goto('/'); + + await test.step('Alert: same tab', async () => { + const emailSupport = await page.getByRole('link', { + name: 'email our support staff', + exact: true, + }); + await expectLinkOpensSameTab(emailSupport); + }); + + await test.step('Login.gov: same tab', async () => { + const loginGov = await page.getByRole('link', { name: 'Login.gov ' }); + await expectLinkOpensSameTab(loginGov); + + const createLoginGov = await page.getByRole('button', { + name: 'Create an account with Login.gov', + }); + await expectLinkOpensSameTab(createLoginGov); + }); + + await test.step('GLEIF: same tab', async () => { + const gleif = selectLink(page, SelectorLinkText.gleif.long); + await expectLinkOpensSameTab(gleif); + }); + + await test.step('Get help: same tab', async () => { + const getHelp = await page.getByRole('link', { + name: 'Find answers to frequently asked questions', + }); + await expectLinkOpensSameTab(getHelp); + const emailSupport = await page.getByRole('link', { + name: 'Email our support staff', + exact: true, + }); + await expectLinkOpensSameTab(emailSupport); + }); + + await test.step('Sidebar: same tab', async () => { + const finalRule = selectLink(page, SelectorLinkText.sidebar.finalRule); + const filingInstructionGuide = selectLink( + page, + SelectorLinkText.sidebar.collection, + ); + const collectionReportingReqs = selectLink( + page, + SelectorLinkText.sidebar.collection, + ); + const privacyNotice = selectLink(page, SelectorLinkText.privacyNotice); + + await expectAll( + [ + finalRule, + filingInstructionGuide, + collectionReportingReqs, + privacyNotice, + ], + expectLinkOpensSameTab, + ); + }); +}); diff --git a/e2e/pages/shared-lending-platform/unauthenticated-homepage/paperworkReductionAct.spec.ts b/e2e/pages/shared-lending-platform/unauthenticated-homepage/paperworkReductionAct.spec.ts index 74a8604f7..a6b85f818 100644 --- a/e2e/pages/shared-lending-platform/unauthenticated-homepage/paperworkReductionAct.spec.ts +++ b/e2e/pages/shared-lending-platform/unauthenticated-homepage/paperworkReductionAct.spec.ts @@ -1,23 +1,24 @@ import { expect, test } from '@playwright/test'; -import { checkSnapshot } from '../../../utils/snapshotTesting'; +import { expectLinkOpensSameTab } from '../../../utils/openLink'; import { expectedPaperworkReductionActUrl } from '../../../utils/testFixture.utils'; +import { SelectorLinkText, selectLink } from '../../../utils/verifyLinkTargets'; -test('Unauthenticated homepage: Paperwork Reduction Act', async ({ page }) => { +test('Verify link - Unauthenticated homepage: Paperwork Reduction Act', async ({ + page, +}) => { await test.step('Verify on the Paperwork Reduction Act and link exists', async () => { await page.goto('/'); await expect(page.locator('#sidebar')).toContainText( 'Paperwork Reduction Act', ); - await expect( - page.getByRole('link', { name: 'View Paperwork Reduction Act' }), - ).toBeVisible(); - await checkSnapshot(page); + + await expectLinkOpensSameTab( + selectLink(page, SelectorLinkText.paperworkReductionAct), + ); }); await test.step('Navigates to the Paperwork Reduction Act summary', async () => { - await page - .getByRole('link', { name: 'View Paperwork Reduction Act' }) - .click(); + await selectLink(page, SelectorLinkText.paperworkReductionAct).click(); await expect(page).toHaveURL(expectedPaperworkReductionActUrl); await expect(page.getByText('/Home')).toBeVisible(); await expect(page.getByRole('heading')).toContainText( diff --git a/e2e/pages/shared-lending-platform/unauthenticated-homepage/sampleTestCfpbLogoGoesToConsumerFinance.spec.ts b/e2e/pages/shared-lending-platform/unauthenticated-homepage/sampleTestCfpbLogoGoesToConsumerFinance.spec.ts index b71f627bb..f8f134b5e 100644 --- a/e2e/pages/shared-lending-platform/unauthenticated-homepage/sampleTestCfpbLogoGoesToConsumerFinance.spec.ts +++ b/e2e/pages/shared-lending-platform/unauthenticated-homepage/sampleTestCfpbLogoGoesToConsumerFinance.spec.ts @@ -1,12 +1,12 @@ import { expect } from '@playwright/test'; import { test } from '../../../fixtures/testFixture'; -import { clickLinkWithRetry } from '../../../utils/clickExternalLinkWithRetry'; +import { openLinkSameTab } from '../../../utils/openLink'; // this is just an example test (e2e tests should be way longer than this) test('Unauthenticated homepage: CFPB logo link goes to consumerfinance.gov', async ({ page, }) => { await page.goto('/'); - await clickLinkWithRetry({ page, target: page.getByLabel('Home') }); + await openLinkSameTab({ page, target: page.getByLabel('Home') }); await expect(page).toHaveURL('https://www.consumerfinance.gov/'); }); diff --git a/e2e/utils/clickExternalLinkWithRetry.ts b/e2e/utils/clickExternalLinkWithRetry.ts deleted file mode 100644 index b8b08f817..000000000 --- a/e2e/utils/clickExternalLinkWithRetry.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { Locator, Page } from '@playwright/test'; - -/** - * Helps address intermittent failures when loading external links - * by attempting to click the link multiple times, hoping one of - * the retries succeeds. - * @param page Page object to allow navigation operations - * @param target Target that should be clicked - */ -export const clickLinkWithRetry = async ({ - page, - target, - maxRetries = 2, -}: { - page: Page; - target: Locator; - maxRetries?: number; -}) => { - const URL_LOAD_FAILED = 'chrome-error://chromewebdata/'; - let counter = 0; - - await target.click(); - - while (page.url() === URL_LOAD_FAILED && counter < maxRetries) { - counter += 1; - // eslint-disable-next-line no-await-in-loop - await page.goBack(); - // eslint-disable-next-line no-await-in-loop - await target.click(); - } -}; diff --git a/e2e/utils/openLink.ts b/e2e/utils/openLink.ts new file mode 100644 index 000000000..08aeb9fb0 --- /dev/null +++ b/e2e/utils/openLink.ts @@ -0,0 +1,85 @@ +import type { Locator, Page } from '@playwright/test'; +import { expect } from '@playwright/test'; + +const URL_LOAD_FAILED = 'chrome-error://chromewebdata/'; + +interface ClickLinkParameters { + page: Page; + target: Locator; + maxRetries?: number; +} + +// Confirms link opens in a new tab +export const expectLinkOpensNewTab = async (link: Locator) => { + await expect(link).toBeVisible(); + const target = await link.getAttribute('target'); + expect(target).toBe('_blank'); +}; + +// Confirms link opens in the same tab +export const expectLinkOpensSameTab = async (link: Locator) => { + await expect(link).toBeVisible(); + const target = await link.getAttribute('target'); + expect(target).toBe(null); +}; + +/** + * Helps address intermittent failures when loading external links + * by attempting to click the link multiple times, hoping one of + * the retries succeeds. + * @param page Page object to allow navigation operations + * @param target Target that should be clicked + */ +export const openLinkSameTab = async ({ + page, + target, + maxRetries = 2, +}: ClickLinkParameters) => { + let counter = 0; + + await expectLinkOpensSameTab(target); + + await target.click(); + + while (page.url() === URL_LOAD_FAILED && counter < maxRetries) { + counter += 1; + // eslint-disable-next-line no-await-in-loop + await page.goBack(); + // eslint-disable-next-line no-await-in-loop + await target.click(); + } +}; + +/** + * Opens a link in a new tab, allowing multiple retries if the link fails to load + * @param page Page object to allow navigation operations + * @param target Target that should be clicked + */ +export const openLinkNewTab = async ({ + page, + target, + maxRetries = 2, +}: ClickLinkParameters) => { + let counter = 0; + + await expectLinkOpensNewTab(target); + + while (counter < maxRetries) { + const newTabPromise = page.waitForEvent('popup'); + // eslint-disable-next-line no-await-in-loop + await target.click(); + // eslint-disable-next-line no-await-in-loop + const newTab = await newTabPromise; + + // Error, close tab and try again + if (newTab.url() === URL_LOAD_FAILED) { + // eslint-disable-next-line no-await-in-loop + await newTab.close(); + counter += 1; + } + // Link successfully opened in new tab + else return newTab; + } + + return page; +}; diff --git a/e2e/utils/verifyLinkTargets.ts b/e2e/utils/verifyLinkTargets.ts new file mode 100644 index 000000000..78dcd60c8 --- /dev/null +++ b/e2e/utils/verifyLinkTargets.ts @@ -0,0 +1,79 @@ +import type { Locator, Page } from '@playwright/test'; +import { expect } from '@playwright/test'; + +// Run a test function on each element of an array of locators +// i.e. expectAll([fig, gleif], expectLinkOpensNewTab) +export const expectAll = async ( + locators: Locator[], + testFunction: (target: Locator) => Promise, +) => { + for (const locator of locators) { + // eslint-disable-next-line no-await-in-loop + await testFunction(locator); + } +}; + +export const selectLink = (container: Locator | Page, name: string) => + container.getByRole('link', { name }); + +// Helper to create Locators for multiple links +// i.e. selectLinks(page, ['Home', {name: 'GLEIF', exact: true }]) +export const selectLinks = (container: Locator | Page, options: string[]) => + options.map(value => { + // Entry is a string to search by + if (typeof value === 'string') + return container.getByRole('link', { name: value }); + + // Entry is an options object i.e. { name: 'Home', exact: true } + return container.getByRole('link', value); + }); + +export const selectCrumbtrailLink = ( + container: Locator | Page, + name: string, +) => { + const crumbtrail = container.locator('#crumbtrail'); + return crumbtrail.getByRole('link', { name }); +}; + +export const selectAllNavLinks = async (page: Page) => { + return page.locator('#nav-links a').all(); +}; + +// Frequent text of page links +export const SelectorLinkText = { + gleif: { + long: 'Global LEI Foundation (GLEIF)', + short: 'GLEIF', + }, + fig: { + long: 'filing instructions guide for small business lending data', + readAboutFiling: 'Read about the filing process', + readAboutValidations: 'Read about data validations', + section4: 'section 4, "Data validation"', + }, + crumbtrail: { + home: 'Home', + }, + upload: { + aNewFile: 'Upload a new file', + }, + navbar: { + logout: 'LOG OUT', + }, + sidebar: { + fig: 'Filing instructions guide', + collection: 'Collection and reporting requirements', + finalRule: 'Final rule', + }, + privacyNotice: 'View Privacy Notice', + paperworkReductionAct: 'View Paperwork Reduction Act', +}; + +export const expectLogoutButtonVisible = async (page: Page) => { + const navbar = page.locator('#nav-links'); + const logout = navbar.getByRole('button', { + name: SelectorLinkText.navbar.logout, + }); + await expect(logout).toBeVisible(); +}; diff --git a/src/components/CrumbTrail.tsx b/src/components/CrumbTrail.tsx index b70b70247..31cdc54ca 100644 --- a/src/components/CrumbTrail.tsx +++ b/src/components/CrumbTrail.tsx @@ -9,7 +9,8 @@ const separatorKeys = Array.from({ length }).map((_, index) => `key-${index}`); const INCREMENT_BY_ONE = 1; function CrumbTrail({ - className, + id = 'crumbtrail', + className = '', children, }: JSX.IntrinsicElements['div']): JSX.Element | null { let items: ReactNode[] = []; @@ -46,6 +47,7 @@ function CrumbTrail({ return (
{items} diff --git a/src/components/Link.tsx b/src/components/Link.tsx index 4ee9fbf69..b8981b66a 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -10,7 +10,7 @@ import { import { IconExternalLink, isExternalLinkImplied, - isNewTabImplied, + useIsNewTabImplied, } from './Link.utils'; interface LinkProperties extends DesignSystemReactLinkProperties { @@ -38,7 +38,8 @@ export function Link({ let icon; // Open link in new tab - const openInNewTab = isNewTab ?? isNewTabImplied(href); + const isNewTabImplied = useIsNewTabImplied(href); + const openInNewTab = isNewTab ?? isNewTabImplied; if (openInNewTab) otherProperties.target = '_blank'; // Treat as an External link diff --git a/src/components/Link.utils.tsx b/src/components/Link.utils.tsx index 7b368fbf9..adda77d6a 100644 --- a/src/components/Link.utils.tsx +++ b/src/components/Link.utils.tsx @@ -1,26 +1,28 @@ +import useSblAuth from 'api/useSblAuth'; import { Icon } from 'design-system-react'; import type { ReactElement } from 'react'; +import { useLocation } from 'react-router-dom'; -// Link to specific regulation -// Ex: /rules-policy/regulations/1002/109/#a-1-ii -const regsLinkPattern = /\/rules-policy\/regulations\/\d+\/\d+\/#.+/; - -// Link to specific FIG subsection -// Ex: /small-business-lending/filing-instructions-guide/2024-guide/#4.4.1 -const figLinkPattern = /\/filing-instructions-guide\/\d{4}-guide\/#.+/; - -/** - * Programmatically determine if a link is external to the CFPB sphere of websites - */ -export const isExternalLinkImplied = (targetUrl: string): boolean => { +// Parse URL string into URL object +const parseURL = (url: string): URL | false => { let parsed; try { - parsed = new URL(targetUrl); + parsed = new URL(url); } catch { return false; // Relative targets will fail parsing (ex. '/home') } + return parsed; +}; + +/** + * Programmatically determine if a link is external to the CFPB sphere of websites + */ +export const isExternalLinkImplied = (targetUrl: string): boolean => { + const parsed = parseURL(targetUrl); + if (!parsed) return false; // Invalid URL or Relative URL (ex. '/home') + const externalProtocols = ['http', 'tel:', 'sms:', 'ftp:']; if (externalProtocols.includes(parsed.protocol)) return true; @@ -46,7 +48,59 @@ export function IconExternalLink(): ReactElement { ); } -export function isNewTabImplied(href: string | undefined): boolean { +// Determine if the the target href should open in a new tab. +// This logic has been centralized and codified to avoid +// bespoke logic per link. +// +// Reference: https://github.com/cfpb/sbl-project/issues/295 + +export function useIsNewTabImplied(href: string | undefined): boolean { + const { ...auth } = useSblAuth(); + const { pathname } = useLocation(); + + // No link to open if (!href) return false; - return regsLinkPattern.test(href) || figLinkPattern.test(href); + + // User is NOT logged-in to Login.gov / Keycloak + if (!auth.isAuthenticated) return false; + + // User is on a "Complete user profile" page, which qualifies as "Not logged-in" + // (Once associated with an LEI, users are always redirected away from these pages) + if (pathname.includes('/profile/complete')) return false; + + // Login.gov account page opens in same tab + if (href === 'https://secure.login.gov/account/') return false; + + // Open a new tab when the user is logged-in but visiting an unauthenticated page + const isCollectionAndReportingRequirements = href.endsWith( + '/compliance/compliance-resources/', + ); + const isFinalRule = href.endsWith('/rules-policy/final-rules/'); + const isPaperworkReductionAct = href.endsWith( + '/paperwork-reduction-act-notice', + ); + const isUnauthenticatedHomepage = href.toString() === '/'; + const isViewPrivacyNotice = href.endsWith('/privacy-notice'); + + if ( + isViewPrivacyNotice || + isPaperworkReductionAct || + isFinalRule || + isCollectionAndReportingRequirements || + isUnauthenticatedHomepage + ) + return true; + + // Open in the same tab if the target is within our app + const parsed = parseURL(href); + if (!parsed) return false; + + const isTargetWithinOurAppDomain = new RegExp( + `([\\S]*\\.)?(${window.location.host})`, + ).test(parsed.host); + + if (isTargetWithinOurAppDomain) return false; + + // All other links will open in a new tab + return true; } diff --git a/src/components/__tests__/AdditionalResources.test.tsx b/src/components/__tests__/AdditionalResources.test.tsx index b0d41932b..c323fd490 100644 --- a/src/components/__tests__/AdditionalResources.test.tsx +++ b/src/components/__tests__/AdditionalResources.test.tsx @@ -1,17 +1,20 @@ import { render, screen } from '@testing-library/react'; import AdditionalResources from 'components/AdditionalResources'; import { ListLink } from 'components/Link'; +import { AuthProvider } from 'react-oidc-context'; import { MemoryRouter } from 'react-router-dom'; describe('', () => { it('Renders expected content', async () => { render( - - - First link - Second link - - , + + + + First link + Second link + + + , ); // Heading diff --git a/src/pages/Filing/FilingApp/InstitutionCard.helpers.ts b/src/pages/Filing/FilingApp/InstitutionCard.helpers.ts index dae4908ce..029f6334b 100644 --- a/src/pages/Filing/FilingApp/InstitutionCard.helpers.ts +++ b/src/pages/Filing/FilingApp/InstitutionCard.helpers.ts @@ -17,6 +17,15 @@ export const UI_STEPS = [ SIGN_SUBMIT, ]; +export const InstitutionCardTitle = { + start: 'Start the filing process', + upload: 'Upload your lending data', + errors: 'Resolve errors in your lending data', + warnings: 'Review warnings in your lending data', + provideDetails: 'Provide filing details', + signSubmit: 'Sign and submit your filing', +}; + type StatusProperties = SecondaryButtonType & StatusCardType; // Derive the content to be displayed for a Filing in the given `status` @@ -52,7 +61,7 @@ export function deriveCardContent({ // 2) (START_A_FILING) indicate to new users that they have not previously started the Filing process for this institution case FilingStatusAsString.TYPES_OF_INSTITUTION: case FilingStatusAsString.START_A_FILING: { - title = 'Start the filing process'; + title = InstitutionCardTitle.start; description = 'You will be asked to select all applicable types of financial institutions if you have not provided this previously. Otherwise, you will be asked to select a file to upload.'; @@ -70,7 +79,7 @@ export function deriveCardContent({ // Latest submission is started, and possibly has a file uploaded, but has not been validated case FilingStatusAsString.SUBMISSION_STARTED: { - title = 'Upload your lending data'; + title = InstitutionCardTitle.upload; description = 'You will be asked to select a file to upload. We will perform validation checks on your register to ensure that data entries do not contain errors and are ready to submit.'; @@ -83,7 +92,7 @@ export function deriveCardContent({ // Latest submission has errors that prevent further progress in the Filing process case FilingStatusAsString.VALIDATION_WITH_ERRORS: { - title = 'Resolve errors in your lending data'; + title = InstitutionCardTitle.errors; description = 'Your file was successfully uploaded but validation checks returned errors. If applicable, make corrections to your register and upload a new file.'; @@ -96,7 +105,7 @@ export function deriveCardContent({ // Latest submission has warnings that are not yet accepted case FilingStatusAsString.VALIDATION_WITH_WARNINGS: { - title = 'Review warnings in your lending data'; + title = InstitutionCardTitle.warnings; description = 'Your file successfully passed all error validation checks. If applicable, correct or verify the accuracy of register values flagged by warning validations.'; @@ -109,7 +118,7 @@ export function deriveCardContent({ // Latest submission has no point of contact info populated case FILING_DETAILS: { - title = 'Provide filing details'; + title = InstitutionCardTitle.provideDetails; description = 'You have completed the validation steps. Next, provide the contact information of a person that the Bureau or other regulators may contact with questions about your filing.'; @@ -121,7 +130,7 @@ export function deriveCardContent({ // Latest submission is ready to sign and submit case SIGN_SUBMIT: { - title = 'Sign and submit your filing'; + title = InstitutionCardTitle.signSubmit; description = `You have completed the steps required to prepare your filing. Before you submit your filing, review and certify the accuracy and completeness of the data reported.`; mainButtonLabel = 'Continue filing';