From 21a02137cc817c6ede673a3eebc59e273e0e16b1 Mon Sep 17 00:00:00 2001 From: Rory Doak <138574807+RODO94@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:25:01 +0000 Subject: [PATCH] feat: Add E2E Coverage for Property Information (#4025) --- e2e/tests/ui-driven/package.json | 1 + e2e/tests/ui-driven/pnpm-lock.yaml | 7 ++ .../src/create-flow-with-geospatial.spec.ts | 66 +++++++++++++++++-- .../ui-driven/src/helpers/addComponent.ts | 43 ++++++++++++ .../ui-driven/src/helpers/geospatialChecks.ts | 12 ++++ e2e/tests/ui-driven/src/helpers/types.ts | 2 + .../ui-driven/src/mocks/geospatialMocks.ts | 39 +++++++++++ e2e/tests/ui-driven/src/pages/Editor.ts | 24 +++++++ 8 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 e2e/tests/ui-driven/src/helpers/geospatialChecks.ts create mode 100644 e2e/tests/ui-driven/src/mocks/geospatialMocks.ts diff --git a/e2e/tests/ui-driven/package.json b/e2e/tests/ui-driven/package.json index 91c2c8e9db..b5aaa364e3 100644 --- a/e2e/tests/ui-driven/package.json +++ b/e2e/tests/ui-driven/package.json @@ -22,6 +22,7 @@ "packageManager": "pnpm@8.6.6", "devDependencies": { "@playwright/test": "^1.49.0", + "@types/geojson": "^7946.0.14", "@types/node": "18.16.1", "eslint-plugin-playwright": "^0.20.0" } diff --git a/e2e/tests/ui-driven/pnpm-lock.yaml b/e2e/tests/ui-driven/pnpm-lock.yaml index 4e3fc3e0fe..75407766b7 100644 --- a/e2e/tests/ui-driven/pnpm-lock.yaml +++ b/e2e/tests/ui-driven/pnpm-lock.yaml @@ -40,6 +40,9 @@ devDependencies: '@playwright/test': specifier: ^1.49.0 version: 1.49.0 + '@types/geojson': + specifier: ^7946.0.14 + version: 7946.0.14 '@types/node': specifier: 18.16.1 version: 18.16.1 @@ -647,6 +650,10 @@ packages: resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} dev: false + /@types/geojson@7946.0.14: + resolution: {integrity: sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==} + dev: true + /@types/json-schema@7.0.15: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} dev: false diff --git a/e2e/tests/ui-driven/src/create-flow-with-geospatial.spec.ts b/e2e/tests/ui-driven/src/create-flow-with-geospatial.spec.ts index 38cb922659..8bc6fe181b 100644 --- a/e2e/tests/ui-driven/src/create-flow-with-geospatial.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow-with-geospatial.spec.ts @@ -6,7 +6,11 @@ import { } from "./helpers/context"; import { getTeamPage } from "./helpers/getPage"; import { createAuthenticatedSession } from "./helpers/globalHelpers"; -import { answerFindProperty, clickContinue } from "./helpers/userActions"; +import { + answerFindProperty, + answerQuestion, + clickContinue, +} from "./helpers/userActions"; import { PlaywrightEditor } from "./pages/Editor"; import { navigateToService, @@ -15,6 +19,11 @@ import { } from "./helpers/navigateAndPublish"; import { TestContext } from "./helpers/types"; import { serviceProps } from "./helpers/serviceData"; +import { checkGeoJsonContent } from "./helpers/geospatialChecks"; +import { + mockMapGeoJson, + mockPropertyTypeOptions, +} from "./mocks/geospatialMocks"; test.describe("Flow creation, publish and preview", () => { let context: TestContext = { @@ -48,10 +57,21 @@ test.describe("Flow creation, publish and preview", () => { await editor.createFindProperty(); await expect(editor.nodeList).toContainText(["Find property"]); + // Find property will automate past this question at first + await editor.createQuestionWithDataFieldOptions( + "What type of property is it?", + mockPropertyTypeOptions, + "property.type", + ); + await expect(editor.nodeList).toContainText([ + "What type of property is it?", + ]); + // but property info "change" button will navigate back to it + await editor.createPropertyInformation(); + await expect(editor.nodeList).toContainText(["About the property"]); await editor.createInternalPortal(); await editor.populateInternalPortal(); await page.getByRole("link", { name: "start" }).click(); // return to main flow - await editor.createFilter(); await editor.createUploadAndLabel(); // TODO: editor.createPropertyInfo() await editor.createDrawBoundary(); @@ -61,7 +81,6 @@ test.describe("Flow creation, publish and preview", () => { await expect(editor.nodeList).toContainText([ "Find property", "an internal portalEdit Portal", - "Filter - Planning permissionImmuneMissing informationPermission neededPrior approvalNoticePermitted developmentNot developmentNo flag result", "Upload and label", "Confirm your location plan", "Planning constraints", @@ -99,18 +118,57 @@ test.describe("Flow creation, publish and preview", () => { await page.goto( `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); + await expect( page.locator("h1", { hasText: "Find the property" }), ).toBeVisible(); await answerFindProperty(page); await clickContinue({ page }); + await expect( + page.getByRole("heading", { name: "About the property" }), + ).toBeVisible(); + + // Check map component has geoJson content + await checkGeoJsonContent(page, mockMapGeoJson); + + // Check property info is being shown + await expect(page.getByText("Test Street, Testville")).toBeVisible(); + await expect(page.getByText("Residential - Semi Detached")).toBeVisible(); + const changeButton = page.getByRole("button", { + name: "Change your Property type", + }); + + await changeButton.click(); + + // ensure residential is selected on back nav to test previouslySubmittedData is working + await expect( + page.getByRole("radio", { name: "Residential", checked: true }), + ).toBeVisible(); + + await answerQuestion({ + page: page, + title: "What type of property is it?", + answer: "Commercial", + }); + + // navigate back to Property Info page + await clickContinue({ page }); + await expect( + page.getByRole("heading", { name: "About the property" }), + ).toBeVisible(); + + // Ensure we've successfully changed property type + await expect(page.getByText("Residential - Semi Detached")).toBeHidden(); + await expect(page.getByText("Commercial")).toBeVisible(); + + await clickContinue({ page }); + await expect( page.locator("h1", { hasText: "A notice inside a portal!" }), ).toBeVisible(); await clickContinue({ page }); - // TODO: answer filter? // TODO: answer uploadAndLabel // TODO: answerPropertyInfo, answerDrawBoundary, answerPlanningConstraints }); diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index 61c0f8ca4e..d6d523b0fa 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -2,6 +2,7 @@ import { ComponentType } from "@opensystemslab/planx-core/types"; import { expect, Locator, Page } from "@playwright/test"; import { contextDefaults } from "./context"; import { externalPortalServiceProps } from "./serviceData"; +import { OptionWithDataValues } from "./types"; const createBaseComponent = async ( page: Page, @@ -81,6 +82,9 @@ const createBaseComponent = async ( break; case ComponentType.FindProperty: break; + case ComponentType.PropertyInformation: + await page.getByLabel("Show users a 'change' link to").click(); + break; case ComponentType.PlanningConstraints: break; case ComponentType.DrawBoundary: @@ -153,6 +157,21 @@ export const createQuestionWithOptions = async ( ); }; +export const createQuestionWithDataFieldOptions = async ( + page: Page, + locatingNode: Locator, + questionText: string, + options: OptionWithDataValues[], + dataField: string, +) => { + await locatingNode.click(); + await page.getByRole("dialog").waitFor(); + await page.getByPlaceholder("Text").fill(questionText); + await page.getByPlaceholder("Data Field").fill(dataField); + await createComponentOptionsWithDataValues(page, options); + await page.locator('button[form="modal"][type="submit"]').click(); +}; + export const createNotice = async ( page: Page, locatingNode: Locator, @@ -275,6 +294,17 @@ export const createFindProperty = async (page: Page, locatingNode: Locator) => { await createBaseComponent(page, locatingNode, ComponentType.FindProperty); }; +export const createPropertyInformation = async ( + page: Page, + locatingNode: Locator, +) => { + await createBaseComponent( + page, + locatingNode, + ComponentType.PropertyInformation, + ); +}; + export const createPlanningConstraints = async ( page: Page, locatingNode: Locator, @@ -331,6 +361,19 @@ async function createComponentOptions( } } +async function createComponentOptionsWithDataValues( + page: Page, + options: OptionWithDataValues[], +) { + let index = 0; + for (const option of options) { + await page.locator("button").filter({ hasText: "add new" }).click(); + await page.getByPlaceholder("Option").nth(index).fill(option.optionText); + await page.getByPlaceholder("Data Value").nth(index).fill(option.dataValue); + index++; + } +} + export const createList = async ( page: Page, locatingNode: Locator, diff --git a/e2e/tests/ui-driven/src/helpers/geospatialChecks.ts b/e2e/tests/ui-driven/src/helpers/geospatialChecks.ts new file mode 100644 index 0000000000..7b55e01997 --- /dev/null +++ b/e2e/tests/ui-driven/src/helpers/geospatialChecks.ts @@ -0,0 +1,12 @@ +import { expect, Page } from "@playwright/test"; +import { Feature } from "geojson"; + +export const checkGeoJsonContent = async (page: Page, geoJson: Feature) => { + // Wait for the map component to be present + const mapComponent = await page.waitForSelector("my-map"); + + // Get the geojsonData attribute + const geojsonData = await mapComponent.getAttribute("geojsondata"); + + expect(JSON.parse(geojsonData!)).toEqual(geoJson); +}; diff --git a/e2e/tests/ui-driven/src/helpers/types.ts b/e2e/tests/ui-driven/src/helpers/types.ts index 25c3bff286..b76bb5dd9e 100644 --- a/e2e/tests/ui-driven/src/helpers/types.ts +++ b/e2e/tests/ui-driven/src/helpers/types.ts @@ -21,3 +21,5 @@ export interface TestContext { externalPortalFlow?: Flow; sessionIds?: string[]; } + +export type OptionWithDataValues = { optionText: string; dataValue: string }; diff --git a/e2e/tests/ui-driven/src/mocks/geospatialMocks.ts b/e2e/tests/ui-driven/src/mocks/geospatialMocks.ts new file mode 100644 index 0000000000..fb26308b1d --- /dev/null +++ b/e2e/tests/ui-driven/src/mocks/geospatialMocks.ts @@ -0,0 +1,39 @@ +import { OptionWithDataValues } from "../helpers/types"; + +export const mockPropertyTypeOptions: OptionWithDataValues[] = [ + { optionText: "Residential", dataValue: "residential" }, + { optionText: "Commercial", dataValue: "commercial" }, +]; + +import { Feature } from "geojson"; + +export const mockMapGeoJson: Feature = { + geometry: { + type: "MultiPolygon", + coordinates: [ + [ + [ + [-0.633498, 51.605485], + [-0.633455, 51.605606], + [-0.633788, 51.605643], + [-0.634429, 51.605799], + [-0.634429, 51.605767], + [-0.633498, 51.605485], + ], + ], + ], + }, + type: "Feature", + properties: { + "entry-date": "2024-05-06", + "start-date": "2010-05-12", + "end-date": "", + entity: 12000041468, + name: "", + dataset: "title-boundary", + typology: "geography", + reference: "45211072", + prefix: "title-boundary", + "organisation-entity": "13", + }, +}; diff --git a/e2e/tests/ui-driven/src/pages/Editor.ts b/e2e/tests/ui-driven/src/pages/Editor.ts index d19ae67146..0439cd71ce 100644 --- a/e2e/tests/ui-driven/src/pages/Editor.ts +++ b/e2e/tests/ui-driven/src/pages/Editor.ts @@ -20,6 +20,8 @@ import { createNotice, createNumberInput, createPlanningConstraints, + createPropertyInformation, + createQuestionWithDataFieldOptions, createQuestionWithOptions, createResult, createReview, @@ -27,6 +29,7 @@ import { createTextInput, createUploadAndLabel, } from "../helpers/addComponent"; +import { OptionWithDataValues } from "../helpers/types"; export class PlaywrightEditor { readonly page: Page; @@ -81,6 +84,23 @@ export class PlaywrightEditor { ).toBeVisible(); } + async createQuestionWithDataFieldOptions( + title: string, + answers: OptionWithDataValues[], + dataField: string, + ) { + await createQuestionWithDataFieldOptions( + this.page, + this.getNextNode(), + title, + answers, + dataField, + ); + await expect( + this.page.locator("a").filter({ hasText: title }), + ).toBeVisible(); + } + async createNoticeOnEachBranch() { // Add a notice to the "Yes" path await createNotice( @@ -168,6 +188,10 @@ export class PlaywrightEditor { await createFindProperty(this.page, this.getNextNode()); } + async createPropertyInformation() { + await createPropertyInformation(this.page, this.getNextNode()); + } + async createDrawBoundary() { await createDrawBoundary(this.page, this.getNextNode()); }