Repository: dhsc-govuk/FingertipsNext
Pull request #572: DHSCFT-670 refactor isolated UI tests
Author: login: williamwillis11
Created: 2025-05-13T08:36:56Z | Merged: 2025-05-14T13:46:33Z

# Description

Jira ticket: [DHSCFT-670](https://bjss-enterprise.atlassian.net/browse/DHSCFT-670)

refactor frontend/fingertips-frontend/playwright/tests/isolated_ui_tests/navigation_and_validation.spec.ts for better readability/maintainability 

## Changes

- see above

## Validation


all tests passing


Diff:
diff --git a/frontend/fingertips-frontend/playwright/page-objects/components/areaFilter.ts b/frontend/fingertips-frontend/playwright/page-objects/components/areaFilter.ts
index 29a460777..55deb4500 100644
--- a/frontend/fingertips-frontend/playwright/page-objects/components/areaFilter.ts
+++ b/frontend/fingertips-frontend/playwright/page-objects/components/areaFilter.ts
@@ -120,7 +120,7 @@ export default class AreaFilter extends BasePage {
     expect(String(groupOptions)).toContain(contain);
   }
 
-  async selectAreaType(areaType: string) {
+  async selectAreaTypeAndAssertURLUpdated(areaType: string) {
     await this.selectOptionAndAwaitLoadingComplete(
       this.page.getByTestId(this.areaTypeSelector),
       areaType
@@ -129,7 +129,7 @@ export default class AreaFilter extends BasePage {
     await this.waitForURLToContain(areaType);
   }
 
-  async selectGroupType(groupType: string) {
+  async selectGroupTypeAndAssertURLUpdated(groupType: string) {
     await this.selectOptionAndAwaitLoadingComplete(
       this.page.getByTestId(this.groupTypeSelector),
       groupType
@@ -138,7 +138,7 @@ export default class AreaFilter extends BasePage {
     await this.waitForURLToContain(groupType);
   }
 
-  async selectGroup(group: string) {
+  async selectGroupAndAssertURLUpdated(group: string) {
     await this.selectOptionAndAwaitLoadingComplete(
       this.page.getByTestId(this.groupSelector),
       group
@@ -147,7 +147,7 @@ export default class AreaFilter extends BasePage {
     await this.waitForURLToContain(SearchParams.GroupSelected);
   }
 
-  async selectArea(areaName: string, areaCode: string) {
+  async selectAreaAndAssertURLUpdated(areaName: string, areaCode: string) {
     await this.checkAndAwaitLoadingComplete(
       this.page.getByRole('checkbox', { name: areaName })
     );
@@ -171,7 +171,7 @@ export default class AreaFilter extends BasePage {
     if (searchMode === SearchMode.ONLY_SUBJECT) {
       await this.waitForURLToContain(searchTerm);
 
-      await this.selectAreaType(areaTypeFilter);
+      await this.selectAreaTypeAndAssertURLUpdated(areaTypeFilter);
 
       // Select appropriate number of checkboxes based on area mode
       const areaCheckboxList = this.page
@@ -198,13 +198,13 @@ export default class AreaFilter extends BasePage {
 
       // England area mode
       if (AreaMode.ENGLAND_AREA === areaMode) {
-        await this.selectAreaType('england');
+        await this.selectAreaTypeAndAssertURLUpdated('england');
         await this.waitForURLToContain(SearchParams.AreaTypeSelected);
 
-        await this.selectGroupType('england');
+        await this.selectGroupTypeAndAssertURLUpdated('england');
         await this.waitForURLToContain(SearchParams.GroupTypeSelected);
 
-        await this.selectArea('england', 'england');
+        await this.selectAreaAndAssertURLUpdated('england', 'england');
         await this.waitForURLToContain(SearchParams.AreasSelected);
 
         await this.waitForURLToContain('england');
diff --git a/frontend/fingertips-frontend/playwright/page-objects/pages/resultsPage.ts b/frontend/fingertips-frontend/playwright/page-objects/pages/resultsPage.ts
index 45ed42854..d5cf1dd9c 100644
--- a/frontend/fingertips-frontend/playwright/page-objects/pages/resultsPage.ts
+++ b/frontend/fingertips-frontend/playwright/page-objects/pages/resultsPage.ts
@@ -174,7 +174,9 @@ export default class ResultsPage extends AreaFilter {
    * Selecting the passed in indicators checkboxes
    * @param expectedIndicatorsToSelect - a list of all indicators to be selected
    */
-  async selectIndicatorCheckboxes(expectedIndicatorsToSelect: IndicatorInfo[]) {
+  async selectIndicatorCheckboxesAndVerifyURLUpdated(
+    expectedIndicatorsToSelect: IndicatorInfo[]
+  ) {
     for (const indicatorID of expectedIndicatorsToSelect) {
       const indicatorIDString = String(indicatorID.indicatorID);
       const checkbox = this.page.getByTestId(
diff --git a/frontend/fingertips-frontend/playwright/tests/fully_integrated_e2e_tests/core_journeys.spec.ts b/frontend/fingertips-frontend/playwright/tests/fully_integrated_e2e_tests/core_journeys.spec.ts
index 8af626cf9..5f220d0c5 100644
--- a/frontend/fingertips-frontend/playwright/tests/fully_integrated_e2e_tests/core_journeys.spec.ts
+++ b/frontend/fingertips-frontend/playwright/tests/fully_integrated_e2e_tests/core_journeys.spec.ts
@@ -98,7 +98,9 @@ test.describe(
               searchMode
             );
 
-            await resultsPage.selectIndicatorCheckboxes(indicatorsToSelect);
+            await resultsPage.selectIndicatorCheckboxesAndVerifyURLUpdated(
+              indicatorsToSelect
+            );
 
             await resultsPage.checkRecentTrends(
               areaMode,
diff --git a/frontend/fingertips-frontend/playwright/tests/isolated_ui_tests/navigation_and_validation.spec.ts b/frontend/fingertips-frontend/playwright/tests/isolated_ui_tests/navigation_and_validation.spec.ts
index 5459a7942..e30c0ba0c 100644
--- a/frontend/fingertips-frontend/playwright/tests/isolated_ui_tests/navigation_and_validation.spec.ts
+++ b/frontend/fingertips-frontend/playwright/tests/isolated_ui_tests/navigation_and_validation.spec.ts
@@ -15,13 +15,18 @@ import { AreaDocument, RawIndicatorDocument } from '@/lib/search/searchTypes';
 import ChartPage from '@/playwright/page-objects/pages/chartPage';
 import { areaCodeForEngland } from '@/lib/chartHelpers/constants';
 
-// tests in this file use mock service worker to mock the API response
-// so that the tests can be run without the need for a backend
-// see frontend/fingertips-frontend/assets/mockIndicatorData.json
-// and frontend/fingertips-frontend/assets/mockAreaData.json
-//@ts-expect-error don't care about type checking this json file
+/**
+ * Note that this test suite uses mock service worker to mock API responses, therefore these playwright tests are isolated from the backend
+ * See:
+ * - frontend/fingertips-frontend/assets/mockIndicatorData.json
+ * - frontend/fingertips-frontend/assets/mockAreaData.json
+ */
+
+// Test data setup
+// @ts-expect-error mock data type casting
 const indicatorData = mockIndicators as RawIndicatorDocument[];
 const subjectSearchTerm = 'hospital';
+const secondSubjectSearchTerm = 'diabetes';
 const indicatorMode = IndicatorMode.ONE_INDICATOR;
 const searchMode = SearchMode.ONLY_SUBJECT;
 let allValidIndicatorIDs: string[];
@@ -29,151 +34,174 @@ let validIndicatorIDs: IndicatorInfo[];
 let allNHSRegionAreas: AreaDocument[];
 let typedIndicatorData: RawIndicatorDocument[];
 
-test.beforeAll(
-  `get indicatorIDs from the mock data source for searchTerm: ${subjectSearchTerm} and get mock area data`,
-  () => {
-    typedIndicatorData = indicatorData.map(
-      (indicator: RawIndicatorDocument) => {
-        return {
-          ...indicator,
-          lastUpdated: new Date(indicator.lastUpdatedDate),
-        };
-      }
-    );
+test.beforeAll('Initialize test data from mock sources', () => {
+  typedIndicatorData = indicatorData.map((indicator: RawIndicatorDocument) => ({
+    ...indicator,
+    lastUpdated: new Date(indicator.lastUpdatedDate),
+  }));
+
+  // Get all valid indicators based on subjectSearchTerm search term
+  allValidIndicatorIDs = getAllIndicatorIDsForSearchTerm(
+    typedIndicatorData,
+    subjectSearchTerm
+  );
+
+  // Filter down valid indicators based on mode
+  validIndicatorIDs = returnIndicatorIDsByIndicatorMode(
+    allValidIndicatorIDs,
+    indicatorMode
+  );
+
+  // Get all NHS region areas
+  allNHSRegionAreas = getAllAreasByAreaType(mockAreas, 'nhs-regions');
+});
 
-    allValidIndicatorIDs = getAllIndicatorIDsForSearchTerm(
-      typedIndicatorData,
-      subjectSearchTerm
-    );
+test.describe('Home Page Tests', () => {
+  test('should navigate to home page and validate accessibility', async ({
+    homePage,
+    axeBuilder,
+  }) => {
+    await homePage.navigateToHomePage();
+    await homePage.checkOnHomePage();
+    await homePage.expectNoAccessibilityViolations(axeBuilder);
+  });
+
+  test('should validate search field form validations', async ({
+    homePage,
+  }) => {
+    await test.step('Navigate to home page', async () => {
+      await homePage.navigateToHomePage();
+      await homePage.checkOnHomePage();
+      await homePage.checkSearchFieldIsPrePopulatedWith(); // nothing - as nothing should be prepopulated when first navigating to the home page
+    });
 
-    validIndicatorIDs = returnIndicatorIDsByIndicatorMode(
-      allValidIndicatorIDs,
-      indicatorMode
-    );
+    await test.step('Try to search with empty field', async () => {
+      await homePage.clickSearchButton();
+    });
 
-    allNHSRegionAreas = getAllAreasByAreaType(mockAreas, 'nhs-regions');
-  }
-);
-test.describe(`Navigation, accessibility and validation tests`, () => {
-  test('client validation testing and navigation behaviour', async ({
+    await test.step('Verify validation messages', async () => {
+      await homePage.checkSummaryValidation(
+        `There is a problemEnter a subject you want to search forEnter an area you want to search for`
+      );
+    });
+  });
+});
+
+test.describe('Results Page Tests', () => {
+  test('should search for indicators and display results', async ({
     homePage,
     resultsPage,
-    chartPage,
-    indicatorPage,
     axeBuilder,
   }) => {
-    test.setTimeout(90000); // TODO - split this test up and remove this extension
-
-    await test.step('Navigate to search page', async () => {
+    await test.step('Navigate to home page', async () => {
       await homePage.navigateToHomePage();
       await homePage.checkOnHomePage();
-      await chartPage.expectNoAccessibilityViolations(axeBuilder);
     });
 
-    await test.step(`Search for indicators using search term ${subjectSearchTerm} and check results title contains the search term`, async () => {
+    await test.step('Search by subject for indicators', async () => {
       await homePage.searchForIndicators(searchMode, subjectSearchTerm);
       await homePage.clickSearchButton();
+    });
 
+    await test.step('Verify results page', async () => {
       await resultsPage.waitForURLToContain(subjectSearchTerm);
       await resultsPage.checkSearchResultsTitle(subjectSearchTerm);
-      await chartPage.expectNoAccessibilityViolations(axeBuilder);
+      await resultsPage.expectNoAccessibilityViolations(axeBuilder);
+    });
+  });
+
+  test('should validate indicator search on results page', async ({
+    resultsPage,
+    axeBuilder,
+  }) => {
+    await test.step('Navigate directly to results page', async () => {
+      await resultsPage.navigateToResults(subjectSearchTerm, []);
     });
 
-    await test.step('Validate indicator search validation on results page', async () => {
+    await test.step('Clear indicator search and verify validation messages', async () => {
       await resultsPage.clearIndicatorSearchBox();
       await resultsPage.clickIndicatorSearchButton();
       await resultsPage.checkForIndicatorSearchError();
-      await chartPage.expectNoAccessibilityViolations(axeBuilder);
+      await resultsPage.expectNoAccessibilityViolations(axeBuilder);
+    });
 
-      await resultsPage.fillIndicatorSearch(subjectSearchTerm);
+    await test.step('Enter a different valid search and verify results page', async () => {
+      await resultsPage.fillIndicatorSearch(secondSubjectSearchTerm);
       await resultsPage.clickIndicatorSearchButton();
-      await resultsPage.checkSearchResultsTitle(subjectSearchTerm);
+      await resultsPage.checkSearchResultsTitle(secondSubjectSearchTerm);
     });
+  });
 
-    await test.step('Select single indicator and let default to England, and check on charts page', async () => {
-      await resultsPage.selectIndicatorCheckboxes(validIndicatorIDs);
-
-      await resultsPage.clickViewChartsButton();
-
-      await chartPage.waitForURLToContain('chart');
+  test('should handle "select all" indicators functionality correctly', async ({
+    resultsPage,
+  }) => {
+    await test.step('Navigate directly to results page', async () => {
+      await resultsPage.navigateToResults(subjectSearchTerm, []);
+      await resultsPage.checkSearchResultsTitleBasedOnSearchMode(
+        searchMode,
+        subjectSearchTerm
+      );
+    });
 
-      await chartPage.expectNoAccessibilityViolations(axeBuilder, [
-        'color-contrast',
-      ]);
+    await test.step('Verify select all checkbox', async () => {
+      await resultsPage.selectIndicatorSelectAllCheckbox();
+      await resultsPage.verifyAllIndicatorsSelected();
+      await resultsPage.verifyUrlContainsAllIndicators(allValidIndicatorIDs);
     });
 
-    await test.step('Select "View background information" link, verify indicator page title and Return to charts page', async () => {
-      const indicator = typedIndicatorData.find(
-        (ind) => ind.indicatorID === validIndicatorIDs[0].indicatorID
-      );
+    await test.step('Verify deselect all checkbox', async () => {
+      await resultsPage.deselectIndicatorSelectAllCheckbox();
+      await resultsPage.verifyNoIndicatorsSelected();
+      await resultsPage.verifyUrlExcludesAllIndicators();
+    });
 
-      if (!indicator) {
-        throw new Error(`Indicator with ID ${validIndicatorIDs[0]} not found`);
-      }
+    await test.step('Verify individual selections to select all', async () => {
+      await resultsPage.selectEveryIndicator(allValidIndicatorIDs);
+      await resultsPage.verifySelectAllCheckboxTicked();
+      await resultsPage.verifyUrlContainsAllIndicators(allValidIndicatorIDs);
+    });
 
-      await resultsPage.clickViewBackgroundInformationLinkForIndicator(
-        indicator
+    await test.step('Verify deselecting one indicator', async () => {
+      await resultsPage.deselectIndicator(allValidIndicatorIDs[0]);
+      await resultsPage.verifySelectAllCheckboxUnticked();
+      await resultsPage.verifyUrlUpdatedAfterDeselection(
+        allValidIndicatorIDs[0]
       );
-
-      await indicatorPage.waitForURLToContain('indicator');
-
-      await indicatorPage.checkIndicatorNameTitle(indicator);
-
-      await indicatorPage.expectNoAccessibilityViolations(axeBuilder);
-
-      await indicatorPage.clickBackLink();
     });
+  });
 
-    await test.step('Select area filters on charts page', async () => {
-      await chartPage.selectAreasFiltersIfRequired(
-        searchMode,
-        AreaMode.THREE_PLUS_AREAS, // change to 3 areas to see different view with barChartEmbeddedTable-component
+  test('should clear indicators from URL when search changes', async ({
+    resultsPage,
+  }) => {
+    await test.step('Navigate to results page with areas', async () => {
+      await resultsPage.navigateToResults(
         subjectSearchTerm,
-        'gps'
-      );
-
-      await chartPage.checkSpecificChartComponent(
-        ChartPage.barChartEmbeddedTableComponent
+        [
+          allNHSRegionAreas[0].areaCode,
+          allNHSRegionAreas[1].areaCode,
+          allNHSRegionAreas[2].areaCode,
+        ],
+        'nhs-regions'
       );
-
-      await chartPage.expectNoAccessibilityViolations(axeBuilder, [
-        'color-contrast',
-      ]);
     });
 
-    await test.step('Return to results page and verify selections are preselected', async () => {
-      await chartPage.clickBackLink();
-
-      await resultsPage.checkSearchResultsTitle(subjectSearchTerm);
-      await resultsPage.checkIndicatorCheckboxChecked(
-        validIndicatorIDs[0].indicatorID
+    await test.step('Select indicator and verify URL updated', async () => {
+      await resultsPage.selectIndicatorCheckboxesAndVerifyURLUpdated(
+        validIndicatorIDs
       );
     });
 
-    await test.step('Return to search page and verify fields are correctly prepopulated', async () => {
-      await resultsPage.clickBackLink();
-
-      await homePage.checkSearchFieldIsPrePopulatedWith(subjectSearchTerm);
-    });
-
-    await test.step('Verify after clearing search field that search page validation prevents forward navigation', async () => {
-      await homePage.clearSearchIndicatorField();
-      await homePage.closeAreaFilterPill(0);
-      await homePage.closeAreaFilterPill(0);
-      await homePage.closeAreaFilterPill(0);
-
-      await homePage.clickSearchButton();
-      await homePage.checkSearchFieldIsPrePopulatedWith(); // nothing should be prepopulated after clearing search field
-      await homePage.checkSummaryValidation(
-        `There is a problemEnter a subject you want to search forEnter an area you want to search for`
-      );
+    await test.step('Clear search and verify indicators removed from URL', async () => {
+      await resultsPage.clearIndicatorSearchBox();
+      await resultsPage.clickIndicatorSearchButton();
+      await resultsPage.verifyUrlExcludesAllIndicators();
     });
   });
+});
 
-  test('check area type pills on results page when areas specified in url', async ({
-    resultsPage,
-  }) => {
-    await test.step('Navigate directly to the results page', async () => {
+test.describe('Area Filter Tests', () => {
+  test('should manage area pills on results page', async ({ resultsPage }) => {
+    await test.step('Navigate directly to results with preselected areas', async () => {
       await resultsPage.navigateToResults(
         subjectSearchTerm,
         [
@@ -183,9 +211,14 @@ test.describe(`Navigation, accessibility and validation tests`, () => {
         ],
         'nhs-regions'
       );
+
+      await resultsPage.checkSearchResultsTitleBasedOnSearchMode(
+        searchMode,
+        subjectSearchTerm
+      );
     });
 
-    await test.step('Check selected area pills matches those specified in url', async () => {
+    await test.step('Verify area pills display correctly', async () => {
       const expectedPillTexts = [
         `${allNHSRegionAreas[0].areaName} ${allNHSRegionAreas[0].areaType}`,
         `${allNHSRegionAreas[1].areaName} ${allNHSRegionAreas[1].areaType}`,
@@ -197,35 +230,33 @@ test.describe(`Navigation, accessibility and validation tests`, () => {
         .toHaveCount(expectedPillTexts.length);
 
       const filterPillNames = await resultsPage.areaFilterPillsText();
-
       sortAlphabetically(filterPillNames);
       sortAlphabetically(expectedPillTexts);
       test.expect(filterPillNames).toEqual(expectedPillTexts);
 
+      // Verify all area filter comboboxes are disabled when an area/areas are selected
       await test.expect(resultsPage.areaFilterCombobox()).toBeDisabled();
     });
 
-    await test.step('Click remove one area pill and re-check area pills', async () => {
+    await test.step('Remove one pill and verify remaining pills', async () => {
       await resultsPage.closeAreaFilterPill(1);
 
-      const expectedPillTexts = [
+      const expectedRemainingPills = [
         `${allNHSRegionAreas[0].areaName} ${allNHSRegionAreas[0].areaType}`,
         `${allNHSRegionAreas[2].areaName} ${allNHSRegionAreas[2].areaType}`,
       ];
+
       await test
         .expect(resultsPage.areaFilterPills())
-        .toHaveCount(expectedPillTexts.length);
+        .toHaveCount(expectedRemainingPills.length);
 
-      const filterPillNames = await resultsPage.areaFilterPillsText();
-
-      sortAlphabetically(filterPillNames);
-      sortAlphabetically(expectedPillTexts);
-      test.expect(filterPillNames).toEqual(expectedPillTexts);
-
-      await test.expect(resultsPage.areaFilterCombobox()).toBeDisabled();
+      const remainingPillNames = await resultsPage.areaFilterPillsText();
+      sortAlphabetically(remainingPillNames);
+      sortAlphabetically(expectedRemainingPills);
+      test.expect(remainingPillNames).toEqual(expectedRemainingPills);
     });
 
-    await test.step('Check url has been updated after area pill removal', async () => {
+    await test.step('Verify URL updated after removing area pill', async () => {
       await resultsPage.waitForURLToContain(allNHSRegionAreas[0].areaCode);
       await test
         .expect(resultsPage.page)
@@ -233,7 +264,7 @@ test.describe(`Navigation, accessibility and validation tests`, () => {
       await resultsPage.waitForURLToContain(allNHSRegionAreas[2].areaCode);
     });
 
-    await test.step('Remove all pills and check url and area type combobox', async () => {
+    await test.step('Remove all pills and verify filter state', async () => {
       await resultsPage.closeAreaFilterPill(1);
       await test
         .expect(resultsPage.page)
@@ -243,141 +274,79 @@ test.describe(`Navigation, accessibility and validation tests`, () => {
       await test
         .expect(resultsPage.page)
         .not.toHaveURL(allNHSRegionAreas[0].areaCode);
-      await test.expect(resultsPage.page).not.toHaveURL(/&as=/);
+      await resultsPage.verifyUrlExcludesAllIndicators();
 
+      // Verify area filter combobox is enabled when no areas are selected
       await test.expect(resultsPage.areaFilterCombobox()).toBeEnabled();
     });
   });
 
-  test('check indicators are removed from url when search changes', async ({
+  test('should support filtering results by different area types', async ({
     resultsPage,
   }) => {
-    await test.step('Navigate directly to the results page', async () => {
-      await resultsPage.navigateToResults(
-        subjectSearchTerm,
-        [
-          allNHSRegionAreas[0].areaCode,
-          allNHSRegionAreas[1].areaCode,
-          allNHSRegionAreas[2].areaCode,
-        ],
-        'nhs-regions'
-      );
-    });
-
-    await test.step('Select single indicator, and verify url is updated to include indicator', async () => {
-      await resultsPage.selectIndicatorCheckboxes(validIndicatorIDs);
-
-      await test.expect(resultsPage.page).toHaveURL(/&is=/);
-    });
-
-    await test.step('Clear search bar and search, verify url is updated to remove indicator', async () => {
-      await resultsPage.clearIndicatorSearchBox();
-      await resultsPage.clickIndicatorSearchButton();
-      await test.expect(resultsPage.page).not.toHaveURL(/&is=/);
-    });
-  });
-
-  test('check "select all" checkbox updates selected indicators and URL', async ({
-    resultsPage,
-  }) => {
-    await test.step('Navigate directly to the results page', async () => {
+    await test.step('Navigate to results page', async () => {
       await resultsPage.navigateToResults(subjectSearchTerm, []);
     });
 
-    await test.step('Tick "Select all" checkbox and verify all indicators are selected and URL is updated', async () => {
-      await resultsPage.selectIndicatorSelectAllCheckbox();
-      await resultsPage.verifyAllIndicatorsSelected();
-      await resultsPage.verifyUrlContainsAllIndicators(allValidIndicatorIDs);
-    });
-
-    await test.step('Untick "Select all" checkbox and verify no indicators are selected and URL is updated', async () => {
-      await resultsPage.deselectIndicatorSelectAllCheckbox();
-      await resultsPage.verifyNoIndicatorsSelected();
-      await resultsPage.verifyUrlExcludesAllIndicators();
-    });
-
-    await test.step('Select indicators one by one and verify "Select all" checkbox becomes ticked and URL updates', async () => {
-      await resultsPage.selectEveryIndicator(allValidIndicatorIDs);
-      await resultsPage.verifySelectAllCheckboxTicked();
-      await resultsPage.verifyUrlContainsAllIndicators(allValidIndicatorIDs);
-    });
-
-    await test.step('Deselect one indicator and verify "Select all" checkbox becomes unticked and URL updates', async () => {
-      await resultsPage.deselectIndicator(allValidIndicatorIDs[0]);
-      await resultsPage.verifySelectAllCheckboxUnticked();
-      await resultsPage.verifyUrlUpdatedAfterDeselection(
-        allValidIndicatorIDs[0]
-      );
-    });
-  });
-
-  test('check area filtering on results page', async ({ resultsPage }) => {
-    await test.step('first filter by GPs', async () => {
-      await resultsPage.navigateToResults(subjectSearchTerm, []);
-
+    await test.step('filter by GP area', async () => {
       const gpAreaType = 'gps';
       const groupType = 'nhs-primary-care-networks';
       const group = 'East Basildon PCN';
       const area = 'aryan medical centre';
       const areaCode = 'F81640';
 
-      await resultsPage.selectAreaType(gpAreaType);
-
-      await resultsPage.selectGroupType(groupType);
-
-      await resultsPage.selectGroup(group);
-
-      await resultsPage.selectArea(area, areaCode);
-
+      await resultsPage.selectAreaTypeAndAssertURLUpdated(gpAreaType);
+      await resultsPage.selectGroupTypeAndAssertURLUpdated(groupType);
+      await resultsPage.selectGroupAndAssertURLUpdated(group);
+      await resultsPage.selectAreaAndAssertURLUpdated(area, areaCode);
       await resultsPage.assertFiltersDisabled();
+    });
 
-      // change group and pick another area
+    await test.step('change to different GP area', async () => {
       await resultsPage.closeAreaFilterPill(0);
 
       const newGroup = 'North 2 Islington PCN';
       const newArea = 'Archway Medical Centre';
       const newAreaCode = 'F83004';
 
-      await resultsPage.selectGroup(newGroup);
-
-      await resultsPage.selectArea(newArea, newAreaCode);
-
+      await resultsPage.selectGroupAndAssertURLUpdated(newGroup);
+      await resultsPage.selectAreaAndAssertURLUpdated(newArea, newAreaCode);
       await resultsPage.assertFiltersDisabled();
     });
 
-    await test.step('then deselect gps and change to filter by England (default)', async () => {
+    await test.step('filter by England', async () => {
       await resultsPage.closeAreaFilterPill(0);
 
       const englandAreaType = 'England';
 
-      await resultsPage.selectAreaType(englandAreaType);
-
+      await resultsPage.selectAreaTypeAndAssertURLUpdated(englandAreaType);
       await resultsPage.assertGroupTypeFilterContainsOnly(englandAreaType);
       await resultsPage.assertGroupFilterContainsOnly(englandAreaType);
-
-      await resultsPage.selectArea(englandAreaType, areaCodeForEngland);
-
+      await resultsPage.selectAreaAndAssertURLUpdated(
+        englandAreaType,
+        areaCodeForEngland
+      );
       await resultsPage.assertFiltersDisabled();
     });
 
-    await test.step('then deselect England and begin filtering by NHS Regions', async () => {
+    await test.step('filter by NHS regions', async () => {
       await resultsPage.closeAreaFilterPill(0);
 
       const nhsRegionAreaType = 'nhs-regions';
       const northWestNhsRegion = 'North West NHS Region';
       const northWestNhsRegionCode = 'E40000010';
 
-      await resultsPage.selectAreaType(nhsRegionAreaType);
-
+      await resultsPage.selectAreaTypeAndAssertURLUpdated(nhsRegionAreaType);
       await resultsPage.assertGroupTypeFilterContainsOnly('England');
       await resultsPage.assertGroupFilterContainsOnly('England');
-
-      await resultsPage.selectArea(northWestNhsRegion, northWestNhsRegionCode);
-
+      await resultsPage.selectAreaAndAssertURLUpdated(
+        northWestNhsRegion,
+        northWestNhsRegionCode
+      );
       await resultsPage.assertFiltersDisabled();
     });
 
-    await test.step('then deselect north west region and begin filtering by counties and unitary authorities', async () => {
+    await test.step('filter by counties and unitary authorities', async () => {
       await resultsPage.closeAreaFilterPill(0);
 
       const countiesAndUnitaryAuthoritiesAreaType =
@@ -386,32 +355,120 @@ test.describe(`Navigation, accessibility and validation tests`, () => {
       const area = 'County Durham';
       const areaCode = 'E06000047';
 
-      await resultsPage.selectAreaType(countiesAndUnitaryAuthoritiesAreaType);
-
+      await resultsPage.selectAreaTypeAndAssertURLUpdated(
+        countiesAndUnitaryAuthoritiesAreaType
+      );
       await resultsPage.assertGroupTypeFilterContainsOnly(
         'England,Combined Authorities,Regions'
       );
       await resultsPage.assertGroupFilterContainsOnly('England');
+      await resultsPage.selectGroupTypeAndAssertURLUpdated(groupType);
+      await resultsPage.assertGroupFilterContainsOnly('');
+      await resultsPage.selectAreaAndAssertURLUpdated(area, areaCode);
+      await resultsPage.assertFiltersDisabled();
+    });
+  });
+});
+
+test.describe('Navigation Tests', () => {
+  test('should handle navigation flow between pages', async ({
+    homePage,
+    resultsPage,
+    chartPage,
+    indicatorPage,
+    axeBuilder,
+  }) => {
+    await test.step('Navigate to home page and search', async () => {
+      await homePage.navigateToHomePage();
+      await homePage.searchForIndicators(searchMode, subjectSearchTerm);
+      await homePage.clickSearchButton();
+    });
 
-      await resultsPage.selectGroupType(groupType);
+    await test.step('Select indicator and check charts accessibility', async () => {
+      await resultsPage.selectIndicatorCheckboxesAndVerifyURLUpdated(
+        validIndicatorIDs
+      );
+      await resultsPage.clickViewChartsButton();
 
-      await resultsPage.assertGroupFilterContainsOnly('');
+      await chartPage.waitForURLToContain('chart');
+      await chartPage.expectNoAccessibilityViolations(axeBuilder, [
+        'color-contrast',
+      ]);
+    });
 
-      await resultsPage.selectArea(area, areaCode);
+    await test.step('Navigate to indicator information page and back', async () => {
+      // get the indicator data for the selected indicatorID
+      const indicator = typedIndicatorData.find(
+        (ind) => ind.indicatorID === validIndicatorIDs[0].indicatorID
+      );
 
-      await resultsPage.assertFiltersDisabled();
+      if (!indicator) {
+        throw new Error(`Indicator with ID ${validIndicatorIDs[0]} not found`);
+      }
+
+      await resultsPage.clickViewBackgroundInformationLinkForIndicator(
+        indicator
+      );
+      await indicatorPage.waitForURLToContain('indicator');
+      await indicatorPage.checkIndicatorNameTitle(indicator);
+      await indicatorPage.expectNoAccessibilityViolations(axeBuilder);
+      await indicatorPage.clickBackLink();
+    });
+
+    await test.step('Apply area filters on chart page', async () => {
+      await chartPage.selectAreasFiltersIfRequired(
+        searchMode,
+        AreaMode.THREE_PLUS_AREAS,
+        subjectSearchTerm,
+        'gps'
+      );
+    });
+
+    await test.step('Verify chart component and accessibility', async () => {
+      await chartPage.checkSpecificChartComponent(
+        ChartPage.barChartEmbeddedTableComponent
+      );
+      await chartPage.expectNoAccessibilityViolations(axeBuilder, [
+        'color-contrast',
+      ]);
+    });
+
+    await test.step('Navigate back to results page and verify state', async () => {
+      await chartPage.clickBackLink();
+      await resultsPage.checkSearchResultsTitle(subjectSearchTerm);
+      await resultsPage.checkIndicatorCheckboxChecked(
+        validIndicatorIDs[0].indicatorID
+      );
+    });
+
+    await test.step('Navigate back to home page and verify state', async () => {
+      await resultsPage.clickBackLink();
+      await homePage.checkSearchFieldIsPrePopulatedWith(subjectSearchTerm);
+    });
+
+    await test.step('Clear search field and close area pills', async () => {
+      await homePage.clearSearchIndicatorField();
+      await homePage.closeAreaFilterPill(0);
+      await homePage.closeAreaFilterPill(0);
+      await homePage.closeAreaFilterPill(0);
+    });
+
+    await test.step('Verify validation prevents forward navigation', async () => {
+      await homePage.clickSearchButton();
+      await homePage.checkSearchFieldIsPrePopulatedWith(); // nothing - as nothing should be prepopulated after clearing search field above
+      await homePage.checkSummaryValidation(
+        `There is a problemEnter a subject you want to search forEnter an area you want to search for`
+      );
     });
   });
 });
 
-// log out current url when a test fails
+// Capture and log out URL on test failure
 test.afterEach(async ({ page }, testInfo) => {
   if (testInfo.status !== testInfo.expectedStatus) {
-    // Test failed - capture the URL
     const url = page.url();
     console.log(`Test failed! Current URL: ${url}`);
 
-    // You can also attach it to the test report
     await testInfo.attach('failed-url', {
       body: url,
       contentType: 'text/plain',


Reviews:
user: login: alibombaye
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T12:44:31Z
user: login: alibombaye
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T12:46:25Z
user: login: alibombaye
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T12:51:43Z
user: login: alibombaye
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T13:37:04Z

Review comments:
diff_hunk: @@ -15,165 +15,193 @@ import { AreaDocument, RawIndicatorDocument } from '@/lib/search/searchTypes';
 import ChartPage from '@/playwright/page-objects/pages/chartPage';
 import { areaCodeForEngland } from '@/lib/chartHelpers/constants';
 
-// tests in this file use mock service worker to mock the API response
-// so that the tests can be run without the need for a backend
-// see frontend/fingertips-frontend/assets/mockIndicatorData.json
-// and frontend/fingertips-frontend/assets/mockAreaData.json
-//@ts-expect-error don't care about type checking this json file
+/**
+ * Note that this test suite uses mock service worker to mock API responses, therefore these playwright tests are isolated from the backend
+ * See:
+ * - frontend/fingertips-frontend/assets/mockIndicatorData.json
+ * - frontend/fingertips-frontend/assets/mockAreaData.json
+ */
+
+// Test data setup
+// @ts-expect-error mock data type casting
 const indicatorData = mockIndicators as RawIndicatorDocument[];
 const subjectSearchTerm = 'hospital';
+const secondSubjectSearchTerm = 'diabetes';
 const indicatorMode = IndicatorMode.ONE_INDICATOR;
 const searchMode = SearchMode.ONLY_SUBJECT;
 let allValidIndicatorIDs: string[];
 let validIndicatorIDs: IndicatorInfo[];
 let allNHSRegionAreas: AreaDocument[];
 let typedIndicatorData: RawIndicatorDocument[];
 
-test.beforeAll(
-  `get indicatorIDs from the mock data source for searchTerm: ${subjectSearchTerm} and get mock area data`,
-  () => {
-    typedIndicatorData = indicatorData.map(
-      (indicator: RawIndicatorDocument) => {
-        return {
-          ...indicator,
-          lastUpdated: new Date(indicator.lastUpdatedDate),
-        };
-      }
-    );
+test.beforeAll('Initialize test data from mock sources', () => {
+  typedIndicatorData = indicatorData.map((indicator: RawIndicatorDocument) => ({
+    ...indicator,
+    lastUpdated: new Date(indicator.lastUpdatedDate),
+  }));
+
+  // Get all valid indicators based on subjectSearchTerm search term
+  allValidIndicatorIDs = getAllIndicatorIDsForSearchTerm(
+    typedIndicatorData,
+    subjectSearchTerm
+  );
+
+  // Filter down valid indicators based on mode
+  validIndicatorIDs = returnIndicatorIDsByIndicatorMode(
+    allValidIndicatorIDs,
+    indicatorMode
+  );
+
+  // Get all NHS region areas
+  allNHSRegionAreas = getAllAreasByAreaType(mockAreas, 'nhs-regions');
+});
 
-    allValidIndicatorIDs = getAllIndicatorIDsForSearchTerm(
-      typedIndicatorData,
-      subjectSearchTerm
-    );
+test.describe('Search Page Tests', () => {
+  test('should navigate to search page and validate accessibility', async ({
path: frontend/fingertips-frontend/playwright/tests/isolated_ui_tests/navigation_and_validation.spec.ts
user: login: alibombaye
body: `should navigate to home page and validate accessibility`?
created_at: 2025-05-14T12:44:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -15,165 +15,193 @@ import { AreaDocument, RawIndicatorDocument } from '@/lib/search/searchTypes';
 import ChartPage from '@/playwright/page-objects/pages/chartPage';
 import { areaCodeForEngland } from '@/lib/chartHelpers/constants';
 
-// tests in this file use mock service worker to mock the API response
-// so that the tests can be run without the need for a backend
-// see frontend/fingertips-frontend/assets/mockIndicatorData.json
-// and frontend/fingertips-frontend/assets/mockAreaData.json
-//@ts-expect-error don't care about type checking this json file
+/**
+ * Note that this test suite uses mock service worker to mock API responses, therefore these playwright tests are isolated from the backend
+ * See:
+ * - frontend/fingertips-frontend/assets/mockIndicatorData.json
+ * - frontend/fingertips-frontend/assets/mockAreaData.json
+ */
+
+// Test data setup
+// @ts-expect-error mock data type casting
 const indicatorData = mockIndicators as RawIndicatorDocument[];
 const subjectSearchTerm = 'hospital';
+const secondSubjectSearchTerm = 'diabetes';
 const indicatorMode = IndicatorMode.ONE_INDICATOR;
 const searchMode = SearchMode.ONLY_SUBJECT;
 let allValidIndicatorIDs: string[];
 let validIndicatorIDs: IndicatorInfo[];
 let allNHSRegionAreas: AreaDocument[];
 let typedIndicatorData: RawIndicatorDocument[];
 
-test.beforeAll(
-  `get indicatorIDs from the mock data source for searchTerm: ${subjectSearchTerm} and get mock area data`,
-  () => {
-    typedIndicatorData = indicatorData.map(
-      (indicator: RawIndicatorDocument) => {
-        return {
-          ...indicator,
-          lastUpdated: new Date(indicator.lastUpdatedDate),
-        };
-      }
-    );
+test.beforeAll('Initialize test data from mock sources', () => {
+  typedIndicatorData = indicatorData.map((indicator: RawIndicatorDocument) => ({
+    ...indicator,
+    lastUpdated: new Date(indicator.lastUpdatedDate),
+  }));
+
+  // Get all valid indicators based on subjectSearchTerm search term
+  allValidIndicatorIDs = getAllIndicatorIDsForSearchTerm(
+    typedIndicatorData,
+    subjectSearchTerm
+  );
+
+  // Filter down valid indicators based on mode
+  validIndicatorIDs = returnIndicatorIDsByIndicatorMode(
+    allValidIndicatorIDs,
+    indicatorMode
+  );
+
+  // Get all NHS region areas
+  allNHSRegionAreas = getAllAreasByAreaType(mockAreas, 'nhs-regions');
+});
 
-    allValidIndicatorIDs = getAllIndicatorIDsForSearchTerm(
-      typedIndicatorData,
-      subjectSearchTerm
-    );
+test.describe('Search Page Tests', () => {
+  test('should navigate to search page and validate accessibility', async ({
+    homePage,
+    axeBuilder,
+  }) => {
+    await homePage.navigateToHomePage();
+    await homePage.checkOnHomePage();
+    await homePage.expectNoAccessibilityViolations(axeBuilder);
+  });
+
+  test('should validate search field form validations', async ({
+    homePage,
+  }) => {
+    await test.step('Navigate to home page', async () => {
+      await homePage.navigateToHomePage();
+      await homePage.checkOnHomePage();
+      await homePage.clearSearchIndicatorField();
path: frontend/fingertips-frontend/playwright/tests/isolated_ui_tests/navigation_and_validation.spec.ts
user: login: alibombaye
body: move this line perhaps to the next step instead - feels like it goes with it.

By the way do we need to do this? it should already be empty. Perhaps just a check it is empty
created_at: 2025-05-14T12:46:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -197,43 +230,41 @@ test.describe(`Navigation, accessibility and validation tests`, () => {
         .toHaveCount(expectedPillTexts.length);
 
       const filterPillNames = await resultsPage.areaFilterPillsText();
-
       sortAlphabetically(filterPillNames);
       sortAlphabetically(expectedPillTexts);
       test.expect(filterPillNames).toEqual(expectedPillTexts);
 
+      // Verify area filter combobox is disabled when areas are selected
path: frontend/fingertips-frontend/playwright/tests/isolated_ui_tests/navigation_and_validation.spec.ts
user: login: alibombaye
body: `// Verify all area filter comboboxes are disabled when an area/areas are selected`?
created_at: 2025-05-14T12:51:43Z
author_association: CONTRIBUTOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-05-14T13:37:01Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=dhsc-govuk_FingertipsNext&pullRequest=572) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=dhsc-govuk_FingertipsNext&pullRequest=572&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=dhsc-govuk_FingertipsNext&pullRequest=572&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=dhsc-govuk_FingertipsNext&pullRequest=572&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=dhsc-govuk_FingertipsNext&pullRequest=572&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=dhsc-govuk_FingertipsNext&pullRequest=572&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=dhsc-govuk_FingertipsNext&pullRequest=572)


author_association: NONE