From b11f728eb6800f563128b5a17b510e8d3fac2010 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Tue, 3 Sep 2024 12:13:15 +0100 Subject: [PATCH 01/39] Add more guidance to readme --- e2e/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/e2e/README.md b/e2e/README.md index 0585a1bcc9..4bac3a74af 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -8,6 +8,14 @@ More info about e2e tests is available in our [testing approach documentation](h We use [Playwright](https://playwright.dev/docs/api/class-test) to run UI driven tests where user interactions are simulated via a web browser. +### Running UI-driven tests + +1. Navigate to `/tests/ui-driven` +2. Run `pnpm install` to install the Playwright package. +3. Run `pnpm exec playwright install` to install the Playwright test browsers. +4. Run the tests with `pnpm test` + + ## API driven tests From 1700ad9a58f3adad9ccf39d028caf55d740f3df7 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:11:31 +0100 Subject: [PATCH 02/39] Extract out createQuestion and createNotice helpers --- .../src/create-flow/create-flow.spec.ts | 68 +++++++++---------- .../ui-driven/src/create-flow/helpers.ts | 34 +++++++++- 2 files changed, 65 insertions(+), 37 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index d30b6f269d..6645ddebb2 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -1,16 +1,21 @@ -import { test, expect, Browser } from "@playwright/test"; +import { Browser, expect, test } from "@playwright/test"; +import type { Context } from "../context"; import { contextDefaults, setUpTestContext, tearDownTestContext, } from "../context"; import { - createAuthenticatedSession, answerQuestion, clickContinue, + createAuthenticatedSession, } from "../globalHelpers"; -import type { Context } from "../context"; -import { getTeamPage, isGetUserRequest } from "./helpers"; +import { + createNotice, + createQuestionWithOptions, + getTeamPage, + isGetUserRequest, +} from "./helpers"; test.describe("Navigation", () => { let context: Context = { @@ -52,7 +57,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)), + (req) => (isRepeatedRequestMade = isGetUserRequest(req)) ); Promise.all([ @@ -102,42 +107,33 @@ test.describe("Navigation", () => { // update context to allow flow to be torn down context.flow = { ...serviceProps }; - await page.locator("li.hanger > a").click(); - await page.getByRole("dialog").waitFor(); - const questionText = "Is this a test?"; - await page.getByPlaceholder("Text").fill(questionText); - - await page.locator("button").filter({ hasText: "add new" }).click(); - await page.getByPlaceholder("Option").fill("Yes"); - - await page.locator("button").filter({ hasText: "add new" }).click(); - await page.getByPlaceholder("Option").nth(1).fill("No"); - - await page.locator("button").filter({ hasText: "Create question" }).click(); + await createQuestionWithOptions(page, "li.hanger > a", questionText, [ + "Yes", + "No", + ]); await expect( - page.locator("a").filter({ hasText: questionText }), + page.locator("a").filter({ hasText: questionText }) ).toBeVisible(); // Add a notice to the "Yes" path const yesBranch = page.locator("#flow .card .options .option").nth(0); - await yesBranch.locator(".hanger > a").click(); - await page.getByRole("dialog").waitFor(); - await page.locator("select").selectOption({ label: "Notice" }); const yesBranchNoticeText = "Yes! this is a test"; - await page.getByPlaceholder("Notice").fill(yesBranchNoticeText); - await page.locator("button").filter({ hasText: "Create notice" }).click(); + await createNotice( + page, + yesBranch.locator(".hanger > a"), + yesBranchNoticeText + ); // Add a notice to the "No" path const noBranch = page.locator("#flow .card .options .option").nth(1); - await noBranch.locator(".hanger > a").click(); - await page.getByRole("dialog").waitFor(); - - await page.locator("select").selectOption({ label: "Notice" }); const noBranchNoticeText = "Sorry, this is a test"; - await page.getByPlaceholder("Notice").fill(noBranchNoticeText); - await page.locator("button").filter({ hasText: "Create notice" }).click(); + await createNotice( + page, + noBranch.locator(".hanger > a"), + noBranchNoticeText + ); const nodes = page.locator(".card"); await expect(nodes.getByText(questionText)).toBeVisible(); @@ -156,7 +152,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -190,11 +186,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }), + page.getByRole("heading", { level: 1, name: "Offline" }) ).toBeVisible(); }); @@ -213,7 +209,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully"), + page.getByText("Service settings updated successfully") ).toBeVisible(); // Exit back to main Editor page @@ -236,13 +232,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }), + page.locator("h1", { hasText: "Yes! this is a test" }) ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -250,7 +246,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }), + page.locator("h1", { hasText: "Sorry, this is a test" }) ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/create-flow/helpers.ts b/e2e/tests/ui-driven/src/create-flow/helpers.ts index 842cf6e430..c2a3fddbe5 100644 --- a/e2e/tests/ui-driven/src/create-flow/helpers.ts +++ b/e2e/tests/ui-driven/src/create-flow/helpers.ts @@ -1,4 +1,4 @@ -import { Browser, Page, Request } from "@playwright/test"; +import { Browser, Locator, Page, Request } from "@playwright/test"; import { createAuthenticatedSession } from "../globalHelpers"; export const isGetUserRequest = (req: Request) => @@ -32,3 +32,35 @@ export async function getTeamPage({ await page.locator("h3", { hasText: teamName }).click(); return page; } + +export const createQuestionWithOptions = async ( + page: Page, + locatingNodeSelector: string, + questionText: string, + options: string[] +) => { + await page.locator(locatingNodeSelector).click(); + await page.getByRole("dialog").waitFor(); + await page.getByPlaceholder("Text").fill(questionText); + + 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); + index++; + } + + await page.locator("button").filter({ hasText: "Create question" }).click(); +}; + +export const createNotice = async ( + page: Page, + locatingNode: Locator, + noticeText: string +) => { + await locatingNode.click(); + await page.getByRole("dialog").waitFor(); + await page.locator("select").selectOption({ label: "Notice" }); + await page.getByPlaceholder("Notice").fill(noticeText); + await page.locator("button").filter({ hasText: "Create notice" }).click(); +}; From f3b684896b74ef805ba5cb7aa0d28fa01b53b36a Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:49:48 +0100 Subject: [PATCH 03/39] Add checklist component to flow --- .../src/create-flow/create-flow.spec.ts | 11 ++++++++++ .../ui-driven/src/create-flow/helpers.ts | 20 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 6645ddebb2..61c7b8fd2f 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -11,6 +11,7 @@ import { createAuthenticatedSession, } from "../globalHelpers"; import { + createChecklist, createNotice, createQuestionWithOptions, getTeamPage, @@ -135,10 +136,20 @@ test.describe("Navigation", () => { noBranchNoticeText ); + // add a checklist + // TODO: find a nicer way to find the next node + const nextNode = page.locator(".hanger > a").nth(5); + createChecklist(page, nextNode, "A checklist title", [ + "Checklist item 1", + "Second checklist item", + "The third checklist item", + ]); + const nodes = page.locator(".card"); await expect(nodes.getByText(questionText)).toBeVisible(); await expect(nodes.getByText(yesBranchNoticeText)).toBeVisible(); await expect(nodes.getByText(noBranchNoticeText)).toBeVisible(); + await expect(nodes.getByText("Checklist item 1")).toBeVisible(); }); test("Cannot preview an unpublished flow", async ({ diff --git a/e2e/tests/ui-driven/src/create-flow/helpers.ts b/e2e/tests/ui-driven/src/create-flow/helpers.ts index c2a3fddbe5..86e73dccce 100644 --- a/e2e/tests/ui-driven/src/create-flow/helpers.ts +++ b/e2e/tests/ui-driven/src/create-flow/helpers.ts @@ -64,3 +64,23 @@ export const createNotice = async ( await page.getByPlaceholder("Notice").fill(noticeText); await page.locator("button").filter({ hasText: "Create notice" }).click(); }; + +export const createChecklist = async ( + page: Page, + locatingNode: Locator, + checklistTitle: string, + checklistOptions: string[] +) => { + await locatingNode.click(); + await page.getByRole("dialog").waitFor(); + await page.locator("select").selectOption({ label: "Checklist" }); + await page.getByPlaceholder("Text").fill(checklistTitle); + + let index = 0; + for (const option of checklistOptions) { + await page.locator("button").filter({ hasText: "add new option" }).click(); + await page.getByPlaceholder("Option").nth(index).fill(option); + index++; + } + await page.locator("button").filter({ hasText: "Create checklist" }).click(); +}; From 4aea0595dc8d5d3e768a5688ac40bc6f92ece222 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Tue, 3 Sep 2024 17:51:54 +0100 Subject: [PATCH 04/39] Prettier --- .../src/create-flow/create-flow.spec.ts | 22 +++++++++---------- .../ui-driven/src/create-flow/helpers.ts | 6 ++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 61c7b8fd2f..e3cdb05b9a 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -58,7 +58,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)) + (req) => (isRepeatedRequestMade = isGetUserRequest(req)), ); Promise.all([ @@ -114,7 +114,7 @@ test.describe("Navigation", () => { "No", ]); await expect( - page.locator("a").filter({ hasText: questionText }) + page.locator("a").filter({ hasText: questionText }), ).toBeVisible(); // Add a notice to the "Yes" path @@ -124,7 +124,7 @@ test.describe("Navigation", () => { await createNotice( page, yesBranch.locator(".hanger > a"), - yesBranchNoticeText + yesBranchNoticeText, ); // Add a notice to the "No" path @@ -133,7 +133,7 @@ test.describe("Navigation", () => { await createNotice( page, noBranch.locator(".hanger > a"), - noBranchNoticeText + noBranchNoticeText, ); // add a checklist @@ -163,7 +163,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -197,11 +197,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }) + page.getByRole("heading", { level: 1, name: "Offline" }), ).toBeVisible(); }); @@ -220,7 +220,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully") + page.getByText("Service settings updated successfully"), ).toBeVisible(); // Exit back to main Editor page @@ -243,13 +243,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }) + page.locator("h1", { hasText: "Yes! this is a test" }), ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -257,7 +257,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }) + page.locator("h1", { hasText: "Sorry, this is a test" }), ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/create-flow/helpers.ts b/e2e/tests/ui-driven/src/create-flow/helpers.ts index 86e73dccce..f567fd9cb1 100644 --- a/e2e/tests/ui-driven/src/create-flow/helpers.ts +++ b/e2e/tests/ui-driven/src/create-flow/helpers.ts @@ -37,7 +37,7 @@ export const createQuestionWithOptions = async ( page: Page, locatingNodeSelector: string, questionText: string, - options: string[] + options: string[], ) => { await page.locator(locatingNodeSelector).click(); await page.getByRole("dialog").waitFor(); @@ -56,7 +56,7 @@ export const createQuestionWithOptions = async ( export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string + noticeText: string, ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -69,7 +69,7 @@ export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[] + checklistOptions: string[], ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); From 9c89e5d2b9f9dc69ed963f667a41cedd6f031011 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Tue, 3 Sep 2024 18:04:14 +0100 Subject: [PATCH 05/39] Create text input --- .../src/create-flow/create-flow.spec.ts | 31 +++++++++++-------- .../ui-driven/src/create-flow/helpers.ts | 12 +++++++ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index e3cdb05b9a..5cdb4c92dd 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -14,6 +14,7 @@ import { createChecklist, createNotice, createQuestionWithOptions, + createTextInput, getTeamPage, isGetUserRequest, } from "./helpers"; @@ -58,7 +59,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)), + (req) => (isRepeatedRequestMade = isGetUserRequest(req)) ); Promise.all([ @@ -114,7 +115,7 @@ test.describe("Navigation", () => { "No", ]); await expect( - page.locator("a").filter({ hasText: questionText }), + page.locator("a").filter({ hasText: questionText }) ).toBeVisible(); // Add a notice to the "Yes" path @@ -124,7 +125,7 @@ test.describe("Navigation", () => { await createNotice( page, yesBranch.locator(".hanger > a"), - yesBranchNoticeText, + yesBranchNoticeText ); // Add a notice to the "No" path @@ -133,23 +134,27 @@ test.describe("Navigation", () => { await createNotice( page, noBranch.locator(".hanger > a"), - noBranchNoticeText, + noBranchNoticeText ); // add a checklist // TODO: find a nicer way to find the next node - const nextNode = page.locator(".hanger > a").nth(5); - createChecklist(page, nextNode, "A checklist title", [ + let nextNode = page.locator(".hanger > a").nth(5); + await createChecklist(page, nextNode, "A checklist title", [ "Checklist item 1", "Second checklist item", "The third checklist item", ]); + nextNode = page.locator(".hanger > a").nth(7); + await createTextInput(page, nextNode, "Tell us about your trees."); + const nodes = page.locator(".card"); await expect(nodes.getByText(questionText)).toBeVisible(); await expect(nodes.getByText(yesBranchNoticeText)).toBeVisible(); await expect(nodes.getByText(noBranchNoticeText)).toBeVisible(); await expect(nodes.getByText("Checklist item 1")).toBeVisible(); + await expect(nodes.getByText("Tell us about your trees.")).toBeVisible(); }); test("Cannot preview an unpublished flow", async ({ @@ -163,7 +168,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -197,11 +202,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }), + page.getByRole("heading", { level: 1, name: "Offline" }) ).toBeVisible(); }); @@ -220,7 +225,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully"), + page.getByText("Service settings updated successfully") ).toBeVisible(); // Exit back to main Editor page @@ -243,13 +248,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }), + page.locator("h1", { hasText: "Yes! this is a test" }) ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -257,7 +262,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }), + page.locator("h1", { hasText: "Sorry, this is a test" }) ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/create-flow/helpers.ts b/e2e/tests/ui-driven/src/create-flow/helpers.ts index f567fd9cb1..edd00bd877 100644 --- a/e2e/tests/ui-driven/src/create-flow/helpers.ts +++ b/e2e/tests/ui-driven/src/create-flow/helpers.ts @@ -84,3 +84,15 @@ export const createChecklist = async ( } await page.locator("button").filter({ hasText: "Create checklist" }).click(); }; + +export const createTextInput = async ( + page: Page, + locatingNode: Locator, + inputTitle: string, +) => { + await locatingNode.click(); + await page.getByRole("dialog").waitFor(); + await page.locator("select").selectOption({ label: "Text Input" }); + await page.getByPlaceholder("Title").fill(inputTitle); + await page.locator("button").filter({ hasText: "Create text-input" }).click(); +}; \ No newline at end of file From 8fac0eb113c6fa75100b87c6d0b3ba1efb4d7baf Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Tue, 3 Sep 2024 18:08:59 +0100 Subject: [PATCH 06/39] Add number input --- .../src/create-flow/create-flow.spec.ts | 5 ++++ .../ui-driven/src/create-flow/helpers.ts | 27 +++++++++++++++---- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 5cdb4c92dd..729f7e9f7b 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -13,6 +13,7 @@ import { import { createChecklist, createNotice, + createNumberInput, createQuestionWithOptions, createTextInput, getTeamPage, @@ -149,12 +150,16 @@ test.describe("Navigation", () => { nextNode = page.locator(".hanger > a").nth(7); await createTextInput(page, nextNode, "Tell us about your trees."); + nextNode = page.locator(".hanger > a").nth(8); + await createNumberInput(page, nextNode, "How old are you?", "years"); + const nodes = page.locator(".card"); await expect(nodes.getByText(questionText)).toBeVisible(); await expect(nodes.getByText(yesBranchNoticeText)).toBeVisible(); await expect(nodes.getByText(noBranchNoticeText)).toBeVisible(); await expect(nodes.getByText("Checklist item 1")).toBeVisible(); await expect(nodes.getByText("Tell us about your trees.")).toBeVisible(); + await expect(nodes.getByText("How old are you?")).toBeVisible(); }); test("Cannot preview an unpublished flow", async ({ diff --git a/e2e/tests/ui-driven/src/create-flow/helpers.ts b/e2e/tests/ui-driven/src/create-flow/helpers.ts index edd00bd877..ec8518c0d0 100644 --- a/e2e/tests/ui-driven/src/create-flow/helpers.ts +++ b/e2e/tests/ui-driven/src/create-flow/helpers.ts @@ -37,7 +37,7 @@ export const createQuestionWithOptions = async ( page: Page, locatingNodeSelector: string, questionText: string, - options: string[], + options: string[] ) => { await page.locator(locatingNodeSelector).click(); await page.getByRole("dialog").waitFor(); @@ -56,7 +56,7 @@ export const createQuestionWithOptions = async ( export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string, + noticeText: string ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -69,7 +69,7 @@ export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[], + checklistOptions: string[] ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -88,11 +88,28 @@ export const createChecklist = async ( export const createTextInput = async ( page: Page, locatingNode: Locator, - inputTitle: string, + inputTitle: string ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); await page.locator("select").selectOption({ label: "Text Input" }); await page.getByPlaceholder("Title").fill(inputTitle); await page.locator("button").filter({ hasText: "Create text-input" }).click(); -}; \ No newline at end of file +}; + +export const createNumberInput = async ( + page: Page, + locatingNode: Locator, + inputTitle: string, + inputUnits: string +) => { + await locatingNode.click(); + await page.getByRole("dialog").waitFor(); + await page.locator("select").selectOption({ label: "Number Input" }); + await page.getByPlaceholder("Title").fill(inputTitle); + await page.getByPlaceholder("eg square metres").fill(inputUnits); + await page + .locator("button") + .filter({ hasText: "Create number-input" }) + .click(); +}; From f3a796a4c37b1fec134de1556771813820de86c7 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Tue, 3 Sep 2024 18:18:25 +0100 Subject: [PATCH 07/39] Create date input --- .../src/create-flow/create-flow.spec.ts | 8 ++++++++ .../ui-driven/src/create-flow/helpers.ts | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 729f7e9f7b..2d6ee74c07 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -12,6 +12,7 @@ import { } from "../globalHelpers"; import { createChecklist, + createDateInput, createNotice, createNumberInput, createQuestionWithOptions, @@ -147,12 +148,18 @@ test.describe("Navigation", () => { "The third checklist item", ]); + // add a text input nextNode = page.locator(".hanger > a").nth(7); await createTextInput(page, nextNode, "Tell us about your trees."); + // add a number input nextNode = page.locator(".hanger > a").nth(8); await createNumberInput(page, nextNode, "How old are you?", "years"); + // add a date input + nextNode = page.locator(".hanger > a").nth(9); + createDateInput(page, nextNode, "When is your birthday?"); + const nodes = page.locator(".card"); await expect(nodes.getByText(questionText)).toBeVisible(); await expect(nodes.getByText(yesBranchNoticeText)).toBeVisible(); @@ -160,6 +167,7 @@ test.describe("Navigation", () => { await expect(nodes.getByText("Checklist item 1")).toBeVisible(); await expect(nodes.getByText("Tell us about your trees.")).toBeVisible(); await expect(nodes.getByText("How old are you?")).toBeVisible(); + await expect(nodes.getByText("When is your birthday?")).toBeVisible(); }); test("Cannot preview an unpublished flow", async ({ diff --git a/e2e/tests/ui-driven/src/create-flow/helpers.ts b/e2e/tests/ui-driven/src/create-flow/helpers.ts index ec8518c0d0..930007e14f 100644 --- a/e2e/tests/ui-driven/src/create-flow/helpers.ts +++ b/e2e/tests/ui-driven/src/create-flow/helpers.ts @@ -113,3 +113,22 @@ export const createNumberInput = async ( .filter({ hasText: "Create number-input" }) .click(); }; + +export const createDateInput = async ( + page: Page, + locatingNode: Locator, + inputTitle: string +) => { + await locatingNode.click(); + await page.getByRole("dialog").waitFor(); + await page.locator("select").selectOption({ label: "Date Input" }); + await page.getByPlaceholder("Title").fill(inputTitle); + await page.locator("id=undefined-min-day").fill("01"); + await page.locator("id=undefined-min-month").fill("01"); + await page.locator("id=undefined-min-year").fill("1800"); + await page.locator("id=undefined-max-day").fill("31"); + await page.locator("id=undefined-max-month").fill("12"); + await page.locator("id=undefined-max-year").fill("2199"); + + await page.locator("button").filter({ hasText: "Create date-input" }).click(); +}; From 1a1b618654f5fb0878590a3f0efefc856fb3ca9b Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Tue, 3 Sep 2024 18:24:14 +0100 Subject: [PATCH 08/39] Prettier --- .../src/create-flow/create-flow.spec.ts | 22 +++++++++---------- .../ui-driven/src/create-flow/helpers.ts | 12 +++++----- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 2d6ee74c07..9868a577e1 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -61,7 +61,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)) + (req) => (isRepeatedRequestMade = isGetUserRequest(req)), ); Promise.all([ @@ -117,7 +117,7 @@ test.describe("Navigation", () => { "No", ]); await expect( - page.locator("a").filter({ hasText: questionText }) + page.locator("a").filter({ hasText: questionText }), ).toBeVisible(); // Add a notice to the "Yes" path @@ -127,7 +127,7 @@ test.describe("Navigation", () => { await createNotice( page, yesBranch.locator(".hanger > a"), - yesBranchNoticeText + yesBranchNoticeText, ); // Add a notice to the "No" path @@ -136,7 +136,7 @@ test.describe("Navigation", () => { await createNotice( page, noBranch.locator(".hanger > a"), - noBranchNoticeText + noBranchNoticeText, ); // add a checklist @@ -181,7 +181,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -215,11 +215,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }) + page.getByRole("heading", { level: 1, name: "Offline" }), ).toBeVisible(); }); @@ -238,7 +238,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully") + page.getByText("Service settings updated successfully"), ).toBeVisible(); // Exit back to main Editor page @@ -261,13 +261,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }) + page.locator("h1", { hasText: "Yes! this is a test" }), ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -275,7 +275,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }) + page.locator("h1", { hasText: "Sorry, this is a test" }), ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/create-flow/helpers.ts b/e2e/tests/ui-driven/src/create-flow/helpers.ts index 930007e14f..8f6d1d5aaa 100644 --- a/e2e/tests/ui-driven/src/create-flow/helpers.ts +++ b/e2e/tests/ui-driven/src/create-flow/helpers.ts @@ -37,7 +37,7 @@ export const createQuestionWithOptions = async ( page: Page, locatingNodeSelector: string, questionText: string, - options: string[] + options: string[], ) => { await page.locator(locatingNodeSelector).click(); await page.getByRole("dialog").waitFor(); @@ -56,7 +56,7 @@ export const createQuestionWithOptions = async ( export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string + noticeText: string, ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -69,7 +69,7 @@ export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[] + checklistOptions: string[], ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -88,7 +88,7 @@ export const createChecklist = async ( export const createTextInput = async ( page: Page, locatingNode: Locator, - inputTitle: string + inputTitle: string, ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -101,7 +101,7 @@ export const createNumberInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputUnits: string + inputUnits: string, ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -117,7 +117,7 @@ export const createNumberInput = async ( export const createDateInput = async ( page: Page, locatingNode: Locator, - inputTitle: string + inputTitle: string, ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); From 414dff471044e4e61707a167097885252ab72aa3 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:18:51 +0100 Subject: [PATCH 09/39] Use uuid for random id --- editor.planx.uk/src/components/Toast/types.ts | 12 ++++++------ editor.planx.uk/src/contexts/ToastContext.tsx | 9 +++++---- editor.planx.uk/src/reducers/toastReducer.ts | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/editor.planx.uk/src/components/Toast/types.ts b/editor.planx.uk/src/components/Toast/types.ts index 99c7e6d614..f45873940a 100644 --- a/editor.planx.uk/src/components/Toast/types.ts +++ b/editor.planx.uk/src/components/Toast/types.ts @@ -1,7 +1,7 @@ export interface Toast { message: string; type: ToastType; - id: number; + id: string; } export type ToastType = "success" | "warning" | "info" | "error"; @@ -9,21 +9,21 @@ export type ToastState = { toasts: Toast[]; }; -export type ToastAction = AddToast | DeleteToast; +export type ToastAction = AddToast | RemoveToast; type AddToast = { type: "ADD_TOAST"; payload: Toast; }; -type DeleteToast = { - type: "DELETE_TOAST"; - payload: { id: number }; +type RemoveToast = { + type: "REMOVE_TOAST"; + payload: { id: string }; }; export type ToastContextType = { addToast: (type: ToastType, message: string) => void; - remove: (id: number) => void; + remove: (id: string) => void; success: (message: string) => void; warning: (message: string) => void; info: (message: string) => void; diff --git a/editor.planx.uk/src/contexts/ToastContext.tsx b/editor.planx.uk/src/contexts/ToastContext.tsx index b340c8428c..a331a9e13d 100644 --- a/editor.planx.uk/src/contexts/ToastContext.tsx +++ b/editor.planx.uk/src/contexts/ToastContext.tsx @@ -7,9 +7,10 @@ import { } from "components/Toast/types"; import React, { createContext, ReactNode, useReducer } from "react"; import { toastReducer } from "reducers/toastReducer"; +import { v4 as uuidv4 } from "uuid"; const defaultCreateContextValue = { - remove: (_id: number) => {}, + remove: (_id: string) => {}, addToast: (_type: ToastType, _message: string) => {}, success: (_message: string) => {}, warning: (_message: string) => {}, @@ -30,11 +31,11 @@ export const ToastContextProvider = ({ }: Readonly<{ children: ReactNode }>) => { const [state, dispatch] = useReducer(toastReducer, initialState); const addToast = (type: ToastType, message: string) => { - const id = Math.floor(Math.random() * 10_000_000); + const id = uuidv4(); dispatch({ type: "ADD_TOAST", payload: { id, message, type } }); }; - const remove = (id: number) => { - dispatch({ type: "DELETE_TOAST", payload: { id } }); + const remove = (id: string) => { + dispatch({ type: "REMOVE_TOAST", payload: { id } }); }; const success = (message: string) => { addToast("success", message); diff --git a/editor.planx.uk/src/reducers/toastReducer.ts b/editor.planx.uk/src/reducers/toastReducer.ts index bf22a221ad..c15986d465 100644 --- a/editor.planx.uk/src/reducers/toastReducer.ts +++ b/editor.planx.uk/src/reducers/toastReducer.ts @@ -7,7 +7,7 @@ export const toastReducer = (state: ToastState, action: ToastAction) => { ...state, toasts: [...state.toasts, action.payload], }; - case "DELETE_TOAST": { + case "REMOVE_TOAST": { const updatedToasts = state.toasts.filter( (toast) => toast.id !== action.payload.id, ); From 71e1a372c9cad1572d596aeb324ee02b7bcebdb6 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Wed, 4 Sep 2024 12:41:26 +0100 Subject: [PATCH 10/39] Address input component --- .../src/create-flow/create-flow.spec.ts | 35 ++++++++++++------- .../ui-driven/src/create-flow/helpers.ts | 31 ++++++++++++---- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 9868a577e1..bdebf0c54f 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -11,6 +11,7 @@ import { createAuthenticatedSession, } from "../globalHelpers"; import { + createAddressInput, createChecklist, createDateInput, createNotice, @@ -61,7 +62,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)), + (req) => (isRepeatedRequestMade = isGetUserRequest(req)) ); Promise.all([ @@ -117,7 +118,7 @@ test.describe("Navigation", () => { "No", ]); await expect( - page.locator("a").filter({ hasText: questionText }), + page.locator("a").filter({ hasText: questionText }) ).toBeVisible(); // Add a notice to the "Yes" path @@ -127,7 +128,7 @@ test.describe("Navigation", () => { await createNotice( page, yesBranch.locator(".hanger > a"), - yesBranchNoticeText, + yesBranchNoticeText ); // Add a notice to the "No" path @@ -136,7 +137,7 @@ test.describe("Navigation", () => { await createNotice( page, noBranch.locator(".hanger > a"), - noBranchNoticeText, + noBranchNoticeText ); // add a checklist @@ -158,7 +159,16 @@ test.describe("Navigation", () => { // add a date input nextNode = page.locator(".hanger > a").nth(9); - createDateInput(page, nextNode, "When is your birthday?"); + await createDateInput(page, nextNode, "When is your birthday?"); + + // add a address input + nextNode = page.locator(".hanger > a").nth(9); + await createAddressInput( + page, + nextNode, + "What is your address?", + "some data field" + ); const nodes = page.locator(".card"); await expect(nodes.getByText(questionText)).toBeVisible(); @@ -168,6 +178,7 @@ test.describe("Navigation", () => { await expect(nodes.getByText("Tell us about your trees.")).toBeVisible(); await expect(nodes.getByText("How old are you?")).toBeVisible(); await expect(nodes.getByText("When is your birthday?")).toBeVisible(); + await expect(nodes.getByText("What is your address?")).toBeVisible(); }); test("Cannot preview an unpublished flow", async ({ @@ -181,7 +192,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -215,11 +226,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }), + page.getByRole("heading", { level: 1, name: "Offline" }) ).toBeVisible(); }); @@ -238,7 +249,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully"), + page.getByText("Service settings updated successfully") ).toBeVisible(); // Exit back to main Editor page @@ -261,13 +272,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }), + page.locator("h1", { hasText: "Yes! this is a test" }) ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -275,7 +286,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }), + page.locator("h1", { hasText: "Sorry, this is a test" }) ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/create-flow/helpers.ts b/e2e/tests/ui-driven/src/create-flow/helpers.ts index 8f6d1d5aaa..c0e6e0fed0 100644 --- a/e2e/tests/ui-driven/src/create-flow/helpers.ts +++ b/e2e/tests/ui-driven/src/create-flow/helpers.ts @@ -37,7 +37,7 @@ export const createQuestionWithOptions = async ( page: Page, locatingNodeSelector: string, questionText: string, - options: string[], + options: string[] ) => { await page.locator(locatingNodeSelector).click(); await page.getByRole("dialog").waitFor(); @@ -56,7 +56,7 @@ export const createQuestionWithOptions = async ( export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string, + noticeText: string ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -69,7 +69,7 @@ export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[], + checklistOptions: string[] ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -88,7 +88,7 @@ export const createChecklist = async ( export const createTextInput = async ( page: Page, locatingNode: Locator, - inputTitle: string, + inputTitle: string ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -101,7 +101,7 @@ export const createNumberInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputUnits: string, + inputUnits: string ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -117,7 +117,7 @@ export const createNumberInput = async ( export const createDateInput = async ( page: Page, locatingNode: Locator, - inputTitle: string, + inputTitle: string ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -132,3 +132,22 @@ export const createDateInput = async ( await page.locator("button").filter({ hasText: "Create date-input" }).click(); }; + +export const createAddressInput = async ( + page: Page, + locatingNode: Locator, + inputTitle: string, + + inputDataField: string +) => { + await locatingNode.click(); + await page.getByRole("dialog").waitFor(); + await page.locator("select").selectOption({ label: "Address Input" }); + await page.getByPlaceholder("Title").fill(inputTitle); + await page.getByPlaceholder("Data Field").fill(inputDataField); + + await page + .locator("button") + .filter({ hasText: "Create address-input" }) + .click(); +}; From 42a5338c958624b54bf90a130f613d0e93d09861 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Wed, 4 Sep 2024 12:44:08 +0100 Subject: [PATCH 11/39] Add contact input --- .../src/create-flow/create-flow.spec.ts | 16 +++++++++++++-- .../ui-driven/src/create-flow/helpers.ts | 20 ++++++++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index bdebf0c54f..eaa8d5d6cd 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -13,6 +13,7 @@ import { import { createAddressInput, createChecklist, + createContactInput, createDateInput, createNotice, createNumberInput, @@ -161,8 +162,8 @@ test.describe("Navigation", () => { nextNode = page.locator(".hanger > a").nth(9); await createDateInput(page, nextNode, "When is your birthday?"); - // add a address input - nextNode = page.locator(".hanger > a").nth(9); + // add an address input + nextNode = page.locator(".hanger > a").nth(10); await createAddressInput( page, nextNode, @@ -170,6 +171,15 @@ test.describe("Navigation", () => { "some data field" ); + // add a contact input + nextNode = page.locator(".hanger > a").nth(11); + await createContactInput( + page, + nextNode, + "What is your contact info?", + "some data field" + ); + const nodes = page.locator(".card"); await expect(nodes.getByText(questionText)).toBeVisible(); await expect(nodes.getByText(yesBranchNoticeText)).toBeVisible(); @@ -179,6 +189,8 @@ test.describe("Navigation", () => { await expect(nodes.getByText("How old are you?")).toBeVisible(); await expect(nodes.getByText("When is your birthday?")).toBeVisible(); await expect(nodes.getByText("What is your address?")).toBeVisible(); + await expect(nodes.getByText("What is your contact info?")).toBeVisible(); + }); test("Cannot preview an unpublished flow", async ({ diff --git a/e2e/tests/ui-driven/src/create-flow/helpers.ts b/e2e/tests/ui-driven/src/create-flow/helpers.ts index c0e6e0fed0..7bb77a9c6e 100644 --- a/e2e/tests/ui-driven/src/create-flow/helpers.ts +++ b/e2e/tests/ui-driven/src/create-flow/helpers.ts @@ -137,7 +137,6 @@ export const createAddressInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string ) => { await locatingNode.click(); @@ -151,3 +150,22 @@ export const createAddressInput = async ( .filter({ hasText: "Create address-input" }) .click(); }; + +export const createContactInput = async ( + page: Page, + locatingNode: Locator, + inputTitle: string, + inputDataField: string +) => { + await locatingNode.click(); + await page.getByRole("dialog").waitFor(); + await page.locator("select").selectOption({ label: "Contact Input" }); + await page.getByPlaceholder("Title").fill(inputTitle); + await page.getByPlaceholder("Data Field").fill(inputDataField); + + await page + .locator("button") + .filter({ hasText: "Create contact-input" }) + .click(); +}; + From a4458b5f4b4e7a27af0de1f6aed87b24821aaa6b Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Wed, 4 Sep 2024 12:53:23 +0100 Subject: [PATCH 12/39] Restructure helper files --- e2e/tests/api-driven/src/globalHelpers.ts | 14 +- .../api-driven/src/invite-to-pay/helpers.ts | 26 +-- .../src/create-flow/create-flow.spec.ts | 26 ++- .../helpers.ts => helpers/addComponent.ts} | 36 +---- .../ui-driven/src/{ => helpers}/context.ts | 52 +++--- e2e/tests/ui-driven/src/helpers/getPage.ts | 31 ++++ .../ui-driven/src/helpers/globalHelpers.ts | 142 +++++++++++++++++ .../userActions.ts} | 149 +----------------- .../ui-driven/src/invite-to-pay/agent.spec.ts | 34 ++-- .../ui-driven/src/invite-to-pay/helpers.ts | 20 ++- .../ui-driven/src/invite-to-pay/mocks.ts | 2 +- .../src/invite-to-pay/nominee.spec.ts | 27 ++-- e2e/tests/ui-driven/src/login.spec.ts | 10 +- e2e/tests/ui-driven/src/pay.spec.ts | 59 ++++--- .../ui-driven/src/save-and-return.spec.ts | 22 +-- e2e/tests/ui-driven/src/sections.spec.ts | 26 +-- 16 files changed, 339 insertions(+), 337 deletions(-) rename e2e/tests/ui-driven/src/{create-flow/helpers.ts => helpers/addComponent.ts} (83%) rename e2e/tests/ui-driven/src/{ => helpers}/context.ts (92%) create mode 100644 e2e/tests/ui-driven/src/helpers/getPage.ts create mode 100644 e2e/tests/ui-driven/src/helpers/globalHelpers.ts rename e2e/tests/ui-driven/src/{globalHelpers.ts => helpers/userActions.ts} (57%) diff --git a/e2e/tests/api-driven/src/globalHelpers.ts b/e2e/tests/api-driven/src/globalHelpers.ts index 88a0cafcb9..a735298e27 100644 --- a/e2e/tests/api-driven/src/globalHelpers.ts +++ b/e2e/tests/api-driven/src/globalHelpers.ts @@ -1,8 +1,8 @@ -import { TEST_EMAIL } from "../../ui-driven/src/globalHelpers"; +import { TEST_EMAIL } from "../../ui-driven/src/helpers/globalHelpers"; import { $admin } from "./client"; export function createTeam( - args?: Partial[0]>, + args?: Partial[0]> ) { return safely(() => $admin.team.create({ @@ -13,12 +13,12 @@ export function createTeam( homepage: "http://www.planx.uk", }, ...args, - }), + }) ); } export function createUser( - args?: Partial[0]>, + args?: Partial[0]> ) { return safely(() => $admin.user.create({ @@ -26,18 +26,18 @@ export function createUser( lastName: "Test", email: TEST_EMAIL, ...args, - }), + }) ); } export function createFlow( - args: Omit[0], "data">, + args: Omit[0], "data"> ) { return safely(() => $admin.flow.create({ data: { dummy: "flowData " }, ...args, - }), + }) ); } diff --git a/e2e/tests/api-driven/src/invite-to-pay/helpers.ts b/e2e/tests/api-driven/src/invite-to-pay/helpers.ts index d8d2db0acf..3102dc9c17 100644 --- a/e2e/tests/api-driven/src/invite-to-pay/helpers.ts +++ b/e2e/tests/api-driven/src/invite-to-pay/helpers.ts @@ -1,20 +1,20 @@ -import { CustomWorld } from "./steps"; -import axios from "axios"; -import { readFileSync } from "node:fs"; import type { FlowGraph, PaymentRequest, } from "@opensystemslab/planx-core/types"; +import axios from "axios"; +import gql from "graphql-tag"; +import { readFileSync } from "node:fs"; +import { TEST_EMAIL } from "../../../ui-driven/src/helpers/globalHelpers"; +import { $admin } from "../client"; +import { createTeam, createUser } from "../globalHelpers"; import { inviteToPayFlowGraph, - sendNodeWithDestination, mockBreadcrumbs, mockPassport, + sendNodeWithDestination, } from "./mocks"; -import { $admin } from "../client"; -import { TEST_EMAIL } from "../../../ui-driven/src/globalHelpers"; -import { createTeam, createUser } from "../globalHelpers"; -import gql from "graphql-tag"; +import { CustomWorld } from "./steps"; export async function setUpMocks() { const serverMockFile = readFileSync(`${__dirname}/mocks/server-mocks.yaml`); @@ -67,7 +67,7 @@ export async function buildSessionForFlow(flowId: string): Promise { } export async function buildPaymentRequestForSession( - sessionId: string, + sessionId: string ): Promise { await $admin.session.lock(sessionId); return $admin.paymentRequest.create({ @@ -80,14 +80,14 @@ export async function buildPaymentRequestForSession( } export async function markPaymentRequestAsPaid( - paymentRequestId: string, + paymentRequestId: string ): Promise { return await $admin.paymentRequest._markAsPaid(paymentRequestId); } export async function getSendResponse( destination: string, - sessionId: string, + sessionId: string ): Promise { switch (destination.toLowerCase()) { case "bops": @@ -127,7 +127,7 @@ export async function waitForResponse({ } export async function getSessionSubmittedAt( - sessionId: string, + sessionId: string ): Promise { const detailedSession = await $admin.session.findDetails(sessionId); return detailedSession?.submittedAt; @@ -180,7 +180,7 @@ const setupMockBopsSubmissionUrl = async (teamId: number) => { { teamId, stagingBopsSubmissionUrl: "http://mock-server:8080", - }, + } ); }; diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index eaa8d5d6cd..938fe9d82e 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -1,15 +1,4 @@ import { Browser, expect, test } from "@playwright/test"; -import type { Context } from "../context"; -import { - contextDefaults, - setUpTestContext, - tearDownTestContext, -} from "../context"; -import { - answerQuestion, - clickContinue, - createAuthenticatedSession, -} from "../globalHelpers"; import { createAddressInput, createChecklist, @@ -19,9 +8,19 @@ import { createNumberInput, createQuestionWithOptions, createTextInput, - getTeamPage, +} from "../helpers/addComponent"; +import type { Context } from "../helpers/context"; +import { + contextDefaults, + setUpTestContext, + tearDownTestContext, +} from "../helpers/context"; +import { getTeamPage } from "../helpers/getPage"; +import { + createAuthenticatedSession, isGetUserRequest, -} from "./helpers"; +} from "../helpers/globalHelpers"; +import { answerQuestion, clickContinue } from "../helpers/userActions"; test.describe("Navigation", () => { let context: Context = { @@ -190,7 +189,6 @@ test.describe("Navigation", () => { await expect(nodes.getByText("When is your birthday?")).toBeVisible(); await expect(nodes.getByText("What is your address?")).toBeVisible(); await expect(nodes.getByText("What is your contact info?")).toBeVisible(); - }); test("Cannot preview an unpublished flow", async ({ diff --git a/e2e/tests/ui-driven/src/create-flow/helpers.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts similarity index 83% rename from e2e/tests/ui-driven/src/create-flow/helpers.ts rename to e2e/tests/ui-driven/src/helpers/addComponent.ts index 7bb77a9c6e..2a389e63f3 100644 --- a/e2e/tests/ui-driven/src/create-flow/helpers.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -1,37 +1,4 @@ -import { Browser, Locator, Page, Request } from "@playwright/test"; -import { createAuthenticatedSession } from "../globalHelpers"; - -export const isGetUserRequest = (req: Request) => - req.url().includes("/user/me"); - -export async function getAdminPage({ - browser, - userId, -}: { - browser: Browser; - userId: number; -}): Promise { - const page = await createAuthenticatedSession({ browser, userId }); - await page.goto("/"); - await page.waitForResponse((response) => { - return response.url().includes("/graphql"); - }); - return page; -} - -export async function getTeamPage({ - browser, - userId, - teamName, -}: { - browser: Browser; - userId: number; - teamName: string; -}): Promise { - const page = await getAdminPage({ browser, userId }); - await page.locator("h3", { hasText: teamName }).click(); - return page; -} +import { Locator, Page } from "@playwright/test"; export const createQuestionWithOptions = async ( page: Page, @@ -168,4 +135,3 @@ export const createContactInput = async ( .filter({ hasText: "Create contact-input" }) .click(); }; - diff --git a/e2e/tests/ui-driven/src/context.ts b/e2e/tests/ui-driven/src/helpers/context.ts similarity index 92% rename from e2e/tests/ui-driven/src/context.ts rename to e2e/tests/ui-driven/src/helpers/context.ts index 7f08ccf25f..dde360b9d5 100644 --- a/e2e/tests/ui-driven/src/context.ts +++ b/e2e/tests/ui-driven/src/helpers/context.ts @@ -1,8 +1,8 @@ -import assert from "node:assert"; -import { log } from "./globalHelpers"; -import { sign } from "jsonwebtoken"; import { CoreDomainClient } from "@opensystemslab/planx-core"; import { GraphQLClient, gql } from "graphql-request"; +import { sign } from "jsonwebtoken"; +import assert from "node:assert"; +import { log } from "./globalHelpers"; type NewTeam = Parameters[0]; @@ -47,7 +47,7 @@ export const contextDefaults: Context = { }; export async function setUpTestContext( - initialContext: Context, + initialContext: Context ): Promise { const $admin = getCoreDomainClient(); const context: Context = { ...initialContext }; @@ -117,7 +117,7 @@ export function generateAuthenticationToken(userId: string) { "x-hasura-user-id": `${userId}`, }, }, - process.env.JWT_SECRET, + process.env.JWT_SECRET ); } @@ -127,7 +127,7 @@ export function getCoreDomainClient(): CoreDomainClient { const API = process.env.HASURA_GRAPHQL_URL!.replace( "${HASURA_PROXY_PORT}", - process.env.HASURA_PROXY_PORT!, + process.env.HASURA_PROXY_PORT! ); const SECRET = process.env.HASURA_GRAPHQL_ADMIN_SECRET!; return new CoreDomainClient({ @@ -142,7 +142,7 @@ export function getGraphQLClient(): GraphQLClient { export async function findSessionId( adminGQLClient: GraphQLClient, - context, + context ): Promise { // get the flow id which may have a session const flowResponse: { flows: { id: string }[] } = @@ -152,7 +152,7 @@ export async function findSessionId( id } }`, - { slug: context.flow?.slug }, + { slug: context.flow?.slug } ); if (!flowResponse.flows.length || !flowResponse.flows[0].id) { return; @@ -172,7 +172,7 @@ export async function findSessionId( } } `, - { flowId, email: context.user?.email }, + { flowId, email: context.user?.email } ); if (response.lowcal_sessions.length && response.lowcal_sessions[0].id) { return response.lowcal_sessions[0].id; @@ -188,7 +188,7 @@ async function deleteSession(adminGQLClient: GraphQLClient, context) { id } }`, - { sessionId }, + { sessionId } ); } } @@ -201,14 +201,14 @@ async function deleteSession(adminGQLClient: GraphQLClient, context) { id } }`, - { sessionId }, + { sessionId } ); } } async function deletePublishedFlow( adminGQLClient: GraphQLClient, - context: Context, + context: Context ) { if (context.flow?.publishedId) { log(`deleting published flow ${context.flow?.publishedId}`); @@ -218,7 +218,7 @@ async function deletePublishedFlow( id } }`, - { publishedFlowId: context.flow?.publishedId }, + { publishedFlowId: context.flow?.publishedId } ); } } @@ -232,7 +232,7 @@ async function deleteFlow(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { flowId: context.flow?.id }, + { flowId: context.flow?.id } ); } else if (context.flow?.slug) { // try deleting via slug (when cleaning up from a previously failed test) @@ -242,11 +242,11 @@ async function deleteFlow(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { slug: context.flow?.slug }, + { slug: context.flow?.slug } ); if (response.flows.length && response.flows[0].id) { log( - `deleting flow ${context.flow?.slug} flowId: ${response.flows[0].id}`, + `deleting flow ${context.flow?.slug} flowId: ${response.flows[0].id}` ); await adminGQLClient.request( `mutation DeleteTestFlow( $flowId: uuid!) { @@ -254,7 +254,7 @@ async function deleteFlow(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { flowId: response.flows[0].id }, + { flowId: response.flows[0].id } ); } } @@ -269,7 +269,7 @@ async function deleteUser(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { userId: context.user?.id }, + { userId: context.user?.id } ); } else if (context.user?.email) { // try deleting via email (when cleaning up from a previously failed test) @@ -279,11 +279,11 @@ async function deleteUser(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { email: context.user?.email }, + { email: context.user?.email } ); if (response.users.length && response.users[0].id) { log( - `deleting user ${context.user?.email} userId: ${response.users[0].id}`, + `deleting user ${context.user?.email} userId: ${response.users[0].id}` ); await adminGQLClient.request( `mutation DeleteTestUser($userId: Int!) { @@ -291,7 +291,7 @@ async function deleteUser(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { userId: response.users[0].id }, + { userId: response.users[0].id } ); } } @@ -306,7 +306,7 @@ async function deleteTeam(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { teamId: context.team?.id }, + { teamId: context.team?.id } ); } else if (context.team?.slug) { // try deleting via slug (when cleaning up from a previously failed test) @@ -316,11 +316,11 @@ async function deleteTeam(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { slug: context.team?.slug }, + { slug: context.team?.slug } ); if (response.teams.length && response.teams[0].id) { log( - `deleting team ${context.team?.slug} teamId: ${response.teams[0].id}`, + `deleting team ${context.team?.slug} teamId: ${response.teams[0].id}` ); await adminGQLClient.request( `mutation DeleteTestTeam( $teamId: Int!) { @@ -328,7 +328,7 @@ async function deleteTeam(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { teamId: response.teams[0].id }, + { teamId: response.teams[0].id } ); } } @@ -353,7 +353,7 @@ async function setupGovPaySecret($admin: CoreDomainClient, context: Context) { { team_id: context.team.id, staging_govpay_secret: process.env.GOV_UK_PAY_SECRET_E2E, - }, + } ); } catch (error) { throw Error("Failed to setup GovPay secret for E2E team"); diff --git a/e2e/tests/ui-driven/src/helpers/getPage.ts b/e2e/tests/ui-driven/src/helpers/getPage.ts new file mode 100644 index 0000000000..8d9880c12e --- /dev/null +++ b/e2e/tests/ui-driven/src/helpers/getPage.ts @@ -0,0 +1,31 @@ +import { Browser, Page } from "@playwright/test"; +import { createAuthenticatedSession } from "./globalHelpers"; + +export async function getAdminPage({ + browser, + userId, +}: { + browser: Browser; + userId: number; +}): Promise { + const page = await createAuthenticatedSession({ browser, userId }); + await page.goto("/"); + await page.waitForResponse((response) => { + return response.url().includes("/graphql"); + }); + return page; +} + +export async function getTeamPage({ + browser, + userId, + teamName, +}: { + browser: Browser; + userId: number; + teamName: string; +}): Promise { + const page = await getAdminPage({ browser, userId }); + await page.locator("h3", { hasText: teamName }).click(); + return page; +} diff --git a/e2e/tests/ui-driven/src/helpers/globalHelpers.ts b/e2e/tests/ui-driven/src/helpers/globalHelpers.ts new file mode 100644 index 0000000000..4fa4fb14d0 --- /dev/null +++ b/e2e/tests/ui-driven/src/helpers/globalHelpers.ts @@ -0,0 +1,142 @@ +import { FlowGraph } from "@opensystemslab/planx-core/types"; +import type { Browser, Page, Request } from "@playwright/test"; +import { gql } from "graphql-request"; +import type { Context } from "./context"; +import { generateAuthenticationToken, getGraphQLClient } from "./context"; + +// Test card numbers to be used in gov.uk sandbox environment +// reference: https://docs.payments.service.gov.uk/testing_govuk_pay/#if-you-39-re-using-a-test-39-sandbox-39-account +export const cards = { + successful_card_number: "4444333322221111", + invalid_card_number: "4000000000000002", +}; + +// Gov.uk Notify requests testing service use smoke test email addresses +// see https://docs.notifications.service.gov.uk/rest-api.html#smoke-testing +export const TEST_EMAIL = + "simulate-delivered@notifications.service.gov.uk" as const; + +// utility functions + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function log(...args: any[]) { + process.env.DEBUG_LOG && console.log(...args); +} + +// a collection of useful playwright actions +// these could evolve into fixtures: https://playwright.dev/docs/test-fixtures + +export function debugPageConsole(page: Page) { + page.on("console", (msg) => console.log(msg.text())); +} + +// used to detect `{ "setItem": ... }`, `{"getItem": ... }` +// and "section state updated" debug messages on state transitions +export async function waitForDebugLog(page: Page) { + return new Promise((resolve) => { + page.on("console", (msg) => { + if (msg.type() == "debug") { + resolve(true); + } + }); + }); +} + +export async function createAuthenticatedSession({ + browser, + userId, +}: { + browser: Browser; + userId: number; +}): Promise { + const browserContext = await browser.newContext(); + const page = await browserContext.newPage(); + const token = generateAuthenticationToken(`${userId}`); + await browserContext.addCookies([ + { + name: "jwt", + domain: "localhost", + path: "/", + value: token, + }, + { + name: "auth", + domain: "localhost", + path: "/", + value: JSON.stringify({ loggedIn: true }), + }, + ]); + return page; +} + +export async function setFeatureFlag(page: Page, featureFlag: string) { + await page.addInitScript( + (featureFlag: string) => + window.localStorage.setItem( + "FEATURE_FLAGS", + JSON.stringify([featureFlag]) + ), + featureFlag + ); +} + +export async function getSessionId(page: Page): Promise { + // @ts-expect-error - Property api does not exist on type Window & typeof globalThis + const sessionId = page.evaluate(() => window.api.getState().sessionId); + if (!sessionId) throw Error("Session ID missing from window"); + return sessionId; +} + +export async function addSessionToContext(page: Page, context: Context) { + const sessionId = await getSessionId(page); + context.sessionIds!.push(sessionId); + return sessionId; +} + +export async function waitForPaymentResponse( + page: Page, + context: Context +): Promise<{ paymentId: string; state?: { status: string } }> { + const { payment_id: paymentId, state } = await page + .waitForResponse((response) => { + return response.url().includes(`pay/${context.team!.slug!}`); + }) + .then((req) => req.json()); + if (!paymentId) throw new Error("Bad payment response"); + return { paymentId, state }; +} + +export async function modifyFlow({ + context, + modifiedFlow, +}: { + context: Context; + modifiedFlow: FlowGraph; +}) { + const adminGQLClient = getGraphQLClient(); + if (!context.flow?.id || !context.user?.id) { + throw new Error("context must have a flow and user"); + } + await adminGQLClient.request( + gql` + mutation UpdateTestFlow($flowId: uuid!, $userId: Int!, $data: jsonb!) { + update_flows_by_pk(pk_columns: { id: $flowId }, _set: { data: $data }) { + id + data + } + insert_published_flows_one( + object: { flow_id: $flowId, data: $data, publisher_id: $userId } + ) { + id + } + } + `, + { + flowId: context.flow!.id, + userId: context.user!.id, + data: modifiedFlow, + } + ); +} +export const isGetUserRequest = (req: Request) => + req.url().includes("/user/me"); diff --git a/e2e/tests/ui-driven/src/globalHelpers.ts b/e2e/tests/ui-driven/src/helpers/userActions.ts similarity index 57% rename from e2e/tests/ui-driven/src/globalHelpers.ts rename to e2e/tests/ui-driven/src/helpers/userActions.ts index 56331da06b..613507beaa 100644 --- a/e2e/tests/ui-driven/src/globalHelpers.ts +++ b/e2e/tests/ui-driven/src/helpers/userActions.ts @@ -1,76 +1,9 @@ -import { mockOSPlacesResponse } from "./mocks/osPlacesResponse"; +import type { Locator, Page } from "@playwright/test"; import { expect } from "@playwright/test"; -import type { Page, Browser, Locator } from "@playwright/test"; -import { findSessionId, generateAuthenticationToken } from "./context"; +import { mockOSPlacesResponse } from "../mocks/osPlacesResponse"; import type { Context } from "./context"; -import { getGraphQLClient } from "./context"; -import { gql } from "graphql-request"; -import { FlowGraph } from "@opensystemslab/planx-core/types"; - -// Test card numbers to be used in gov.uk sandbox environment -// reference: https://docs.payments.service.gov.uk/testing_govuk_pay/#if-you-39-re-using-a-test-39-sandbox-39-account -export const cards = { - successful_card_number: "4444333322221111", - invalid_card_number: "4000000000000002", -}; - -// Gov.uk Notify requests testing service use smoke test email addresses -// see https://docs.notifications.service.gov.uk/rest-api.html#smoke-testing -export const TEST_EMAIL = - "simulate-delivered@notifications.service.gov.uk" as const; - -// utility functions - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function log(...args: any[]) { - process.env.DEBUG_LOG && console.log(...args); -} - -// a collection of useful playwright actions -// these could evolve into fixtures: https://playwright.dev/docs/test-fixtures - -export function debugPageConsole(page: Page) { - page.on("console", (msg) => console.log(msg.text())); -} - -// used to detect `{ "setItem": ... }`, `{"getItem": ... }` -// and "section state updated" debug messages on state transitions -export async function waitForDebugLog(page: Page) { - return new Promise((resolve) => { - page.on("console", (msg) => { - if (msg.type() == "debug") { - resolve(true); - } - }); - }); -} - -export async function createAuthenticatedSession({ - browser, - userId, -}: { - browser: Browser; - userId: number; -}): Promise { - const browserContext = await browser.newContext(); - const page = await browserContext.newPage(); - const token = generateAuthenticationToken(`${userId}`); - await browserContext.addCookies([ - { - name: "jwt", - domain: "localhost", - path: "/", - value: token, - }, - { - name: "auth", - domain: "localhost", - path: "/", - value: JSON.stringify({ loggedIn: true }), - }, - ]); - return page; -} +import { findSessionId, getGraphQLClient } from "./context"; +import { TEST_EMAIL, log, waitForDebugLog } from "./globalHelpers"; export async function saveSession({ page, @@ -270,7 +203,7 @@ export async function answerFindProperty(page: Page) { async function setupOSMockResponse(page: Page) { const ordnanceSurveryPlacesEndpoint = new RegExp( - /proxy\/ordnance-survey\/search\/places\/v1\/postcode\/*/, + /proxy\/ordnance-survey\/search\/places\/v1\/postcode\/*/ ); await page.route(ordnanceSurveryPlacesEndpoint, async (route) => { await route.fulfill({ @@ -292,80 +225,10 @@ export async function answerContactInput( lastName: string; phoneNumber: string; email: string; - }, + } ) { await page.getByLabel("First name").fill(firstName); await page.getByLabel("Last name").fill(lastName); await page.getByLabel("Phone number").fill(phoneNumber); await page.getByLabel("Email address").fill(email); } - -export async function setFeatureFlag(page: Page, featureFlag: string) { - await page.addInitScript( - (featureFlag: string) => - window.localStorage.setItem( - "FEATURE_FLAGS", - JSON.stringify([featureFlag]), - ), - featureFlag, - ); -} - -export async function getSessionId(page: Page): Promise { - // @ts-expect-error - Property api does not exist on type Window & typeof globalThis - const sessionId = page.evaluate(() => window.api.getState().sessionId); - if (!sessionId) throw Error("Session ID missing from window"); - return sessionId; -} - -export async function addSessionToContext(page: Page, context: Context) { - const sessionId = await getSessionId(page); - context.sessionIds!.push(sessionId); - return sessionId; -} - -export async function waitForPaymentResponse( - page: Page, - context: Context, -): Promise<{ paymentId: string; state?: { status: string } }> { - const { payment_id: paymentId, state } = await page - .waitForResponse((response) => { - return response.url().includes(`pay/${context.team!.slug!}`); - }) - .then((req) => req.json()); - if (!paymentId) throw new Error("Bad payment response"); - return { paymentId, state }; -} - -export async function modifyFlow({ - context, - modifiedFlow, -}: { - context: Context; - modifiedFlow: FlowGraph; -}) { - const adminGQLClient = getGraphQLClient(); - if (!context.flow?.id || !context.user?.id) { - throw new Error("context must have a flow and user"); - } - await adminGQLClient.request( - gql` - mutation UpdateTestFlow($flowId: uuid!, $userId: Int!, $data: jsonb!) { - update_flows_by_pk(pk_columns: { id: $flowId }, _set: { data: $data }) { - id - data - } - insert_published_flows_one( - object: { flow_id: $flowId, data: $data, publisher_id: $userId } - ) { - id - } - } - `, - { - flowId: context.flow!.id, - userId: context.user!.id, - data: modifiedFlow, - }, - ); -} diff --git a/e2e/tests/ui-driven/src/invite-to-pay/agent.spec.ts b/e2e/tests/ui-driven/src/invite-to-pay/agent.spec.ts index 7442170cf2..77ea290944 100644 --- a/e2e/tests/ui-driven/src/invite-to-pay/agent.spec.ts +++ b/e2e/tests/ui-driven/src/invite-to-pay/agent.spec.ts @@ -1,13 +1,18 @@ -import { test, expect, Page, BrowserContext } from "@playwright/test"; -import { addSessionToContext, modifyFlow } from "../globalHelpers"; -import inviteToPayFlow from "../mocks/flows/invite-to-pay-flow"; +import { BrowserContext, Page, expect, test } from "@playwright/test"; import { Context, contextDefaults, getGraphQLClient, setUpTestContext, tearDownTestContext, -} from "../context"; +} from "../helpers/context"; +import { addSessionToContext, modifyFlow } from "../helpers/globalHelpers"; +import { + clickContinue, + returnToSession, + saveSession, +} from "../helpers/userActions"; +import inviteToPayFlow from "../mocks/flows/invite-to-pay-flow"; import { answerInviteToPayForm, getPaymentRequestBySessionId, @@ -15,7 +20,6 @@ import { navigateToPayComponent, } from "./helpers"; import { mockPaymentRequest, modifiedInviteToPayFlow } from "./mocks"; -import { saveSession, returnToSession, clickContinue } from "../globalHelpers"; let context: Context = { ...contextDefaults, @@ -52,7 +56,7 @@ test.describe("Agent journey @regression", async () => { await expect(toggleInviteToPayButton).toBeVisible(); await toggleInviteToPayButton.click(); const inviteToPayFormHeader = page.getByText( - "Invite someone else to pay for this application", + "Invite someone else to pay for this application" ); await expect(inviteToPayFormHeader).toBeVisible(); @@ -61,7 +65,7 @@ test.describe("Agent journey @regression", async () => { await page.waitForResponse((res) => res.url().includes("invite-to-pay")); const errorMessage = page.getByText( - "Error generating payment request, please try again", + "Error generating payment request, please try again" ); await expect(errorMessage).toBeHidden(); @@ -109,7 +113,7 @@ test.describe("Agent journey @regression", async () => { await expect( secondPage.getByRole("heading", { name: "Resume your application", - }), + }) ).toBeVisible(); await secondPage.getByLabel("Email address").fill(context.user.email); await secondPage.getByTestId("continue-button").click(); @@ -117,7 +121,7 @@ test.describe("Agent journey @regression", async () => { await expect( secondPage.getByRole("heading", { name: "Sorry, you can't make changes to this application", - }), + }) ).toBeVisible(); await expect(secondPage.getByTestId("continue-button")).toBeHidden(); }); @@ -139,14 +143,14 @@ test.describe("Agent journey @regression", async () => { await expect( secondPage.getByRole("heading", { name: "Resume your application", - }), + }) ).toBeVisible(); await secondPage.getByLabel("Email address").fill(context.user.email); await secondPage.getByTestId("continue-button").click(); // Reconciliation ignored const reconciliationText = secondPage.getByText( - "This service has been updated since you last saved your application. We will ask you to answer any updated questions again when you continue.", + "This service has been updated since you last saved your application. We will ask you to answer any updated questions again when you continue." ); await expect(reconciliationText).toBeHidden(); @@ -154,7 +158,7 @@ test.describe("Agent journey @regression", async () => { await expect( secondPage.getByRole("heading", { name: "Sorry, you can't make changes to this application", - }), + }) ).toBeVisible(); await expect(secondPage.getByTestId("continue-button")).toBeHidden(); }); @@ -173,7 +177,7 @@ test.describe("Agent journey @regression", async () => { await answerInviteToPayForm(tab1); await tab1.getByRole("button", { name: "Send invitation to pay" }).click(); await tab1.waitForResponse( - (resp) => resp.url().includes("/v1/graphql") && resp.status() === 200, + (resp) => resp.url().includes("/v1/graphql") && resp.status() === 200 ); const paymentRequest = await getPaymentRequestBySessionId({ sessionId, @@ -186,7 +190,7 @@ test.describe("Agent journey @regression", async () => { // ...and fail to do so const errorMessage = tab2.getByText( - "Cannot initialise a new payment for locked session", + "Cannot initialise a new payment for locked session" ); await expect(errorMessage).toBeVisible(); }); @@ -213,7 +217,7 @@ test.describe("Agent journey @regression", async () => { // ...and fail to do so const errorMessage = tab2.getByText( - "Error generating payment request, please try again", + "Error generating payment request, please try again" ); await expect(errorMessage).toBeVisible(); }); diff --git a/e2e/tests/ui-driven/src/invite-to-pay/helpers.ts b/e2e/tests/ui-driven/src/invite-to-pay/helpers.ts index 36e60c26ec..5668542b74 100644 --- a/e2e/tests/ui-driven/src/invite-to-pay/helpers.ts +++ b/e2e/tests/ui-driven/src/invite-to-pay/helpers.ts @@ -1,16 +1,14 @@ +import { PaymentRequest } from "@opensystemslab/planx-core/dist/types"; +import type { Page } from "@playwright/test"; +import { GraphQLClient, gql } from "graphql-request"; +import { Context } from "../helpers/context"; +import { TEST_EMAIL, addSessionToContext, log } from "../helpers/globalHelpers"; import { answerChecklist, - log, - answerFindProperty, answerContactInput, - addSessionToContext, - TEST_EMAIL, -} from "../globalHelpers"; -import type { Page } from "@playwright/test"; -import { gql, GraphQLClient } from "graphql-request"; -import { fillInEmail } from "../globalHelpers"; -import { PaymentRequest } from "@opensystemslab/planx-core/dist/types"; -import { Context } from "../context"; + answerFindProperty, + fillInEmail, +} from "../helpers/userActions"; /** * Navigates to pay component whilst completing the minimum requirements for an Invite to Pay flow @@ -73,7 +71,7 @@ export async function getPaymentRequestBySessionId({ } } `, - { sessionId }, + { sessionId } ); return paymentRequests[0]; } catch (e) { diff --git a/e2e/tests/ui-driven/src/invite-to-pay/mocks.ts b/e2e/tests/ui-driven/src/invite-to-pay/mocks.ts index f9f64e1956..0006a617a5 100644 --- a/e2e/tests/ui-driven/src/invite-to-pay/mocks.ts +++ b/e2e/tests/ui-driven/src/invite-to-pay/mocks.ts @@ -4,8 +4,8 @@ import { PaymentRequest, SessionData, } from "@opensystemslab/planx-core/types"; +import { TEST_EMAIL } from "../helpers/globalHelpers"; import inviteToPayFlow from "../mocks/flows/invite-to-pay-flow"; -import { TEST_EMAIL } from "../globalHelpers"; export const mockPaymentRequest: Partial = { payeeEmail: TEST_EMAIL, diff --git a/e2e/tests/ui-driven/src/invite-to-pay/nominee.spec.ts b/e2e/tests/ui-driven/src/invite-to-pay/nominee.spec.ts index 1d2276a048..47d003e3a5 100644 --- a/e2e/tests/ui-driven/src/invite-to-pay/nominee.spec.ts +++ b/e2e/tests/ui-driven/src/invite-to-pay/nominee.spec.ts @@ -1,18 +1,19 @@ -import { test, expect, Page, APIRequestContext } from "@playwright/test"; +import { PaymentRequest, Session } from "@opensystemslab/planx-core/types"; +import { APIRequestContext, Page, expect, test } from "@playwright/test"; +import { GraphQLClient, gql } from "graphql-request"; import { v4 as uuidV4 } from "uuid"; -import { fillGovUkCardDetails, cards } from "../globalHelpers"; -import inviteToPayFlow from "../mocks/flows/invite-to-pay-flow"; import { Context, contextDefaults, getGraphQLClient, setUpTestContext, tearDownTestContext, -} from "../context"; -import { mockPaymentRequestDetails, mockSessionData } from "./mocks"; -import { GraphQLClient, gql } from "graphql-request"; -import { PaymentRequest, Session } from "@opensystemslab/planx-core/types"; +} from "../helpers/context"; +import { cards } from "../helpers/globalHelpers"; +import { fillGovUkCardDetails } from "../helpers/userActions"; +import inviteToPayFlow from "../mocks/flows/invite-to-pay-flow"; import { getPaymentRequestBySessionId } from "./helpers"; +import { mockPaymentRequestDetails, mockSessionData } from "./mocks"; let context: Context = { ...contextDefaults, @@ -48,10 +49,10 @@ test.describe("Nominee journey @regression", async () => { await navigateToPaymentRequestPage(paymentRequest, page); await expect( - page.getByRole("heading", { name: "Pay for your application" }), + page.getByRole("heading", { name: "Pay for your application" }) ).toBeVisible(); await expect( - page.locator("#main-content").getByText("Invite to pay test"), + page.locator("#main-content").getByText("Invite to pay test") ).toBeVisible(); await expect(page.getByText("123, Test Street, Testville")).toBeVisible(); @@ -123,7 +124,7 @@ test.describe("Nominee journey @regression", async () => { async function navigateToPaymentRequestPage( paymentRequest: PaymentRequest, - page: Page, + page: Page ) { const paymentRequestURL = `/${context.team!.slug!}/${context.flow! .slug!}/pay?analytics=false&paymentRequestId=${paymentRequest.id}`; @@ -132,7 +133,7 @@ async function navigateToPaymentRequestPage( } async function setupPaymentRequest( - request: APIRequestContext, + request: APIRequestContext ): Promise< Record<"paymentRequest", PaymentRequest> & Record<"sessionId", string> > { @@ -184,13 +185,13 @@ async function createSession({ */ async function createPaymentRequest( request: APIRequestContext, - sessionId: string, + sessionId: string ) { const response = await request.post( `http://localhost:${process.env.API_PORT}/invite-to-pay/${sessionId}`, { data: mockPaymentRequestDetails, - }, + } ); return response.json(); } diff --git a/e2e/tests/ui-driven/src/login.spec.ts b/e2e/tests/ui-driven/src/login.spec.ts index 62481fc98c..160471a506 100644 --- a/e2e/tests/ui-driven/src/login.spec.ts +++ b/e2e/tests/ui-driven/src/login.spec.ts @@ -1,11 +1,11 @@ -import { test, expect } from "@playwright/test"; -import { createAuthenticatedSession } from "./globalHelpers"; +import { expect, test } from "@playwright/test"; +import type { Context } from "./helpers/context"; import { contextDefaults, setUpTestContext, tearDownTestContext, -} from "./context"; -import type { Context } from "./context"; +} from "./helpers/context"; +import { createAuthenticatedSession } from "./helpers/globalHelpers"; test.describe("Login", () => { let context: Context = { @@ -67,7 +67,7 @@ test.describe("Login", () => { route.continue(); }); await expect( - page.locator("h1").filter({ hasText: "Services" }), + page.locator("h1").filter({ hasText: "Services" }) ).toBeVisible(); await expect(page.getByText(toastText)).toBeHidden(); }); diff --git a/e2e/tests/ui-driven/src/pay.spec.ts b/e2e/tests/ui-driven/src/pay.spec.ts index d7e6e9b56a..4d1eebdbb2 100644 --- a/e2e/tests/ui-driven/src/pay.spec.ts +++ b/e2e/tests/ui-driven/src/pay.spec.ts @@ -1,23 +1,22 @@ -import { test, expect } from "@playwright/test"; -import { - cards, - fillGovUkCardDetails, - getSessionId, - log, - submitCardDetails, - waitForPaymentResponse, -} from "./globalHelpers"; -import type { Page } from "@playwright/test"; -import payFlow from "./mocks/flows/pay-flow.json"; -import { gql, GraphQLClient } from "graphql-request"; import type { SessionData } from "@opensystemslab/planx-core/types"; -import type { Context } from "./context"; +import type { Page } from "@playwright/test"; +import { expect, test } from "@playwright/test"; +import { GraphQLClient, gql } from "graphql-request"; +import type { Context } from "./helpers/context"; import { contextDefaults, getGraphQLClient, setUpTestContext, tearDownTestContext, -} from "./context"; +} from "./helpers/context"; +import { + cards, + getSessionId, + log, + waitForPaymentResponse, +} from "./helpers/globalHelpers"; +import { fillGovUkCardDetails, submitCardDetails } from "./helpers/userActions"; +import payFlow from "./mocks/flows/pay-flow.json"; let context: Context = { ...contextDefaults, @@ -69,7 +68,7 @@ test.describe("Gov Pay integration @regression", async () => { status: "success", paymentId: paymentId, adminGQLClient, - }), + }) ).toBe(true); // ensure that data stored in the session matches the latest payment attempt @@ -93,7 +92,7 @@ test.describe("Gov Pay integration @regression", async () => { const { paymentId: failedPaymentRef } = await waitForPaymentResponse( page, - context, + context ); expect(failedPaymentRef).toBeTruthy(); @@ -103,7 +102,7 @@ test.describe("Gov Pay integration @regression", async () => { status: "failed", paymentId: failedPaymentRef, adminGQLClient, - }), + }) ).toBe(true); // ensure that data stored in the session matches the latest payment attempt @@ -128,7 +127,7 @@ test.describe("Gov Pay integration @regression", async () => { status: "success", paymentId: paymentId, adminGQLClient, - }), + }) ).toBe(true); // ensure that data stored in the session matches the latest payment attempt @@ -151,7 +150,7 @@ test.describe("Gov Pay integration @regression", async () => { await page.locator("#return-url").click(); const { paymentId: failedPaymentRef } = await waitForPaymentResponse( page, - context, + context ); expect(failedPaymentRef).toBeTruthy(); @@ -161,7 +160,7 @@ test.describe("Gov Pay integration @regression", async () => { status: "failed", // note: GovPay returns "failed" rather than "cancelled" paymentId: failedPaymentRef, adminGQLClient, - }), + }) ).toBe(true); await page.getByText("Retry payment").click(); @@ -179,7 +178,7 @@ test.describe("Gov Pay integration @regression", async () => { status: "success", paymentId: paymentId, adminGQLClient, - }), + }) ).toBe(true); // ensure that data stored in the session matches the latest payment attempt @@ -212,7 +211,7 @@ test.describe("Gov Pay integration @regression", async () => { sessionId, }); expect(initialSession?.data?.govUkPayment?.state?.status).toEqual( - "created", + "created" ); // ensure a audit log entry was created expect( @@ -220,7 +219,7 @@ test.describe("Gov Pay integration @regression", async () => { status: "created", paymentId: initialSession!.data!.govUkPayment!.payment_id, adminGQLClient, - }), + }) ).toBe(true); // retry the payment @@ -237,7 +236,7 @@ test.describe("Gov Pay integration @regression", async () => { status: "success", paymentId, adminGQLClient, - }), + }) ).toBe(true); // ensure that data stored in the session matches the latest payment attempt @@ -280,7 +279,7 @@ test.describe("Gov Pay integration @regression", async () => { await submitCardDetails(page); const { paymentId: actualPaymentId } = await waitForPaymentResponse( page, - context, + context ); // ensure that data stored in the session matches the latest payment attempt @@ -308,7 +307,7 @@ test.describe("Gov Pay integration @regression", async () => { await submitCardDetails(page); const { paymentId: actualPaymentId } = await waitForPaymentResponse( page, - context, + context ); await expect(page.getByText("Application sent")).toBeVisible(); await expect(page.getByText(actualPaymentId)).toBeVisible(); @@ -317,12 +316,12 @@ test.describe("Gov Pay integration @regression", async () => { await page.goBack(); // Unable to make another payment - just get a status page... await expect( - page.locator("h1").getByText("Your payment was successful"), + page.locator("h1").getByText("Your payment was successful") ).toBeVisible(); // ...with a link back to PlanX await page.locator("a").getByText("View your payment summary").click(); await expect( - page.locator("h1").getByText("Application sent"), + page.locator("h1").getByText("Application sent") ).toBeVisible(); }); }); @@ -361,7 +360,7 @@ async function hasPaymentStatus({ } } `, - { paymentId, status }, + { paymentId, status } ); if ( response.payment_status.length === 1 && @@ -393,7 +392,7 @@ async function findSession({ } } `, - { sessionId }, + { sessionId } ); return response.lowcal_sessions[0]; } diff --git a/e2e/tests/ui-driven/src/save-and-return.spec.ts b/e2e/tests/ui-driven/src/save-and-return.spec.ts index 162615bad5..c2d01b954e 100644 --- a/e2e/tests/ui-driven/src/save-and-return.spec.ts +++ b/e2e/tests/ui-driven/src/save-and-return.spec.ts @@ -1,23 +1,23 @@ -import { test, expect } from "@playwright/test"; -import { - simpleSendFlow, - modifiedSimpleSendFlow, -} from "./mocks/flows/save-and-return-flows"; +import { expect, test } from "@playwright/test"; +import type { Context } from "./helpers/context"; import { contextDefaults, setUpTestContext, tearDownTestContext, -} from "./context"; +} from "./helpers/context"; +import { modifyFlow } from "./helpers/globalHelpers"; import { - fillInEmail, + answerQuestion, clickContinue, + fillInEmail, findQuestion, - answerQuestion, returnToSession, saveSession, - modifyFlow, -} from "./globalHelpers"; -import type { Context } from "./context"; +} from "./helpers/userActions"; +import { + modifiedSimpleSendFlow, + simpleSendFlow, +} from "./mocks/flows/save-and-return-flows"; test.describe("Save and return", () => { let context: Context = { diff --git a/e2e/tests/ui-driven/src/sections.spec.ts b/e2e/tests/ui-driven/src/sections.spec.ts index 2a44985e8a..c465151612 100644 --- a/e2e/tests/ui-driven/src/sections.spec.ts +++ b/e2e/tests/ui-driven/src/sections.spec.ts @@ -1,26 +1,26 @@ +import type { FlowGraph } from "@opensystemslab/planx-core/types"; import { expect, test } from "@playwright/test"; -import { flow, updatedQuestionAnswers } from "./mocks/flows/sections-flow"; +import { gql } from "graphql-request"; +import type { Context } from "./helpers/context"; import { contextDefaults, + getGraphQLClient, setUpTestContext, tearDownTestContext, - getGraphQLClient, -} from "./context"; +} from "./helpers/context"; import { - fillInEmail, - answerQuestion, answerChecklist, - clickContinue, + answerQuestion, clickBack, + clickContinue, + expectConfirmation, expectNotice, expectSections, - expectConfirmation, - saveSession, + fillInEmail, returnToSession, -} from "./globalHelpers"; -import { gql } from "graphql-request"; -import type { Context } from "./context"; -import type { FlowGraph } from "@opensystemslab/planx-core/types"; + saveSession, +} from "./helpers/userActions"; +import { flow, updatedQuestionAnswers } from "./mocks/flows/sections-flow"; // TODO: move this type to planx-core // also defined in editor.planx.uk/src/types.ts @@ -556,6 +556,6 @@ async function modifyFlow({ flowId: context.flow!.id, userId: context.user!.id, data: flowData, - }, + } ); } From 5b523a509c9a738f9027bceba9f63889b71d9e06 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Wed, 4 Sep 2024 12:54:00 +0100 Subject: [PATCH 13/39] Prettier --- e2e/tests/api-driven/src/globalHelpers.ts | 12 ++--- .../api-driven/src/invite-to-pay/helpers.ts | 10 ++-- .../src/create-flow/create-flow.spec.ts | 26 +++++------ .../ui-driven/src/helpers/addComponent.ts | 16 +++---- e2e/tests/ui-driven/src/helpers/context.ts | 46 +++++++++---------- .../ui-driven/src/helpers/globalHelpers.ts | 8 ++-- .../ui-driven/src/helpers/userActions.ts | 4 +- .../ui-driven/src/invite-to-pay/agent.spec.ts | 20 ++++---- .../ui-driven/src/invite-to-pay/helpers.ts | 2 +- .../src/invite-to-pay/nominee.spec.ts | 12 ++--- e2e/tests/ui-driven/src/login.spec.ts | 2 +- e2e/tests/ui-driven/src/pay.spec.ts | 32 ++++++------- e2e/tests/ui-driven/src/sections.spec.ts | 2 +- 13 files changed, 96 insertions(+), 96 deletions(-) diff --git a/e2e/tests/api-driven/src/globalHelpers.ts b/e2e/tests/api-driven/src/globalHelpers.ts index a735298e27..569f98cc9b 100644 --- a/e2e/tests/api-driven/src/globalHelpers.ts +++ b/e2e/tests/api-driven/src/globalHelpers.ts @@ -2,7 +2,7 @@ import { TEST_EMAIL } from "../../ui-driven/src/helpers/globalHelpers"; import { $admin } from "./client"; export function createTeam( - args?: Partial[0]> + args?: Partial[0]>, ) { return safely(() => $admin.team.create({ @@ -13,12 +13,12 @@ export function createTeam( homepage: "http://www.planx.uk", }, ...args, - }) + }), ); } export function createUser( - args?: Partial[0]> + args?: Partial[0]>, ) { return safely(() => $admin.user.create({ @@ -26,18 +26,18 @@ export function createUser( lastName: "Test", email: TEST_EMAIL, ...args, - }) + }), ); } export function createFlow( - args: Omit[0], "data"> + args: Omit[0], "data">, ) { return safely(() => $admin.flow.create({ data: { dummy: "flowData " }, ...args, - }) + }), ); } diff --git a/e2e/tests/api-driven/src/invite-to-pay/helpers.ts b/e2e/tests/api-driven/src/invite-to-pay/helpers.ts index 3102dc9c17..315bac1764 100644 --- a/e2e/tests/api-driven/src/invite-to-pay/helpers.ts +++ b/e2e/tests/api-driven/src/invite-to-pay/helpers.ts @@ -67,7 +67,7 @@ export async function buildSessionForFlow(flowId: string): Promise { } export async function buildPaymentRequestForSession( - sessionId: string + sessionId: string, ): Promise { await $admin.session.lock(sessionId); return $admin.paymentRequest.create({ @@ -80,14 +80,14 @@ export async function buildPaymentRequestForSession( } export async function markPaymentRequestAsPaid( - paymentRequestId: string + paymentRequestId: string, ): Promise { return await $admin.paymentRequest._markAsPaid(paymentRequestId); } export async function getSendResponse( destination: string, - sessionId: string + sessionId: string, ): Promise { switch (destination.toLowerCase()) { case "bops": @@ -127,7 +127,7 @@ export async function waitForResponse({ } export async function getSessionSubmittedAt( - sessionId: string + sessionId: string, ): Promise { const detailedSession = await $admin.session.findDetails(sessionId); return detailedSession?.submittedAt; @@ -180,7 +180,7 @@ const setupMockBopsSubmissionUrl = async (teamId: number) => { { teamId, stagingBopsSubmissionUrl: "http://mock-server:8080", - } + }, ); }; diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 938fe9d82e..5732b50106 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -62,7 +62,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)) + (req) => (isRepeatedRequestMade = isGetUserRequest(req)), ); Promise.all([ @@ -118,7 +118,7 @@ test.describe("Navigation", () => { "No", ]); await expect( - page.locator("a").filter({ hasText: questionText }) + page.locator("a").filter({ hasText: questionText }), ).toBeVisible(); // Add a notice to the "Yes" path @@ -128,7 +128,7 @@ test.describe("Navigation", () => { await createNotice( page, yesBranch.locator(".hanger > a"), - yesBranchNoticeText + yesBranchNoticeText, ); // Add a notice to the "No" path @@ -137,7 +137,7 @@ test.describe("Navigation", () => { await createNotice( page, noBranch.locator(".hanger > a"), - noBranchNoticeText + noBranchNoticeText, ); // add a checklist @@ -167,7 +167,7 @@ test.describe("Navigation", () => { page, nextNode, "What is your address?", - "some data field" + "some data field", ); // add a contact input @@ -176,7 +176,7 @@ test.describe("Navigation", () => { page, nextNode, "What is your contact info?", - "some data field" + "some data field", ); const nodes = page.locator(".card"); @@ -202,7 +202,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -236,11 +236,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }) + page.getByRole("heading", { level: 1, name: "Offline" }), ).toBeVisible(); }); @@ -259,7 +259,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully") + page.getByText("Service settings updated successfully"), ).toBeVisible(); // Exit back to main Editor page @@ -282,13 +282,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }) + page.locator("h1", { hasText: "Yes! this is a test" }), ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -296,7 +296,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }) + page.locator("h1", { hasText: "Sorry, this is a test" }), ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index 2a389e63f3..e94bb34eb1 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -4,7 +4,7 @@ export const createQuestionWithOptions = async ( page: Page, locatingNodeSelector: string, questionText: string, - options: string[] + options: string[], ) => { await page.locator(locatingNodeSelector).click(); await page.getByRole("dialog").waitFor(); @@ -23,7 +23,7 @@ export const createQuestionWithOptions = async ( export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string + noticeText: string, ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -36,7 +36,7 @@ export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[] + checklistOptions: string[], ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -55,7 +55,7 @@ export const createChecklist = async ( export const createTextInput = async ( page: Page, locatingNode: Locator, - inputTitle: string + inputTitle: string, ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -68,7 +68,7 @@ export const createNumberInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputUnits: string + inputUnits: string, ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -84,7 +84,7 @@ export const createNumberInput = async ( export const createDateInput = async ( page: Page, locatingNode: Locator, - inputTitle: string + inputTitle: string, ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -104,7 +104,7 @@ export const createAddressInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string + inputDataField: string, ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -122,7 +122,7 @@ export const createContactInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string + inputDataField: string, ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); diff --git a/e2e/tests/ui-driven/src/helpers/context.ts b/e2e/tests/ui-driven/src/helpers/context.ts index dde360b9d5..bccc2b232a 100644 --- a/e2e/tests/ui-driven/src/helpers/context.ts +++ b/e2e/tests/ui-driven/src/helpers/context.ts @@ -47,7 +47,7 @@ export const contextDefaults: Context = { }; export async function setUpTestContext( - initialContext: Context + initialContext: Context, ): Promise { const $admin = getCoreDomainClient(); const context: Context = { ...initialContext }; @@ -117,7 +117,7 @@ export function generateAuthenticationToken(userId: string) { "x-hasura-user-id": `${userId}`, }, }, - process.env.JWT_SECRET + process.env.JWT_SECRET, ); } @@ -127,7 +127,7 @@ export function getCoreDomainClient(): CoreDomainClient { const API = process.env.HASURA_GRAPHQL_URL!.replace( "${HASURA_PROXY_PORT}", - process.env.HASURA_PROXY_PORT! + process.env.HASURA_PROXY_PORT!, ); const SECRET = process.env.HASURA_GRAPHQL_ADMIN_SECRET!; return new CoreDomainClient({ @@ -142,7 +142,7 @@ export function getGraphQLClient(): GraphQLClient { export async function findSessionId( adminGQLClient: GraphQLClient, - context + context, ): Promise { // get the flow id which may have a session const flowResponse: { flows: { id: string }[] } = @@ -152,7 +152,7 @@ export async function findSessionId( id } }`, - { slug: context.flow?.slug } + { slug: context.flow?.slug }, ); if (!flowResponse.flows.length || !flowResponse.flows[0].id) { return; @@ -172,7 +172,7 @@ export async function findSessionId( } } `, - { flowId, email: context.user?.email } + { flowId, email: context.user?.email }, ); if (response.lowcal_sessions.length && response.lowcal_sessions[0].id) { return response.lowcal_sessions[0].id; @@ -188,7 +188,7 @@ async function deleteSession(adminGQLClient: GraphQLClient, context) { id } }`, - { sessionId } + { sessionId }, ); } } @@ -201,14 +201,14 @@ async function deleteSession(adminGQLClient: GraphQLClient, context) { id } }`, - { sessionId } + { sessionId }, ); } } async function deletePublishedFlow( adminGQLClient: GraphQLClient, - context: Context + context: Context, ) { if (context.flow?.publishedId) { log(`deleting published flow ${context.flow?.publishedId}`); @@ -218,7 +218,7 @@ async function deletePublishedFlow( id } }`, - { publishedFlowId: context.flow?.publishedId } + { publishedFlowId: context.flow?.publishedId }, ); } } @@ -232,7 +232,7 @@ async function deleteFlow(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { flowId: context.flow?.id } + { flowId: context.flow?.id }, ); } else if (context.flow?.slug) { // try deleting via slug (when cleaning up from a previously failed test) @@ -242,11 +242,11 @@ async function deleteFlow(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { slug: context.flow?.slug } + { slug: context.flow?.slug }, ); if (response.flows.length && response.flows[0].id) { log( - `deleting flow ${context.flow?.slug} flowId: ${response.flows[0].id}` + `deleting flow ${context.flow?.slug} flowId: ${response.flows[0].id}`, ); await adminGQLClient.request( `mutation DeleteTestFlow( $flowId: uuid!) { @@ -254,7 +254,7 @@ async function deleteFlow(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { flowId: response.flows[0].id } + { flowId: response.flows[0].id }, ); } } @@ -269,7 +269,7 @@ async function deleteUser(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { userId: context.user?.id } + { userId: context.user?.id }, ); } else if (context.user?.email) { // try deleting via email (when cleaning up from a previously failed test) @@ -279,11 +279,11 @@ async function deleteUser(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { email: context.user?.email } + { email: context.user?.email }, ); if (response.users.length && response.users[0].id) { log( - `deleting user ${context.user?.email} userId: ${response.users[0].id}` + `deleting user ${context.user?.email} userId: ${response.users[0].id}`, ); await adminGQLClient.request( `mutation DeleteTestUser($userId: Int!) { @@ -291,7 +291,7 @@ async function deleteUser(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { userId: response.users[0].id } + { userId: response.users[0].id }, ); } } @@ -306,7 +306,7 @@ async function deleteTeam(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { teamId: context.team?.id } + { teamId: context.team?.id }, ); } else if (context.team?.slug) { // try deleting via slug (when cleaning up from a previously failed test) @@ -316,11 +316,11 @@ async function deleteTeam(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { slug: context.team?.slug } + { slug: context.team?.slug }, ); if (response.teams.length && response.teams[0].id) { log( - `deleting team ${context.team?.slug} teamId: ${response.teams[0].id}` + `deleting team ${context.team?.slug} teamId: ${response.teams[0].id}`, ); await adminGQLClient.request( `mutation DeleteTestTeam( $teamId: Int!) { @@ -328,7 +328,7 @@ async function deleteTeam(adminGQLClient: GraphQLClient, context: Context) { id } }`, - { teamId: response.teams[0].id } + { teamId: response.teams[0].id }, ); } } @@ -353,7 +353,7 @@ async function setupGovPaySecret($admin: CoreDomainClient, context: Context) { { team_id: context.team.id, staging_govpay_secret: process.env.GOV_UK_PAY_SECRET_E2E, - } + }, ); } catch (error) { throw Error("Failed to setup GovPay secret for E2E team"); diff --git a/e2e/tests/ui-driven/src/helpers/globalHelpers.ts b/e2e/tests/ui-driven/src/helpers/globalHelpers.ts index 4fa4fb14d0..29e00f7e64 100644 --- a/e2e/tests/ui-driven/src/helpers/globalHelpers.ts +++ b/e2e/tests/ui-driven/src/helpers/globalHelpers.ts @@ -74,9 +74,9 @@ export async function setFeatureFlag(page: Page, featureFlag: string) { (featureFlag: string) => window.localStorage.setItem( "FEATURE_FLAGS", - JSON.stringify([featureFlag]) + JSON.stringify([featureFlag]), ), - featureFlag + featureFlag, ); } @@ -95,7 +95,7 @@ export async function addSessionToContext(page: Page, context: Context) { export async function waitForPaymentResponse( page: Page, - context: Context + context: Context, ): Promise<{ paymentId: string; state?: { status: string } }> { const { payment_id: paymentId, state } = await page .waitForResponse((response) => { @@ -135,7 +135,7 @@ export async function modifyFlow({ flowId: context.flow!.id, userId: context.user!.id, data: modifiedFlow, - } + }, ); } export const isGetUserRequest = (req: Request) => diff --git a/e2e/tests/ui-driven/src/helpers/userActions.ts b/e2e/tests/ui-driven/src/helpers/userActions.ts index 613507beaa..bb2a048125 100644 --- a/e2e/tests/ui-driven/src/helpers/userActions.ts +++ b/e2e/tests/ui-driven/src/helpers/userActions.ts @@ -203,7 +203,7 @@ export async function answerFindProperty(page: Page) { async function setupOSMockResponse(page: Page) { const ordnanceSurveryPlacesEndpoint = new RegExp( - /proxy\/ordnance-survey\/search\/places\/v1\/postcode\/*/ + /proxy\/ordnance-survey\/search\/places\/v1\/postcode\/*/, ); await page.route(ordnanceSurveryPlacesEndpoint, async (route) => { await route.fulfill({ @@ -225,7 +225,7 @@ export async function answerContactInput( lastName: string; phoneNumber: string; email: string; - } + }, ) { await page.getByLabel("First name").fill(firstName); await page.getByLabel("Last name").fill(lastName); diff --git a/e2e/tests/ui-driven/src/invite-to-pay/agent.spec.ts b/e2e/tests/ui-driven/src/invite-to-pay/agent.spec.ts index 77ea290944..23d94b2609 100644 --- a/e2e/tests/ui-driven/src/invite-to-pay/agent.spec.ts +++ b/e2e/tests/ui-driven/src/invite-to-pay/agent.spec.ts @@ -56,7 +56,7 @@ test.describe("Agent journey @regression", async () => { await expect(toggleInviteToPayButton).toBeVisible(); await toggleInviteToPayButton.click(); const inviteToPayFormHeader = page.getByText( - "Invite someone else to pay for this application" + "Invite someone else to pay for this application", ); await expect(inviteToPayFormHeader).toBeVisible(); @@ -65,7 +65,7 @@ test.describe("Agent journey @regression", async () => { await page.waitForResponse((res) => res.url().includes("invite-to-pay")); const errorMessage = page.getByText( - "Error generating payment request, please try again" + "Error generating payment request, please try again", ); await expect(errorMessage).toBeHidden(); @@ -113,7 +113,7 @@ test.describe("Agent journey @regression", async () => { await expect( secondPage.getByRole("heading", { name: "Resume your application", - }) + }), ).toBeVisible(); await secondPage.getByLabel("Email address").fill(context.user.email); await secondPage.getByTestId("continue-button").click(); @@ -121,7 +121,7 @@ test.describe("Agent journey @regression", async () => { await expect( secondPage.getByRole("heading", { name: "Sorry, you can't make changes to this application", - }) + }), ).toBeVisible(); await expect(secondPage.getByTestId("continue-button")).toBeHidden(); }); @@ -143,14 +143,14 @@ test.describe("Agent journey @regression", async () => { await expect( secondPage.getByRole("heading", { name: "Resume your application", - }) + }), ).toBeVisible(); await secondPage.getByLabel("Email address").fill(context.user.email); await secondPage.getByTestId("continue-button").click(); // Reconciliation ignored const reconciliationText = secondPage.getByText( - "This service has been updated since you last saved your application. We will ask you to answer any updated questions again when you continue." + "This service has been updated since you last saved your application. We will ask you to answer any updated questions again when you continue.", ); await expect(reconciliationText).toBeHidden(); @@ -158,7 +158,7 @@ test.describe("Agent journey @regression", async () => { await expect( secondPage.getByRole("heading", { name: "Sorry, you can't make changes to this application", - }) + }), ).toBeVisible(); await expect(secondPage.getByTestId("continue-button")).toBeHidden(); }); @@ -177,7 +177,7 @@ test.describe("Agent journey @regression", async () => { await answerInviteToPayForm(tab1); await tab1.getByRole("button", { name: "Send invitation to pay" }).click(); await tab1.waitForResponse( - (resp) => resp.url().includes("/v1/graphql") && resp.status() === 200 + (resp) => resp.url().includes("/v1/graphql") && resp.status() === 200, ); const paymentRequest = await getPaymentRequestBySessionId({ sessionId, @@ -190,7 +190,7 @@ test.describe("Agent journey @regression", async () => { // ...and fail to do so const errorMessage = tab2.getByText( - "Cannot initialise a new payment for locked session" + "Cannot initialise a new payment for locked session", ); await expect(errorMessage).toBeVisible(); }); @@ -217,7 +217,7 @@ test.describe("Agent journey @regression", async () => { // ...and fail to do so const errorMessage = tab2.getByText( - "Error generating payment request, please try again" + "Error generating payment request, please try again", ); await expect(errorMessage).toBeVisible(); }); diff --git a/e2e/tests/ui-driven/src/invite-to-pay/helpers.ts b/e2e/tests/ui-driven/src/invite-to-pay/helpers.ts index 5668542b74..10347d7a81 100644 --- a/e2e/tests/ui-driven/src/invite-to-pay/helpers.ts +++ b/e2e/tests/ui-driven/src/invite-to-pay/helpers.ts @@ -71,7 +71,7 @@ export async function getPaymentRequestBySessionId({ } } `, - { sessionId } + { sessionId }, ); return paymentRequests[0]; } catch (e) { diff --git a/e2e/tests/ui-driven/src/invite-to-pay/nominee.spec.ts b/e2e/tests/ui-driven/src/invite-to-pay/nominee.spec.ts index 47d003e3a5..bd78120699 100644 --- a/e2e/tests/ui-driven/src/invite-to-pay/nominee.spec.ts +++ b/e2e/tests/ui-driven/src/invite-to-pay/nominee.spec.ts @@ -49,10 +49,10 @@ test.describe("Nominee journey @regression", async () => { await navigateToPaymentRequestPage(paymentRequest, page); await expect( - page.getByRole("heading", { name: "Pay for your application" }) + page.getByRole("heading", { name: "Pay for your application" }), ).toBeVisible(); await expect( - page.locator("#main-content").getByText("Invite to pay test") + page.locator("#main-content").getByText("Invite to pay test"), ).toBeVisible(); await expect(page.getByText("123, Test Street, Testville")).toBeVisible(); @@ -124,7 +124,7 @@ test.describe("Nominee journey @regression", async () => { async function navigateToPaymentRequestPage( paymentRequest: PaymentRequest, - page: Page + page: Page, ) { const paymentRequestURL = `/${context.team!.slug!}/${context.flow! .slug!}/pay?analytics=false&paymentRequestId=${paymentRequest.id}`; @@ -133,7 +133,7 @@ async function navigateToPaymentRequestPage( } async function setupPaymentRequest( - request: APIRequestContext + request: APIRequestContext, ): Promise< Record<"paymentRequest", PaymentRequest> & Record<"sessionId", string> > { @@ -185,13 +185,13 @@ async function createSession({ */ async function createPaymentRequest( request: APIRequestContext, - sessionId: string + sessionId: string, ) { const response = await request.post( `http://localhost:${process.env.API_PORT}/invite-to-pay/${sessionId}`, { data: mockPaymentRequestDetails, - } + }, ); return response.json(); } diff --git a/e2e/tests/ui-driven/src/login.spec.ts b/e2e/tests/ui-driven/src/login.spec.ts index 160471a506..23bfbecf83 100644 --- a/e2e/tests/ui-driven/src/login.spec.ts +++ b/e2e/tests/ui-driven/src/login.spec.ts @@ -67,7 +67,7 @@ test.describe("Login", () => { route.continue(); }); await expect( - page.locator("h1").filter({ hasText: "Services" }) + page.locator("h1").filter({ hasText: "Services" }), ).toBeVisible(); await expect(page.getByText(toastText)).toBeHidden(); }); diff --git a/e2e/tests/ui-driven/src/pay.spec.ts b/e2e/tests/ui-driven/src/pay.spec.ts index 4d1eebdbb2..edf63ffdb1 100644 --- a/e2e/tests/ui-driven/src/pay.spec.ts +++ b/e2e/tests/ui-driven/src/pay.spec.ts @@ -68,7 +68,7 @@ test.describe("Gov Pay integration @regression", async () => { status: "success", paymentId: paymentId, adminGQLClient, - }) + }), ).toBe(true); // ensure that data stored in the session matches the latest payment attempt @@ -92,7 +92,7 @@ test.describe("Gov Pay integration @regression", async () => { const { paymentId: failedPaymentRef } = await waitForPaymentResponse( page, - context + context, ); expect(failedPaymentRef).toBeTruthy(); @@ -102,7 +102,7 @@ test.describe("Gov Pay integration @regression", async () => { status: "failed", paymentId: failedPaymentRef, adminGQLClient, - }) + }), ).toBe(true); // ensure that data stored in the session matches the latest payment attempt @@ -127,7 +127,7 @@ test.describe("Gov Pay integration @regression", async () => { status: "success", paymentId: paymentId, adminGQLClient, - }) + }), ).toBe(true); // ensure that data stored in the session matches the latest payment attempt @@ -150,7 +150,7 @@ test.describe("Gov Pay integration @regression", async () => { await page.locator("#return-url").click(); const { paymentId: failedPaymentRef } = await waitForPaymentResponse( page, - context + context, ); expect(failedPaymentRef).toBeTruthy(); @@ -160,7 +160,7 @@ test.describe("Gov Pay integration @regression", async () => { status: "failed", // note: GovPay returns "failed" rather than "cancelled" paymentId: failedPaymentRef, adminGQLClient, - }) + }), ).toBe(true); await page.getByText("Retry payment").click(); @@ -178,7 +178,7 @@ test.describe("Gov Pay integration @regression", async () => { status: "success", paymentId: paymentId, adminGQLClient, - }) + }), ).toBe(true); // ensure that data stored in the session matches the latest payment attempt @@ -211,7 +211,7 @@ test.describe("Gov Pay integration @regression", async () => { sessionId, }); expect(initialSession?.data?.govUkPayment?.state?.status).toEqual( - "created" + "created", ); // ensure a audit log entry was created expect( @@ -219,7 +219,7 @@ test.describe("Gov Pay integration @regression", async () => { status: "created", paymentId: initialSession!.data!.govUkPayment!.payment_id, adminGQLClient, - }) + }), ).toBe(true); // retry the payment @@ -236,7 +236,7 @@ test.describe("Gov Pay integration @regression", async () => { status: "success", paymentId, adminGQLClient, - }) + }), ).toBe(true); // ensure that data stored in the session matches the latest payment attempt @@ -279,7 +279,7 @@ test.describe("Gov Pay integration @regression", async () => { await submitCardDetails(page); const { paymentId: actualPaymentId } = await waitForPaymentResponse( page, - context + context, ); // ensure that data stored in the session matches the latest payment attempt @@ -307,7 +307,7 @@ test.describe("Gov Pay integration @regression", async () => { await submitCardDetails(page); const { paymentId: actualPaymentId } = await waitForPaymentResponse( page, - context + context, ); await expect(page.getByText("Application sent")).toBeVisible(); await expect(page.getByText(actualPaymentId)).toBeVisible(); @@ -316,12 +316,12 @@ test.describe("Gov Pay integration @regression", async () => { await page.goBack(); // Unable to make another payment - just get a status page... await expect( - page.locator("h1").getByText("Your payment was successful") + page.locator("h1").getByText("Your payment was successful"), ).toBeVisible(); // ...with a link back to PlanX await page.locator("a").getByText("View your payment summary").click(); await expect( - page.locator("h1").getByText("Application sent") + page.locator("h1").getByText("Application sent"), ).toBeVisible(); }); }); @@ -360,7 +360,7 @@ async function hasPaymentStatus({ } } `, - { paymentId, status } + { paymentId, status }, ); if ( response.payment_status.length === 1 && @@ -392,7 +392,7 @@ async function findSession({ } } `, - { sessionId } + { sessionId }, ); return response.lowcal_sessions[0]; } diff --git a/e2e/tests/ui-driven/src/sections.spec.ts b/e2e/tests/ui-driven/src/sections.spec.ts index c465151612..0aca98272b 100644 --- a/e2e/tests/ui-driven/src/sections.spec.ts +++ b/e2e/tests/ui-driven/src/sections.spec.ts @@ -556,6 +556,6 @@ async function modifyFlow({ flowId: context.flow!.id, userId: context.user!.id, data: flowData, - } + }, ); } From 107e7aecd071c1114698ba6f4ee2c12d2bcc01a5 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Wed, 4 Sep 2024 18:49:37 +0100 Subject: [PATCH 14/39] Refactor to use createBaseInput helper --- .../src/create-flow/create-flow.spec.ts | 36 ++-- .../ui-driven/src/helpers/addComponent.ts | 187 ++++++++++-------- 2 files changed, 121 insertions(+), 102 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 5732b50106..83f5b7e113 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -62,7 +62,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)), + (req) => (isRepeatedRequestMade = isGetUserRequest(req)) ); Promise.all([ @@ -112,13 +112,15 @@ test.describe("Navigation", () => { // update context to allow flow to be torn down context.flow = { ...serviceProps }; + const firstNode = page.locator("li.hanger > a").first(); + const questionText = "Is this a test?"; - await createQuestionWithOptions(page, "li.hanger > a", questionText, [ + await createQuestionWithOptions(page, firstNode, questionText, [ "Yes", "No", ]); await expect( - page.locator("a").filter({ hasText: questionText }), + page.locator("a").filter({ hasText: questionText }) ).toBeVisible(); // Add a notice to the "Yes" path @@ -128,7 +130,7 @@ test.describe("Navigation", () => { await createNotice( page, yesBranch.locator(".hanger > a"), - yesBranchNoticeText, + yesBranchNoticeText ); // Add a notice to the "No" path @@ -137,10 +139,9 @@ test.describe("Navigation", () => { await createNotice( page, noBranch.locator(".hanger > a"), - noBranchNoticeText, + noBranchNoticeText ); - // add a checklist // TODO: find a nicer way to find the next node let nextNode = page.locator(".hanger > a").nth(5); await createChecklist(page, nextNode, "A checklist title", [ @@ -149,34 +150,29 @@ test.describe("Navigation", () => { "The third checklist item", ]); - // add a text input nextNode = page.locator(".hanger > a").nth(7); await createTextInput(page, nextNode, "Tell us about your trees."); - // add a number input nextNode = page.locator(".hanger > a").nth(8); await createNumberInput(page, nextNode, "How old are you?", "years"); - // add a date input nextNode = page.locator(".hanger > a").nth(9); await createDateInput(page, nextNode, "When is your birthday?"); - // add an address input nextNode = page.locator(".hanger > a").nth(10); await createAddressInput( page, nextNode, "What is your address?", - "some data field", + "some data field" ); - // add a contact input nextNode = page.locator(".hanger > a").nth(11); await createContactInput( page, nextNode, "What is your contact info?", - "some data field", + "some data field" ); const nodes = page.locator(".card"); @@ -202,7 +198,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -236,11 +232,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }), + page.getByRole("heading", { level: 1, name: "Offline" }) ).toBeVisible(); }); @@ -259,7 +255,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully"), + page.getByText("Service settings updated successfully") ).toBeVisible(); // Exit back to main Editor page @@ -282,13 +278,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }), + page.locator("h1", { hasText: "Yes! this is a test" }) ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -296,7 +292,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }), + page.locator("h1", { hasText: "Sorry, this is a test" }) ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index e94bb34eb1..2b27b97236 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -1,137 +1,160 @@ import { Locator, Page } from "@playwright/test"; -export const createQuestionWithOptions = async ( +const createBaseInput = async ( page: Page, - locatingNodeSelector: string, - questionText: string, - options: string[], + locatingNode: Locator, + type: string, + title?: string, + options?: string[] ) => { - await page.locator(locatingNodeSelector).click(); + await locatingNode.click(); await page.getByRole("dialog").waitFor(); - await page.getByPlaceholder("Text").fill(questionText); + await page.locator("select").selectOption({ label: type }); - 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); - index++; + switch (type) { + case "Question": + await page.getByPlaceholder("Text").fill(title || ""); + if (options) { + await createOptions(options, "add new", page); + } + break; + case "Notice": + await page.getByPlaceholder("Notice").fill(title || ""); + break; + case "Checklist": + await page.getByPlaceholder("Text").fill(title || ""); + if (options) { + await createOptions(options, "add new option", page); + } + break; + case "Text Input": + await page.getByPlaceholder("Title").fill(title || ""); + break; + case "Number Input": + await page.getByPlaceholder("Title").fill(title || ""); + await page.getByPlaceholder("eg square metres").fill(options?.[0] || ""); + break; + case "Date Input": + await page.getByPlaceholder("Title").fill(title || ""); + // fill with hardcoded dates for now + await page.locator("id=undefined-min-day").fill("01"); + await page.locator("id=undefined-min-month").fill("01"); + await page.locator("id=undefined-min-year").fill("1800"); + await page.locator("id=undefined-max-day").fill("31"); + await page.locator("id=undefined-max-month").fill("12"); + await page.locator("id=undefined-max-year").fill("2199"); + break; + case "Address Input": + await page.getByPlaceholder("Title").fill(title || ""); + await page.getByPlaceholder("Data Field").fill(options?.[0] || ""); + break; + case "Contact Input": + await page.getByPlaceholder("Title").fill(title || ""); + await page.getByPlaceholder("Data Field").fill(options?.[0] || ""); + break; + default: + throw new Error(`Unsupported type: ${type}`); } - await page.locator("button").filter({ hasText: "Create question" }).click(); + // convert type name to lowercase, with dashes if there are spaces + const buttonName = type.toLowerCase().replace(/\s/g, "-"); + await page + .locator("button") + .filter({ + hasText: `Create ${buttonName}`, + }) + .click(); +}; + +export const createQuestionWithOptions = async ( + page: Page, + locatingNode: Locator, + questionText: string, + options: string[] +) => { + await createBaseInput(page, locatingNode, "Question", questionText, options); }; export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string, + noticeText: string ) => { - await locatingNode.click(); - await page.getByRole("dialog").waitFor(); - await page.locator("select").selectOption({ label: "Notice" }); - await page.getByPlaceholder("Notice").fill(noticeText); - await page.locator("button").filter({ hasText: "Create notice" }).click(); + await createBaseInput(page, locatingNode, "Notice", noticeText); }; export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[], + checklistOptions: string[] ) => { - await locatingNode.click(); - await page.getByRole("dialog").waitFor(); - await page.locator("select").selectOption({ label: "Checklist" }); - await page.getByPlaceholder("Text").fill(checklistTitle); - - let index = 0; - for (const option of checklistOptions) { - await page.locator("button").filter({ hasText: "add new option" }).click(); - await page.getByPlaceholder("Option").nth(index).fill(option); - index++; - } - await page.locator("button").filter({ hasText: "Create checklist" }).click(); + createBaseInput( + page, + locatingNode, + "Checklist", + checklistTitle, + checklistOptions + ); }; export const createTextInput = async ( page: Page, locatingNode: Locator, - inputTitle: string, + inputTitle: string ) => { - await locatingNode.click(); - await page.getByRole("dialog").waitFor(); - await page.locator("select").selectOption({ label: "Text Input" }); - await page.getByPlaceholder("Title").fill(inputTitle); - await page.locator("button").filter({ hasText: "Create text-input" }).click(); + await createBaseInput(page, locatingNode, "Text Input", inputTitle); }; export const createNumberInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputUnits: string, + inputUnits: string ) => { - await locatingNode.click(); - await page.getByRole("dialog").waitFor(); - await page.locator("select").selectOption({ label: "Number Input" }); - await page.getByPlaceholder("Title").fill(inputTitle); - await page.getByPlaceholder("eg square metres").fill(inputUnits); - await page - .locator("button") - .filter({ hasText: "Create number-input" }) - .click(); + await createBaseInput(page, locatingNode, "Number Input", inputTitle, [ + inputUnits, + ]); }; export const createDateInput = async ( page: Page, locatingNode: Locator, - inputTitle: string, + inputTitle: string ) => { - await locatingNode.click(); - await page.getByRole("dialog").waitFor(); - await page.locator("select").selectOption({ label: "Date Input" }); - await page.getByPlaceholder("Title").fill(inputTitle); - await page.locator("id=undefined-min-day").fill("01"); - await page.locator("id=undefined-min-month").fill("01"); - await page.locator("id=undefined-min-year").fill("1800"); - await page.locator("id=undefined-max-day").fill("31"); - await page.locator("id=undefined-max-month").fill("12"); - await page.locator("id=undefined-max-year").fill("2199"); - - await page.locator("button").filter({ hasText: "Create date-input" }).click(); + await createBaseInput(page, locatingNode, "Date Input", inputTitle); }; export const createAddressInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string, + inputDataField: string ) => { - await locatingNode.click(); - await page.getByRole("dialog").waitFor(); - await page.locator("select").selectOption({ label: "Address Input" }); - await page.getByPlaceholder("Title").fill(inputTitle); - await page.getByPlaceholder("Data Field").fill(inputDataField); - - await page - .locator("button") - .filter({ hasText: "Create address-input" }) - .click(); + await createBaseInput(page, locatingNode, "Address Input", inputTitle, [ + inputDataField, + ]); }; export const createContactInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string, + inputDataField: string ) => { - await locatingNode.click(); - await page.getByRole("dialog").waitFor(); - await page.locator("select").selectOption({ label: "Contact Input" }); - await page.getByPlaceholder("Title").fill(inputTitle); - await page.getByPlaceholder("Data Field").fill(inputDataField); - - await page - .locator("button") - .filter({ hasText: "Create contact-input" }) - .click(); + await createBaseInput(page, locatingNode, "Contact Input", inputTitle, [ + inputDataField, + ]); }; +async function createOptions( + options: string[], + buttonText: string, + page: Page +) { + let index = 0; + for (const option of options) { + await page.locator("button").filter({ hasText: buttonText }).click(); + await page.getByPlaceholder("Option").nth(index).fill(option); + index++; + } +} From 5efb97ac2ca3e0fb11812bdea9e119bfb5ae04f5 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Wed, 4 Sep 2024 18:50:29 +0100 Subject: [PATCH 15/39] Prettier again --- .../src/create-flow/create-flow.spec.ts | 26 +++++++++---------- .../ui-driven/src/helpers/addComponent.ts | 22 ++++++++-------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 83f5b7e113..c7a9cc726b 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -62,7 +62,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)) + (req) => (isRepeatedRequestMade = isGetUserRequest(req)), ); Promise.all([ @@ -120,7 +120,7 @@ test.describe("Navigation", () => { "No", ]); await expect( - page.locator("a").filter({ hasText: questionText }) + page.locator("a").filter({ hasText: questionText }), ).toBeVisible(); // Add a notice to the "Yes" path @@ -130,7 +130,7 @@ test.describe("Navigation", () => { await createNotice( page, yesBranch.locator(".hanger > a"), - yesBranchNoticeText + yesBranchNoticeText, ); // Add a notice to the "No" path @@ -139,7 +139,7 @@ test.describe("Navigation", () => { await createNotice( page, noBranch.locator(".hanger > a"), - noBranchNoticeText + noBranchNoticeText, ); // TODO: find a nicer way to find the next node @@ -164,7 +164,7 @@ test.describe("Navigation", () => { page, nextNode, "What is your address?", - "some data field" + "some data field", ); nextNode = page.locator(".hanger > a").nth(11); @@ -172,7 +172,7 @@ test.describe("Navigation", () => { page, nextNode, "What is your contact info?", - "some data field" + "some data field", ); const nodes = page.locator(".card"); @@ -198,7 +198,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -232,11 +232,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }) + page.getByRole("heading", { level: 1, name: "Offline" }), ).toBeVisible(); }); @@ -255,7 +255,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully") + page.getByText("Service settings updated successfully"), ).toBeVisible(); // Exit back to main Editor page @@ -278,13 +278,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }) + page.locator("h1", { hasText: "Yes! this is a test" }), ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -292,7 +292,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }) + page.locator("h1", { hasText: "Sorry, this is a test" }), ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index 2b27b97236..9411c2d955 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -5,7 +5,7 @@ const createBaseInput = async ( locatingNode: Locator, type: string, title?: string, - options?: string[] + options?: string[], ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -70,7 +70,7 @@ export const createQuestionWithOptions = async ( page: Page, locatingNode: Locator, questionText: string, - options: string[] + options: string[], ) => { await createBaseInput(page, locatingNode, "Question", questionText, options); }; @@ -78,7 +78,7 @@ export const createQuestionWithOptions = async ( export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string + noticeText: string, ) => { await createBaseInput(page, locatingNode, "Notice", noticeText); }; @@ -87,21 +87,21 @@ export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[] + checklistOptions: string[], ) => { createBaseInput( page, locatingNode, "Checklist", checklistTitle, - checklistOptions + checklistOptions, ); }; export const createTextInput = async ( page: Page, locatingNode: Locator, - inputTitle: string + inputTitle: string, ) => { await createBaseInput(page, locatingNode, "Text Input", inputTitle); }; @@ -110,7 +110,7 @@ export const createNumberInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputUnits: string + inputUnits: string, ) => { await createBaseInput(page, locatingNode, "Number Input", inputTitle, [ inputUnits, @@ -120,7 +120,7 @@ export const createNumberInput = async ( export const createDateInput = async ( page: Page, locatingNode: Locator, - inputTitle: string + inputTitle: string, ) => { await createBaseInput(page, locatingNode, "Date Input", inputTitle); }; @@ -129,7 +129,7 @@ export const createAddressInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string + inputDataField: string, ) => { await createBaseInput(page, locatingNode, "Address Input", inputTitle, [ inputDataField, @@ -140,7 +140,7 @@ export const createContactInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string + inputDataField: string, ) => { await createBaseInput(page, locatingNode, "Contact Input", inputTitle, [ inputDataField, @@ -149,7 +149,7 @@ export const createContactInput = async ( async function createOptions( options: string[], buttonText: string, - page: Page + page: Page, ) { let index = 0; for (const option of options) { From fbafa6ec1b4592b9cfb9f1082e509b15a21aeb7f Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:05:48 +0100 Subject: [PATCH 16/39] Add tasklist component --- .../src/create-flow/create-flow.spec.ts | 34 +++++---- .../ui-driven/src/helpers/addComponent.ts | 74 +++++++++++++------ 2 files changed, 73 insertions(+), 35 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index c7a9cc726b..52ac4822b2 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -7,6 +7,7 @@ import { createNotice, createNumberInput, createQuestionWithOptions, + createTaskList, createTextInput, } from "../helpers/addComponent"; import type { Context } from "../helpers/context"; @@ -62,7 +63,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)), + (req) => (isRepeatedRequestMade = isGetUserRequest(req)) ); Promise.all([ @@ -120,7 +121,7 @@ test.describe("Navigation", () => { "No", ]); await expect( - page.locator("a").filter({ hasText: questionText }), + page.locator("a").filter({ hasText: questionText }) ).toBeVisible(); // Add a notice to the "Yes" path @@ -130,7 +131,7 @@ test.describe("Navigation", () => { await createNotice( page, yesBranch.locator(".hanger > a"), - yesBranchNoticeText, + yesBranchNoticeText ); // Add a notice to the "No" path @@ -139,7 +140,7 @@ test.describe("Navigation", () => { await createNotice( page, noBranch.locator(".hanger > a"), - noBranchNoticeText, + noBranchNoticeText ); // TODO: find a nicer way to find the next node @@ -164,7 +165,7 @@ test.describe("Navigation", () => { page, nextNode, "What is your address?", - "some data field", + "some data field" ); nextNode = page.locator(".hanger > a").nth(11); @@ -172,9 +173,15 @@ test.describe("Navigation", () => { page, nextNode, "What is your contact info?", - "some data field", + "some data field" ); + nextNode = page.locator(".hanger > a").nth(12); + await createTaskList(page, nextNode, "What you should do next", [ + "Have a cup of tea", + "Continue through this flow", + ]); + const nodes = page.locator(".card"); await expect(nodes.getByText(questionText)).toBeVisible(); await expect(nodes.getByText(yesBranchNoticeText)).toBeVisible(); @@ -185,6 +192,7 @@ test.describe("Navigation", () => { await expect(nodes.getByText("When is your birthday?")).toBeVisible(); await expect(nodes.getByText("What is your address?")).toBeVisible(); await expect(nodes.getByText("What is your contact info?")).toBeVisible(); + await expect(nodes.getByText("What you should do next")).toBeVisible(); }); test("Cannot preview an unpublished flow", async ({ @@ -198,7 +206,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -232,11 +240,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }), + page.getByRole("heading", { level: 1, name: "Offline" }) ).toBeVisible(); }); @@ -255,7 +263,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully"), + page.getByText("Service settings updated successfully") ).toBeVisible(); // Exit back to main Editor page @@ -278,13 +286,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }), + page.locator("h1", { hasText: "Yes! this is a test" }) ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -292,7 +300,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }), + page.locator("h1", { hasText: "Sorry, this is a test" }) ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index 9411c2d955..6f17cca883 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -1,11 +1,11 @@ import { Locator, Page } from "@playwright/test"; -const createBaseInput = async ( +const createBaseComponent = async ( page: Page, locatingNode: Locator, type: string, title?: string, - options?: string[], + options?: string[] ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -15,7 +15,7 @@ const createBaseInput = async ( case "Question": await page.getByPlaceholder("Text").fill(title || ""); if (options) { - await createOptions(options, "add new", page); + await createComponentOptions(options, "add new", page); } break; case "Notice": @@ -24,7 +24,7 @@ const createBaseInput = async ( case "Checklist": await page.getByPlaceholder("Text").fill(title || ""); if (options) { - await createOptions(options, "add new option", page); + await createComponentOptions(options, "add new option", page); } break; case "Text Input": @@ -52,6 +52,20 @@ const createBaseInput = async ( await page.getByPlaceholder("Title").fill(title || ""); await page.getByPlaceholder("Data Field").fill(options?.[0] || ""); break; + case "Task List": + await page.getByPlaceholder("Main Title").fill(title || ""); + if (options) { + let index = 0; + for (const option of options) { + await page.locator("button").filter({ hasText: "add new" }).click(); + await page + .getByPlaceholder("Title") + .nth(index + 1) // ignore the main title field + .fill(option); + index++; + } + } + break; default: throw new Error(`Unsupported type: ${type}`); } @@ -70,49 +84,49 @@ export const createQuestionWithOptions = async ( page: Page, locatingNode: Locator, questionText: string, - options: string[], + options: string[] ) => { - await createBaseInput(page, locatingNode, "Question", questionText, options); + await createBaseComponent(page, locatingNode, "Question", questionText, options); }; export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string, + noticeText: string ) => { - await createBaseInput(page, locatingNode, "Notice", noticeText); + await createBaseComponent(page, locatingNode, "Notice", noticeText); }; export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[], + checklistOptions: string[] ) => { - createBaseInput( + createBaseComponent( page, locatingNode, "Checklist", checklistTitle, - checklistOptions, + checklistOptions ); }; export const createTextInput = async ( page: Page, locatingNode: Locator, - inputTitle: string, + inputTitle: string ) => { - await createBaseInput(page, locatingNode, "Text Input", inputTitle); + await createBaseComponent(page, locatingNode, "Text Input", inputTitle); }; export const createNumberInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputUnits: string, + inputUnits: string ) => { - await createBaseInput(page, locatingNode, "Number Input", inputTitle, [ + await createBaseComponent(page, locatingNode, "Number Input", inputTitle, [ inputUnits, ]); }; @@ -120,18 +134,18 @@ export const createNumberInput = async ( export const createDateInput = async ( page: Page, locatingNode: Locator, - inputTitle: string, + inputTitle: string ) => { - await createBaseInput(page, locatingNode, "Date Input", inputTitle); + await createBaseComponent(page, locatingNode, "Date Input", inputTitle); }; export const createAddressInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string, + inputDataField: string ) => { - await createBaseInput(page, locatingNode, "Address Input", inputTitle, [ + await createBaseComponent(page, locatingNode, "Address Input", inputTitle, [ inputDataField, ]); }; @@ -140,13 +154,29 @@ export const createContactInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string, + inputDataField: string ) => { - await createBaseInput(page, locatingNode, "Contact Input", inputTitle, [ + await createBaseComponent(page, locatingNode, "Contact Input", inputTitle, [ inputDataField, ]); }; -async function createOptions( + +export const createTaskList = async ( + page: Page, + locatingNode: Locator, + title: string, + taskListOptions: string[] +) => { + await createBaseComponent( + page, + locatingNode, + "Task List", + title, + taskListOptions + ); +}; + +async function createComponentOptions( options: string[], buttonText: string, page: Page, From f120cef4318763f9341ace1a032097ae14ca1a86 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:19:30 +0100 Subject: [PATCH 17/39] Add review component --- .../src/create-flow/create-flow.spec.ts | 7 +++++++ .../ui-driven/src/helpers/addComponent.ts | 20 +++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 52ac4822b2..7f60db4783 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -7,6 +7,7 @@ import { createNotice, createNumberInput, createQuestionWithOptions, + createReview, createTaskList, createTextInput, } from "../helpers/addComponent"; @@ -182,6 +183,9 @@ test.describe("Navigation", () => { "Continue through this flow", ]); + nextNode = page.locator(".hanger > a").nth(13); + await createReview(page, nextNode); + const nodes = page.locator(".card"); await expect(nodes.getByText(questionText)).toBeVisible(); await expect(nodes.getByText(yesBranchNoticeText)).toBeVisible(); @@ -193,6 +197,9 @@ test.describe("Navigation", () => { await expect(nodes.getByText("What is your address?")).toBeVisible(); await expect(nodes.getByText("What is your contact info?")).toBeVisible(); await expect(nodes.getByText("What you should do next")).toBeVisible(); + await expect( + nodes.getByText("Check your answers before sending your application") + ).toBeVisible(); }); test("Cannot preview an unpublished flow", async ({ diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index 6f17cca883..b8ed26a7bd 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -66,6 +66,12 @@ const createBaseComponent = async ( } } break; + case "Review": + // Don't need to change anything so dummy click to get through switch statement + await page + .getByPlaceholder("Check your answers before sending your application") + .click(); + break; default: throw new Error(`Unsupported type: ${type}`); } @@ -86,7 +92,13 @@ export const createQuestionWithOptions = async ( questionText: string, options: string[] ) => { - await createBaseComponent(page, locatingNode, "Question", questionText, options); + await createBaseComponent( + page, + locatingNode, + "Question", + questionText, + options + ); }; export const createNotice = async ( @@ -176,10 +188,14 @@ export const createTaskList = async ( ); }; +export const createReview = async (page: Page, locatingNode: Locator) => { + await createBaseComponent(page, locatingNode, "Review"); +}; + async function createComponentOptions( options: string[], buttonText: string, - page: Page, + page: Page ) { let index = 0; for (const option of options) { From d753c08cd22fa2a4abc188befef88b4c54157fb1 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:35:02 +0100 Subject: [PATCH 18/39] Add find property node --- .../src/create-flow/create-flow.spec.ts | 5 +++++ .../ui-driven/src/helpers/addComponent.ts | 20 +++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 7f60db4783..af023f8964 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -4,6 +4,7 @@ import { createChecklist, createContactInput, createDateInput, + createFindPropertyComponent, createNotice, createNumberInput, createQuestionWithOptions, @@ -184,6 +185,9 @@ test.describe("Navigation", () => { ]); nextNode = page.locator(".hanger > a").nth(13); + await createFindPropertyComponent(page, nextNode); + + nextNode = page.locator(".hanger > a").nth(14); await createReview(page, nextNode); const nodes = page.locator(".card"); @@ -197,6 +201,7 @@ test.describe("Navigation", () => { await expect(nodes.getByText("What is your address?")).toBeVisible(); await expect(nodes.getByText("What is your contact info?")).toBeVisible(); await expect(nodes.getByText("What you should do next")).toBeVisible(); + await expect(nodes.getByText("Find property")).toBeVisible(); await expect( nodes.getByText("Check your answers before sending your application") ).toBeVisible(); diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index b8ed26a7bd..f70599d8c4 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -67,17 +67,26 @@ const createBaseComponent = async ( } break; case "Review": - // Don't need to change anything so dummy click to get through switch statement + // Don't need to change anything so dummy click await page .getByPlaceholder("Check your answers before sending your application") .click(); break; + case "Find property": + // use default placeholder 'Find the property' + await page.getByPlaceholder("Title").click(); + break; default: throw new Error(`Unsupported type: ${type}`); } // convert type name to lowercase, with dashes if there are spaces - const buttonName = type.toLowerCase().replace(/\s/g, "-"); + // or custom name if it doesn't fit the pattern + const buttonName = + type === "Find property" + ? "find-property-merged" + : type.toLowerCase().replace(/\s/g, "-"); + await page .locator("button") .filter({ @@ -192,6 +201,13 @@ export const createReview = async (page: Page, locatingNode: Locator) => { await createBaseComponent(page, locatingNode, "Review"); }; +export const createFindPropertyComponent = async ( + page: Page, + locatingNode: Locator +) => { + await createBaseComponent(page, locatingNode, "Find property"); +}; + async function createComponentOptions( options: string[], buttonText: string, From 4ac804d69f0f47b5ac532b19eeb8d30dd83491de Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:41:12 +0100 Subject: [PATCH 19/39] Add planning contraints component --- .../ui-driven/src/create-flow/create-flow.spec.ts | 6 ++++++ e2e/tests/ui-driven/src/helpers/addComponent.ts | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index af023f8964..869991f131 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -7,6 +7,7 @@ import { createFindPropertyComponent, createNotice, createNumberInput, + createPlanningConstraintsComponent, createQuestionWithOptions, createReview, createTaskList, @@ -188,6 +189,9 @@ test.describe("Navigation", () => { await createFindPropertyComponent(page, nextNode); nextNode = page.locator(".hanger > a").nth(14); + await createPlanningConstraintsComponent(page, nextNode); + + nextNode = page.locator(".hanger > a").nth(15); await createReview(page, nextNode); const nodes = page.locator(".card"); @@ -202,6 +206,8 @@ test.describe("Navigation", () => { await expect(nodes.getByText("What is your contact info?")).toBeVisible(); await expect(nodes.getByText("What you should do next")).toBeVisible(); await expect(nodes.getByText("Find property")).toBeVisible(); + await expect(nodes.getByText("Planning constraints")).toBeVisible(); + await expect( nodes.getByText("Check your answers before sending your application") ).toBeVisible(); diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index f70599d8c4..43c17fb67f 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -76,6 +76,9 @@ const createBaseComponent = async ( // use default placeholder 'Find the property' await page.getByPlaceholder("Title").click(); break; + case "Planning constraints": + await page.getByPlaceholder(type).click(); + break; default: throw new Error(`Unsupported type: ${type}`); } @@ -208,6 +211,13 @@ export const createFindPropertyComponent = async ( await createBaseComponent(page, locatingNode, "Find property"); }; +export const createPlanningConstraintsComponent = async ( + page: Page, + locatingNode: Locator + ) => { + await createBaseComponent(page, locatingNode, "Planning constraints"); + }; + async function createComponentOptions( options: string[], buttonText: string, From 888f26b58a5fb8600f106de067963a3fcc5b3d56 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:45:25 +0100 Subject: [PATCH 20/39] Add draw boundary component --- .../src/create-flow/create-flow.spec.ts | 13 +++++++---- .../ui-driven/src/helpers/addComponent.ts | 22 +++++++++++-------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 869991f131..57933259e8 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -4,10 +4,11 @@ import { createChecklist, createContactInput, createDateInput, - createFindPropertyComponent, + createDrawBoundary, + createFindProperty, createNotice, createNumberInput, - createPlanningConstraintsComponent, + createPlanningConstraints, createQuestionWithOptions, createReview, createTaskList, @@ -186,12 +187,15 @@ test.describe("Navigation", () => { ]); nextNode = page.locator(".hanger > a").nth(13); - await createFindPropertyComponent(page, nextNode); + await createFindProperty(page, nextNode); nextNode = page.locator(".hanger > a").nth(14); - await createPlanningConstraintsComponent(page, nextNode); + await createDrawBoundary(page, nextNode); nextNode = page.locator(".hanger > a").nth(15); + await createPlanningConstraints(page, nextNode); + + nextNode = page.locator(".hanger > a").nth(16); await createReview(page, nextNode); const nodes = page.locator(".card"); @@ -206,6 +210,7 @@ test.describe("Navigation", () => { await expect(nodes.getByText("What is your contact info?")).toBeVisible(); await expect(nodes.getByText("What you should do next")).toBeVisible(); await expect(nodes.getByText("Find property")).toBeVisible(); + await expect(nodes.getByText("Confirm your location plan")).toBeVisible(); await expect(nodes.getByText("Planning constraints")).toBeVisible(); await expect( diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index 43c17fb67f..526072a28f 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -77,7 +77,10 @@ const createBaseComponent = async ( await page.getByPlaceholder("Title").click(); break; case "Planning constraints": - await page.getByPlaceholder(type).click(); + await page.getByPlaceholder(type).click(); + break; + case "Draw boundary": + page.getByPlaceholder(type); break; default: throw new Error(`Unsupported type: ${type}`); @@ -204,19 +207,20 @@ export const createReview = async (page: Page, locatingNode: Locator) => { await createBaseComponent(page, locatingNode, "Review"); }; -export const createFindPropertyComponent = async ( +export const createFindProperty = async (page: Page, locatingNode: Locator) => { + await createBaseComponent(page, locatingNode, "Find property"); +}; + +export const createPlanningConstraints = async ( page: Page, locatingNode: Locator ) => { - await createBaseComponent(page, locatingNode, "Find property"); + await createBaseComponent(page, locatingNode, "Planning constraints"); }; -export const createPlanningConstraintsComponent = async ( - page: Page, - locatingNode: Locator - ) => { - await createBaseComponent(page, locatingNode, "Planning constraints"); - }; +export const createDrawBoundary = async (page: Page, locatingNode: Locator) => { + await createBaseComponent(page, locatingNode, "Draw boundary"); +}; async function createComponentOptions( options: string[], From a3af6fd1011842b280060e81522835815d8b5cfe Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:04:37 +0100 Subject: [PATCH 21/39] Use an enum instead of magic strings --- .../ui-driven/src/helpers/addComponent.ts | 123 +++++++++++++----- 1 file changed, 89 insertions(+), 34 deletions(-) diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index 526072a28f..35624af0dd 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -1,9 +1,25 @@ import { Locator, Page } from "@playwright/test"; +enum PlanXEditorComponent { + QUESTION = "Question", + NOTICE = "Notice", + CHECKLIST = "Checklist", + TEXT = "Text Input", + NUMBER = "Number Input", + DATE = "Date Input", + ADDRESS = "Address Input", + CONTACT = "Contact Input", + TASKLIST = "Task List", + REVIEW = "Review", + FIND_PROPERTY = "Find property", + PLANNING_CONSTRAINTS = "Planning constraints", + DRAW_BOUNDARY = "Draw boundary", +} + const createBaseComponent = async ( page: Page, locatingNode: Locator, - type: string, + type: PlanXEditorComponent, title?: string, options?: string[] ) => { @@ -12,29 +28,29 @@ const createBaseComponent = async ( await page.locator("select").selectOption({ label: type }); switch (type) { - case "Question": + case PlanXEditorComponent.QUESTION: await page.getByPlaceholder("Text").fill(title || ""); if (options) { await createComponentOptions(options, "add new", page); } break; - case "Notice": + case PlanXEditorComponent.NOTICE: await page.getByPlaceholder("Notice").fill(title || ""); break; - case "Checklist": + case PlanXEditorComponent.CHECKLIST: await page.getByPlaceholder("Text").fill(title || ""); if (options) { await createComponentOptions(options, "add new option", page); } break; - case "Text Input": + case PlanXEditorComponent.TEXT: await page.getByPlaceholder("Title").fill(title || ""); break; - case "Number Input": + case PlanXEditorComponent.NUMBER: await page.getByPlaceholder("Title").fill(title || ""); await page.getByPlaceholder("eg square metres").fill(options?.[0] || ""); break; - case "Date Input": + case PlanXEditorComponent.DATE: await page.getByPlaceholder("Title").fill(title || ""); // fill with hardcoded dates for now await page.locator("id=undefined-min-day").fill("01"); @@ -44,15 +60,15 @@ const createBaseComponent = async ( await page.locator("id=undefined-max-month").fill("12"); await page.locator("id=undefined-max-year").fill("2199"); break; - case "Address Input": + case PlanXEditorComponent.ADDRESS: await page.getByPlaceholder("Title").fill(title || ""); await page.getByPlaceholder("Data Field").fill(options?.[0] || ""); break; - case "Contact Input": + case PlanXEditorComponent.CONTACT: await page.getByPlaceholder("Title").fill(title || ""); await page.getByPlaceholder("Data Field").fill(options?.[0] || ""); break; - case "Task List": + case PlanXEditorComponent.TASKLIST: await page.getByPlaceholder("Main Title").fill(title || ""); if (options) { let index = 0; @@ -66,20 +82,20 @@ const createBaseComponent = async ( } } break; - case "Review": + case PlanXEditorComponent.REVIEW: // Don't need to change anything so dummy click await page .getByPlaceholder("Check your answers before sending your application") .click(); break; - case "Find property": + case PlanXEditorComponent.FIND_PROPERTY: // use default placeholder 'Find the property' await page.getByPlaceholder("Title").click(); break; - case "Planning constraints": + case PlanXEditorComponent.PLANNING_CONSTRAINTS: await page.getByPlaceholder(type).click(); break; - case "Draw boundary": + case PlanXEditorComponent.DRAW_BOUNDARY: page.getByPlaceholder(type); break; default: @@ -89,7 +105,7 @@ const createBaseComponent = async ( // convert type name to lowercase, with dashes if there are spaces // or custom name if it doesn't fit the pattern const buttonName = - type === "Find property" + type === PlanXEditorComponent.FIND_PROPERTY ? "find-property-merged" : type.toLowerCase().replace(/\s/g, "-"); @@ -110,7 +126,7 @@ export const createQuestionWithOptions = async ( await createBaseComponent( page, locatingNode, - "Question", + PlanXEditorComponent.QUESTION, questionText, options ); @@ -121,7 +137,12 @@ export const createNotice = async ( locatingNode: Locator, noticeText: string ) => { - await createBaseComponent(page, locatingNode, "Notice", noticeText); + await createBaseComponent( + page, + locatingNode, + PlanXEditorComponent.NOTICE, + noticeText + ); }; export const createChecklist = async ( @@ -133,7 +154,7 @@ export const createChecklist = async ( createBaseComponent( page, locatingNode, - "Checklist", + PlanXEditorComponent.CHECKLIST, checklistTitle, checklistOptions ); @@ -144,7 +165,12 @@ export const createTextInput = async ( locatingNode: Locator, inputTitle: string ) => { - await createBaseComponent(page, locatingNode, "Text Input", inputTitle); + await createBaseComponent( + page, + locatingNode, + PlanXEditorComponent.TEXT, + inputTitle + ); }; export const createNumberInput = async ( @@ -153,9 +179,13 @@ export const createNumberInput = async ( inputTitle: string, inputUnits: string ) => { - await createBaseComponent(page, locatingNode, "Number Input", inputTitle, [ - inputUnits, - ]); + await createBaseComponent( + page, + locatingNode, + PlanXEditorComponent.NUMBER, + inputTitle, + [inputUnits] + ); }; export const createDateInput = async ( @@ -163,7 +193,12 @@ export const createDateInput = async ( locatingNode: Locator, inputTitle: string ) => { - await createBaseComponent(page, locatingNode, "Date Input", inputTitle); + await createBaseComponent( + page, + locatingNode, + PlanXEditorComponent.DATE, + inputTitle + ); }; export const createAddressInput = async ( @@ -172,9 +207,13 @@ export const createAddressInput = async ( inputTitle: string, inputDataField: string ) => { - await createBaseComponent(page, locatingNode, "Address Input", inputTitle, [ - inputDataField, - ]); + await createBaseComponent( + page, + locatingNode, + PlanXEditorComponent.ADDRESS, + inputTitle, + [inputDataField] + ); }; export const createContactInput = async ( @@ -183,9 +222,13 @@ export const createContactInput = async ( inputTitle: string, inputDataField: string ) => { - await createBaseComponent(page, locatingNode, "Contact Input", inputTitle, [ - inputDataField, - ]); + await createBaseComponent( + page, + locatingNode, + PlanXEditorComponent.CONTACT, + inputTitle, + [inputDataField] + ); }; export const createTaskList = async ( @@ -197,29 +240,41 @@ export const createTaskList = async ( await createBaseComponent( page, locatingNode, - "Task List", + PlanXEditorComponent.TASKLIST, title, taskListOptions ); }; export const createReview = async (page: Page, locatingNode: Locator) => { - await createBaseComponent(page, locatingNode, "Review"); + await createBaseComponent(page, locatingNode, PlanXEditorComponent.REVIEW); }; export const createFindProperty = async (page: Page, locatingNode: Locator) => { - await createBaseComponent(page, locatingNode, "Find property"); + await createBaseComponent( + page, + locatingNode, + PlanXEditorComponent.FIND_PROPERTY + ); }; export const createPlanningConstraints = async ( page: Page, locatingNode: Locator ) => { - await createBaseComponent(page, locatingNode, "Planning constraints"); + await createBaseComponent( + page, + locatingNode, + PlanXEditorComponent.PLANNING_CONSTRAINTS + ); }; export const createDrawBoundary = async (page: Page, locatingNode: Locator) => { - await createBaseComponent(page, locatingNode, "Draw boundary"); + await createBaseComponent( + page, + locatingNode, + PlanXEditorComponent.DRAW_BOUNDARY + ); }; async function createComponentOptions( From fe9d6455312104e63fa4f0944ca3df532dd92258 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:13:09 +0100 Subject: [PATCH 22/39] Lint fix --- .../src/create-flow/create-flow.spec.ts | 28 +++++------ .../ui-driven/src/helpers/addComponent.ts | 48 +++++++++---------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 57933259e8..83653a2d1f 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -67,7 +67,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)) + (req) => (isRepeatedRequestMade = isGetUserRequest(req)), ); Promise.all([ @@ -125,7 +125,7 @@ test.describe("Navigation", () => { "No", ]); await expect( - page.locator("a").filter({ hasText: questionText }) + page.locator("a").filter({ hasText: questionText }), ).toBeVisible(); // Add a notice to the "Yes" path @@ -135,7 +135,7 @@ test.describe("Navigation", () => { await createNotice( page, yesBranch.locator(".hanger > a"), - yesBranchNoticeText + yesBranchNoticeText, ); // Add a notice to the "No" path @@ -144,7 +144,7 @@ test.describe("Navigation", () => { await createNotice( page, noBranch.locator(".hanger > a"), - noBranchNoticeText + noBranchNoticeText, ); // TODO: find a nicer way to find the next node @@ -169,7 +169,7 @@ test.describe("Navigation", () => { page, nextNode, "What is your address?", - "some data field" + "some data field", ); nextNode = page.locator(".hanger > a").nth(11); @@ -177,7 +177,7 @@ test.describe("Navigation", () => { page, nextNode, "What is your contact info?", - "some data field" + "some data field", ); nextNode = page.locator(".hanger > a").nth(12); @@ -214,7 +214,7 @@ test.describe("Navigation", () => { await expect(nodes.getByText("Planning constraints")).toBeVisible(); await expect( - nodes.getByText("Check your answers before sending your application") + nodes.getByText("Check your answers before sending your application"), ).toBeVisible(); }); @@ -229,7 +229,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -263,11 +263,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }) + page.getByRole("heading", { level: 1, name: "Offline" }), ).toBeVisible(); }); @@ -286,7 +286,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully") + page.getByText("Service settings updated successfully"), ).toBeVisible(); // Exit back to main Editor page @@ -309,13 +309,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }) + page.locator("h1", { hasText: "Yes! this is a test" }), ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -323,7 +323,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }) + page.locator("h1", { hasText: "Sorry, this is a test" }), ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index 35624af0dd..7b9a99ab65 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -21,7 +21,7 @@ const createBaseComponent = async ( locatingNode: Locator, type: PlanXEditorComponent, title?: string, - options?: string[] + options?: string[], ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -121,27 +121,27 @@ export const createQuestionWithOptions = async ( page: Page, locatingNode: Locator, questionText: string, - options: string[] + options: string[], ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.QUESTION, questionText, - options + options, ); }; export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string + noticeText: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.NOTICE, - noticeText + noticeText, ); }; @@ -149,27 +149,27 @@ export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[] + checklistOptions: string[], ) => { createBaseComponent( page, locatingNode, PlanXEditorComponent.CHECKLIST, checklistTitle, - checklistOptions + checklistOptions, ); }; export const createTextInput = async ( page: Page, locatingNode: Locator, - inputTitle: string + inputTitle: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.TEXT, - inputTitle + inputTitle, ); }; @@ -177,27 +177,27 @@ export const createNumberInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputUnits: string + inputUnits: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.NUMBER, inputTitle, - [inputUnits] + [inputUnits], ); }; export const createDateInput = async ( page: Page, locatingNode: Locator, - inputTitle: string + inputTitle: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.DATE, - inputTitle + inputTitle, ); }; @@ -205,14 +205,14 @@ export const createAddressInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string + inputDataField: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.ADDRESS, inputTitle, - [inputDataField] + [inputDataField], ); }; @@ -220,14 +220,14 @@ export const createContactInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string + inputDataField: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.CONTACT, inputTitle, - [inputDataField] + [inputDataField], ); }; @@ -235,14 +235,14 @@ export const createTaskList = async ( page: Page, locatingNode: Locator, title: string, - taskListOptions: string[] + taskListOptions: string[], ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.TASKLIST, title, - taskListOptions + taskListOptions, ); }; @@ -254,18 +254,18 @@ export const createFindProperty = async (page: Page, locatingNode: Locator) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.FIND_PROPERTY + PlanXEditorComponent.FIND_PROPERTY, ); }; export const createPlanningConstraints = async ( page: Page, - locatingNode: Locator + locatingNode: Locator, ) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.PLANNING_CONSTRAINTS + PlanXEditorComponent.PLANNING_CONSTRAINTS, ); }; @@ -273,14 +273,14 @@ export const createDrawBoundary = async (page: Page, locatingNode: Locator) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.DRAW_BOUNDARY + PlanXEditorComponent.DRAW_BOUNDARY, ); }; async function createComponentOptions( options: string[], buttonText: string, - page: Page + page: Page, ) { let index = 0; for (const option of options) { From d48a2051b98a4c53baf5c72659b76414f8734cea Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:31:02 +0100 Subject: [PATCH 23/39] Create next steps component --- .../src/create-flow/create-flow.spec.ts | 37 +++++---- .../ui-driven/src/helpers/addComponent.ts | 76 +++++++++++++------ 2 files changed, 74 insertions(+), 39 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 83653a2d1f..47f9500f4a 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -6,6 +6,7 @@ import { createDateInput, createDrawBoundary, createFindProperty, + createNextSteps, createNotice, createNumberInput, createPlanningConstraints, @@ -67,7 +68,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)), + (req) => (isRepeatedRequestMade = isGetUserRequest(req)) ); Promise.all([ @@ -125,7 +126,7 @@ test.describe("Navigation", () => { "No", ]); await expect( - page.locator("a").filter({ hasText: questionText }), + page.locator("a").filter({ hasText: questionText }) ).toBeVisible(); // Add a notice to the "Yes" path @@ -135,7 +136,7 @@ test.describe("Navigation", () => { await createNotice( page, yesBranch.locator(".hanger > a"), - yesBranchNoticeText, + yesBranchNoticeText ); // Add a notice to the "No" path @@ -144,7 +145,7 @@ test.describe("Navigation", () => { await createNotice( page, noBranch.locator(".hanger > a"), - noBranchNoticeText, + noBranchNoticeText ); // TODO: find a nicer way to find the next node @@ -169,7 +170,7 @@ test.describe("Navigation", () => { page, nextNode, "What is your address?", - "some data field", + "some data field" ); nextNode = page.locator(".hanger > a").nth(11); @@ -177,7 +178,7 @@ test.describe("Navigation", () => { page, nextNode, "What is your contact info?", - "some data field", + "some data field" ); nextNode = page.locator(".hanger > a").nth(12); @@ -196,6 +197,12 @@ test.describe("Navigation", () => { await createPlanningConstraints(page, nextNode); nextNode = page.locator(".hanger > a").nth(16); + await createNextSteps(page, nextNode, [ + "A possible next step", + "Another option", + ]); + + nextNode = page.locator(".hanger > a").nth(18); await createReview(page, nextNode); const nodes = page.locator(".card"); @@ -212,9 +219,9 @@ test.describe("Navigation", () => { await expect(nodes.getByText("Find property")).toBeVisible(); await expect(nodes.getByText("Confirm your location plan")).toBeVisible(); await expect(nodes.getByText("Planning constraints")).toBeVisible(); - + await expect(nodes.getByText("Next steps")).toBeVisible(); await expect( - nodes.getByText("Check your answers before sending your application"), + nodes.getByText("Check your answers before sending your application") ).toBeVisible(); }); @@ -229,7 +236,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -263,11 +270,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }), + page.getByRole("heading", { level: 1, name: "Offline" }) ).toBeVisible(); }); @@ -286,7 +293,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully"), + page.getByText("Service settings updated successfully") ).toBeVisible(); // Exit back to main Editor page @@ -309,13 +316,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }), + page.locator("h1", { hasText: "Yes! this is a test" }) ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -323,7 +330,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }), + page.locator("h1", { hasText: "Sorry, this is a test" }) ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index 7b9a99ab65..b3f8894f1f 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -14,6 +14,7 @@ enum PlanXEditorComponent { FIND_PROPERTY = "Find property", PLANNING_CONSTRAINTS = "Planning constraints", DRAW_BOUNDARY = "Draw boundary", + NEXT_STEPS = "Next steps", } const createBaseComponent = async ( @@ -21,7 +22,7 @@ const createBaseComponent = async ( locatingNode: Locator, type: PlanXEditorComponent, title?: string, - options?: string[], + options?: string[] ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -98,6 +99,19 @@ const createBaseComponent = async ( case PlanXEditorComponent.DRAW_BOUNDARY: page.getByPlaceholder(type); break; + case PlanXEditorComponent.NEXT_STEPS: + if (options) { + let index = 0; + for (const option of options) { + await page.locator("button").filter({ hasText: "add new" }).click(); + await page + .getByPlaceholder("Title") + .nth(index + 1) // ignore the main title field + .fill(option); + index++; + } + } + break; default: throw new Error(`Unsupported type: ${type}`); } @@ -121,27 +135,27 @@ export const createQuestionWithOptions = async ( page: Page, locatingNode: Locator, questionText: string, - options: string[], + options: string[] ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.QUESTION, questionText, - options, + options ); }; export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string, + noticeText: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.NOTICE, - noticeText, + noticeText ); }; @@ -149,27 +163,27 @@ export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[], + checklistOptions: string[] ) => { createBaseComponent( page, locatingNode, PlanXEditorComponent.CHECKLIST, checklistTitle, - checklistOptions, + checklistOptions ); }; export const createTextInput = async ( page: Page, locatingNode: Locator, - inputTitle: string, + inputTitle: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.TEXT, - inputTitle, + inputTitle ); }; @@ -177,27 +191,27 @@ export const createNumberInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputUnits: string, + inputUnits: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.NUMBER, inputTitle, - [inputUnits], + [inputUnits] ); }; export const createDateInput = async ( page: Page, locatingNode: Locator, - inputTitle: string, + inputTitle: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.DATE, - inputTitle, + inputTitle ); }; @@ -205,14 +219,14 @@ export const createAddressInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string, + inputDataField: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.ADDRESS, inputTitle, - [inputDataField], + [inputDataField] ); }; @@ -220,14 +234,14 @@ export const createContactInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string, + inputDataField: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.CONTACT, inputTitle, - [inputDataField], + [inputDataField] ); }; @@ -235,14 +249,14 @@ export const createTaskList = async ( page: Page, locatingNode: Locator, title: string, - taskListOptions: string[], + taskListOptions: string[] ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.TASKLIST, title, - taskListOptions, + taskListOptions ); }; @@ -254,18 +268,18 @@ export const createFindProperty = async (page: Page, locatingNode: Locator) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.FIND_PROPERTY, + PlanXEditorComponent.FIND_PROPERTY ); }; export const createPlanningConstraints = async ( page: Page, - locatingNode: Locator, + locatingNode: Locator ) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.PLANNING_CONSTRAINTS, + PlanXEditorComponent.PLANNING_CONSTRAINTS ); }; @@ -273,14 +287,28 @@ export const createDrawBoundary = async (page: Page, locatingNode: Locator) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.DRAW_BOUNDARY, + PlanXEditorComponent.DRAW_BOUNDARY + ); +}; + +export const createNextSteps = async ( + page: Page, + locatingNode: Locator, + nextSteps: string[] +) => { + await createBaseComponent( + page, + locatingNode, + PlanXEditorComponent.NEXT_STEPS, + undefined, + nextSteps ); }; async function createComponentOptions( options: string[], buttonText: string, - page: Page, + page: Page ) { let index = 0; for (const option of options) { From 614626ed4c8805a826ef58142544c8f2e381cbad Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:31:58 +0100 Subject: [PATCH 24/39] Lint fix again --- .../src/create-flow/create-flow.spec.ts | 28 +++++----- .../ui-driven/src/helpers/addComponent.ts | 52 +++++++++---------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 47f9500f4a..9a59850101 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -68,7 +68,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)) + (req) => (isRepeatedRequestMade = isGetUserRequest(req)), ); Promise.all([ @@ -126,7 +126,7 @@ test.describe("Navigation", () => { "No", ]); await expect( - page.locator("a").filter({ hasText: questionText }) + page.locator("a").filter({ hasText: questionText }), ).toBeVisible(); // Add a notice to the "Yes" path @@ -136,7 +136,7 @@ test.describe("Navigation", () => { await createNotice( page, yesBranch.locator(".hanger > a"), - yesBranchNoticeText + yesBranchNoticeText, ); // Add a notice to the "No" path @@ -145,7 +145,7 @@ test.describe("Navigation", () => { await createNotice( page, noBranch.locator(".hanger > a"), - noBranchNoticeText + noBranchNoticeText, ); // TODO: find a nicer way to find the next node @@ -170,7 +170,7 @@ test.describe("Navigation", () => { page, nextNode, "What is your address?", - "some data field" + "some data field", ); nextNode = page.locator(".hanger > a").nth(11); @@ -178,7 +178,7 @@ test.describe("Navigation", () => { page, nextNode, "What is your contact info?", - "some data field" + "some data field", ); nextNode = page.locator(".hanger > a").nth(12); @@ -221,7 +221,7 @@ test.describe("Navigation", () => { await expect(nodes.getByText("Planning constraints")).toBeVisible(); await expect(nodes.getByText("Next steps")).toBeVisible(); await expect( - nodes.getByText("Check your answers before sending your application") + nodes.getByText("Check your answers before sending your application"), ).toBeVisible(); }); @@ -236,7 +236,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -270,11 +270,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }) + page.getByRole("heading", { level: 1, name: "Offline" }), ).toBeVisible(); }); @@ -293,7 +293,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully") + page.getByText("Service settings updated successfully"), ).toBeVisible(); // Exit back to main Editor page @@ -316,13 +316,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }) + page.locator("h1", { hasText: "Yes! this is a test" }), ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -330,7 +330,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }) + page.locator("h1", { hasText: "Sorry, this is a test" }), ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index b3f8894f1f..e58f29da4e 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -22,7 +22,7 @@ const createBaseComponent = async ( locatingNode: Locator, type: PlanXEditorComponent, title?: string, - options?: string[] + options?: string[], ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -135,27 +135,27 @@ export const createQuestionWithOptions = async ( page: Page, locatingNode: Locator, questionText: string, - options: string[] + options: string[], ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.QUESTION, questionText, - options + options, ); }; export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string + noticeText: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.NOTICE, - noticeText + noticeText, ); }; @@ -163,27 +163,27 @@ export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[] + checklistOptions: string[], ) => { createBaseComponent( page, locatingNode, PlanXEditorComponent.CHECKLIST, checklistTitle, - checklistOptions + checklistOptions, ); }; export const createTextInput = async ( page: Page, locatingNode: Locator, - inputTitle: string + inputTitle: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.TEXT, - inputTitle + inputTitle, ); }; @@ -191,27 +191,27 @@ export const createNumberInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputUnits: string + inputUnits: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.NUMBER, inputTitle, - [inputUnits] + [inputUnits], ); }; export const createDateInput = async ( page: Page, locatingNode: Locator, - inputTitle: string + inputTitle: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.DATE, - inputTitle + inputTitle, ); }; @@ -219,14 +219,14 @@ export const createAddressInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string + inputDataField: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.ADDRESS, inputTitle, - [inputDataField] + [inputDataField], ); }; @@ -234,14 +234,14 @@ export const createContactInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string + inputDataField: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.CONTACT, inputTitle, - [inputDataField] + [inputDataField], ); }; @@ -249,14 +249,14 @@ export const createTaskList = async ( page: Page, locatingNode: Locator, title: string, - taskListOptions: string[] + taskListOptions: string[], ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.TASKLIST, title, - taskListOptions + taskListOptions, ); }; @@ -268,18 +268,18 @@ export const createFindProperty = async (page: Page, locatingNode: Locator) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.FIND_PROPERTY + PlanXEditorComponent.FIND_PROPERTY, ); }; export const createPlanningConstraints = async ( page: Page, - locatingNode: Locator + locatingNode: Locator, ) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.PLANNING_CONSTRAINTS + PlanXEditorComponent.PLANNING_CONSTRAINTS, ); }; @@ -287,28 +287,28 @@ export const createDrawBoundary = async (page: Page, locatingNode: Locator) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.DRAW_BOUNDARY + PlanXEditorComponent.DRAW_BOUNDARY, ); }; export const createNextSteps = async ( page: Page, locatingNode: Locator, - nextSteps: string[] + nextSteps: string[], ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.NEXT_STEPS, undefined, - nextSteps + nextSteps, ); }; async function createComponentOptions( options: string[], buttonText: string, - page: Page + page: Page, ) { let index = 0; for (const option of options) { From fa6ce2e4785d8b13d1613229d03938528d9483e6 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Thu, 5 Sep 2024 19:22:05 +0100 Subject: [PATCH 25/39] Add file upload component --- .../src/create-flow/create-flow.spec.ts | 36 +++++---- .../ui-driven/src/helpers/addComponent.ts | 75 +++++++++++-------- 2 files changed, 66 insertions(+), 45 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 9a59850101..920f1b06c8 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -5,6 +5,7 @@ import { createContactInput, createDateInput, createDrawBoundary, + createFileUpload, createFindProperty, createNextSteps, createNotice, @@ -68,7 +69,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)), + (req) => (isRepeatedRequestMade = isGetUserRequest(req)) ); Promise.all([ @@ -126,7 +127,7 @@ test.describe("Navigation", () => { "No", ]); await expect( - page.locator("a").filter({ hasText: questionText }), + page.locator("a").filter({ hasText: questionText }) ).toBeVisible(); // Add a notice to the "Yes" path @@ -136,7 +137,7 @@ test.describe("Navigation", () => { await createNotice( page, yesBranch.locator(".hanger > a"), - yesBranchNoticeText, + yesBranchNoticeText ); // Add a notice to the "No" path @@ -145,7 +146,7 @@ test.describe("Navigation", () => { await createNotice( page, noBranch.locator(".hanger > a"), - noBranchNoticeText, + noBranchNoticeText ); // TODO: find a nicer way to find the next node @@ -170,7 +171,7 @@ test.describe("Navigation", () => { page, nextNode, "What is your address?", - "some data field", + "some data field" ); nextNode = page.locator(".hanger > a").nth(11); @@ -178,7 +179,7 @@ test.describe("Navigation", () => { page, nextNode, "What is your contact info?", - "some data field", + "some data field" ); nextNode = page.locator(".hanger > a").nth(12); @@ -197,12 +198,15 @@ test.describe("Navigation", () => { await createPlanningConstraints(page, nextNode); nextNode = page.locator(".hanger > a").nth(16); + await createFileUpload(page, nextNode, "some data field"); + + nextNode = page.locator(".hanger > a").nth(17); await createNextSteps(page, nextNode, [ "A possible next step", "Another option", ]); - nextNode = page.locator(".hanger > a").nth(18); + nextNode = page.locator(".hanger > a").nth(19); await createReview(page, nextNode); const nodes = page.locator(".card"); @@ -219,9 +223,11 @@ test.describe("Navigation", () => { await expect(nodes.getByText("Find property")).toBeVisible(); await expect(nodes.getByText("Confirm your location plan")).toBeVisible(); await expect(nodes.getByText("Planning constraints")).toBeVisible(); + await expect(nodes.getByText("File upload")).toBeVisible(); + await expect(nodes.getByText("Next steps")).toBeVisible(); await expect( - nodes.getByText("Check your answers before sending your application"), + nodes.getByText("Check your answers before sending your application") ).toBeVisible(); }); @@ -236,7 +242,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -270,11 +276,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }), + page.getByRole("heading", { level: 1, name: "Offline" }) ).toBeVisible(); }); @@ -293,7 +299,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully"), + page.getByText("Service settings updated successfully") ).toBeVisible(); // Exit back to main Editor page @@ -316,13 +322,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }), + page.locator("h1", { hasText: "Yes! this is a test" }) ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -330,7 +336,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }), + page.locator("h1", { hasText: "Sorry, this is a test" }) ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index e58f29da4e..8d2a56267b 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -15,6 +15,7 @@ enum PlanXEditorComponent { PLANNING_CONSTRAINTS = "Planning constraints", DRAW_BOUNDARY = "Draw boundary", NEXT_STEPS = "Next steps", + FILE_UPLOAD = "File Upload", } const createBaseComponent = async ( @@ -22,7 +23,7 @@ const createBaseComponent = async ( locatingNode: Locator, type: PlanXEditorComponent, title?: string, - options?: string[], + options?: string[] ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -90,14 +91,10 @@ const createBaseComponent = async ( .click(); break; case PlanXEditorComponent.FIND_PROPERTY: - // use default placeholder 'Find the property' - await page.getByPlaceholder("Title").click(); break; case PlanXEditorComponent.PLANNING_CONSTRAINTS: - await page.getByPlaceholder(type).click(); break; case PlanXEditorComponent.DRAW_BOUNDARY: - page.getByPlaceholder(type); break; case PlanXEditorComponent.NEXT_STEPS: if (options) { @@ -111,6 +108,10 @@ const createBaseComponent = async ( index++; } } + break; + case PlanXEditorComponent.FILE_UPLOAD: + await page.getByPlaceholder("Data Field").fill(options?.[0] || ""); + break; default: throw new Error(`Unsupported type: ${type}`); @@ -135,27 +136,27 @@ export const createQuestionWithOptions = async ( page: Page, locatingNode: Locator, questionText: string, - options: string[], + options: string[] ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.QUESTION, questionText, - options, + options ); }; export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string, + noticeText: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.NOTICE, - noticeText, + noticeText ); }; @@ -163,27 +164,27 @@ export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[], + checklistOptions: string[] ) => { createBaseComponent( page, locatingNode, PlanXEditorComponent.CHECKLIST, checklistTitle, - checklistOptions, + checklistOptions ); }; export const createTextInput = async ( page: Page, locatingNode: Locator, - inputTitle: string, + inputTitle: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.TEXT, - inputTitle, + inputTitle ); }; @@ -191,27 +192,27 @@ export const createNumberInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputUnits: string, + inputUnits: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.NUMBER, inputTitle, - [inputUnits], + [inputUnits] ); }; export const createDateInput = async ( page: Page, locatingNode: Locator, - inputTitle: string, + inputTitle: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.DATE, - inputTitle, + inputTitle ); }; @@ -219,14 +220,14 @@ export const createAddressInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string, + inputDataField: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.ADDRESS, inputTitle, - [inputDataField], + [inputDataField] ); }; @@ -234,14 +235,14 @@ export const createContactInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string, + inputDataField: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.CONTACT, inputTitle, - [inputDataField], + [inputDataField] ); }; @@ -249,14 +250,14 @@ export const createTaskList = async ( page: Page, locatingNode: Locator, title: string, - taskListOptions: string[], + taskListOptions: string[] ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.TASKLIST, title, - taskListOptions, + taskListOptions ); }; @@ -268,18 +269,18 @@ export const createFindProperty = async (page: Page, locatingNode: Locator) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.FIND_PROPERTY, + PlanXEditorComponent.FIND_PROPERTY ); }; export const createPlanningConstraints = async ( page: Page, - locatingNode: Locator, + locatingNode: Locator ) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.PLANNING_CONSTRAINTS, + PlanXEditorComponent.PLANNING_CONSTRAINTS ); }; @@ -287,28 +288,42 @@ export const createDrawBoundary = async (page: Page, locatingNode: Locator) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.DRAW_BOUNDARY, + PlanXEditorComponent.DRAW_BOUNDARY ); }; export const createNextSteps = async ( page: Page, locatingNode: Locator, - nextSteps: string[], + nextSteps: string[] ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.NEXT_STEPS, undefined, - nextSteps, + nextSteps + ); +}; + +export const createFileUpload = async ( + page: Page, + locatingNode: Locator, + dataField: string +) => { + await createBaseComponent( + page, + locatingNode, + PlanXEditorComponent.FILE_UPLOAD, + undefined, + [dataField] ); }; async function createComponentOptions( options: string[], buttonText: string, - page: Page, + page: Page ) { let index = 0; for (const option of options) { From ceba07458fac5784d75fb624b86d6eb296791554 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Thu, 5 Sep 2024 19:25:09 +0100 Subject: [PATCH 26/39] I really need to learn to run lint fix before committing --- .../src/create-flow/create-flow.spec.ts | 28 +++++----- .../ui-driven/src/helpers/addComponent.ts | 56 +++++++++---------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 920f1b06c8..cd1dc4790d 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -69,7 +69,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)) + (req) => (isRepeatedRequestMade = isGetUserRequest(req)), ); Promise.all([ @@ -127,7 +127,7 @@ test.describe("Navigation", () => { "No", ]); await expect( - page.locator("a").filter({ hasText: questionText }) + page.locator("a").filter({ hasText: questionText }), ).toBeVisible(); // Add a notice to the "Yes" path @@ -137,7 +137,7 @@ test.describe("Navigation", () => { await createNotice( page, yesBranch.locator(".hanger > a"), - yesBranchNoticeText + yesBranchNoticeText, ); // Add a notice to the "No" path @@ -146,7 +146,7 @@ test.describe("Navigation", () => { await createNotice( page, noBranch.locator(".hanger > a"), - noBranchNoticeText + noBranchNoticeText, ); // TODO: find a nicer way to find the next node @@ -171,7 +171,7 @@ test.describe("Navigation", () => { page, nextNode, "What is your address?", - "some data field" + "some data field", ); nextNode = page.locator(".hanger > a").nth(11); @@ -179,7 +179,7 @@ test.describe("Navigation", () => { page, nextNode, "What is your contact info?", - "some data field" + "some data field", ); nextNode = page.locator(".hanger > a").nth(12); @@ -227,7 +227,7 @@ test.describe("Navigation", () => { await expect(nodes.getByText("Next steps")).toBeVisible(); await expect( - nodes.getByText("Check your answers before sending your application") + nodes.getByText("Check your answers before sending your application"), ).toBeVisible(); }); @@ -242,7 +242,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -276,11 +276,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }) + page.getByRole("heading", { level: 1, name: "Offline" }), ).toBeVisible(); }); @@ -299,7 +299,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully") + page.getByText("Service settings updated successfully"), ).toBeVisible(); // Exit back to main Editor page @@ -322,13 +322,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }) + page.locator("h1", { hasText: "Yes! this is a test" }), ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -336,7 +336,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }) + page.locator("h1", { hasText: "Sorry, this is a test" }), ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index 8d2a56267b..4ba616b79b 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -23,7 +23,7 @@ const createBaseComponent = async ( locatingNode: Locator, type: PlanXEditorComponent, title?: string, - options?: string[] + options?: string[], ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -136,27 +136,27 @@ export const createQuestionWithOptions = async ( page: Page, locatingNode: Locator, questionText: string, - options: string[] + options: string[], ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.QUESTION, questionText, - options + options, ); }; export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string + noticeText: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.NOTICE, - noticeText + noticeText, ); }; @@ -164,27 +164,27 @@ export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[] + checklistOptions: string[], ) => { createBaseComponent( page, locatingNode, PlanXEditorComponent.CHECKLIST, checklistTitle, - checklistOptions + checklistOptions, ); }; export const createTextInput = async ( page: Page, locatingNode: Locator, - inputTitle: string + inputTitle: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.TEXT, - inputTitle + inputTitle, ); }; @@ -192,27 +192,27 @@ export const createNumberInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputUnits: string + inputUnits: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.NUMBER, inputTitle, - [inputUnits] + [inputUnits], ); }; export const createDateInput = async ( page: Page, locatingNode: Locator, - inputTitle: string + inputTitle: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.DATE, - inputTitle + inputTitle, ); }; @@ -220,14 +220,14 @@ export const createAddressInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string + inputDataField: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.ADDRESS, inputTitle, - [inputDataField] + [inputDataField], ); }; @@ -235,14 +235,14 @@ export const createContactInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string + inputDataField: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.CONTACT, inputTitle, - [inputDataField] + [inputDataField], ); }; @@ -250,14 +250,14 @@ export const createTaskList = async ( page: Page, locatingNode: Locator, title: string, - taskListOptions: string[] + taskListOptions: string[], ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.TASKLIST, title, - taskListOptions + taskListOptions, ); }; @@ -269,18 +269,18 @@ export const createFindProperty = async (page: Page, locatingNode: Locator) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.FIND_PROPERTY + PlanXEditorComponent.FIND_PROPERTY, ); }; export const createPlanningConstraints = async ( page: Page, - locatingNode: Locator + locatingNode: Locator, ) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.PLANNING_CONSTRAINTS + PlanXEditorComponent.PLANNING_CONSTRAINTS, ); }; @@ -288,42 +288,42 @@ export const createDrawBoundary = async (page: Page, locatingNode: Locator) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.DRAW_BOUNDARY + PlanXEditorComponent.DRAW_BOUNDARY, ); }; export const createNextSteps = async ( page: Page, locatingNode: Locator, - nextSteps: string[] + nextSteps: string[], ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.NEXT_STEPS, undefined, - nextSteps + nextSteps, ); }; export const createFileUpload = async ( page: Page, locatingNode: Locator, - dataField: string + dataField: string, ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.FILE_UPLOAD, undefined, - [dataField] + [dataField], ); }; async function createComponentOptions( options: string[], buttonText: string, - page: Page + page: Page, ) { let index = 0; for (const option of options) { From 780964a6dcdc7690e9834f469786a3c0b8b908cb Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:12:47 +0100 Subject: [PATCH 27/39] getNextNode as function --- .../src/create-flow/create-flow.spec.ts | 75 +++++++------------ .../ui-driven/src/helpers/addComponent.ts | 58 +++++++------- 2 files changed, 58 insertions(+), 75 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index cd1dc4790d..53763248d3 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -69,7 +69,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)), + (req) => (isRepeatedRequestMade = isGetUserRequest(req)) ); Promise.all([ @@ -127,7 +127,7 @@ test.describe("Navigation", () => { "No", ]); await expect( - page.locator("a").filter({ hasText: questionText }), + page.locator("a").filter({ hasText: questionText }) ).toBeVisible(); // Add a notice to the "Yes" path @@ -137,7 +137,7 @@ test.describe("Navigation", () => { await createNotice( page, yesBranch.locator(".hanger > a"), - yesBranchNoticeText, + yesBranchNoticeText ); // Add a notice to the "No" path @@ -146,68 +146,51 @@ test.describe("Navigation", () => { await createNotice( page, noBranch.locator(".hanger > a"), - noBranchNoticeText, + noBranchNoticeText ); - // TODO: find a nicer way to find the next node - let nextNode = page.locator(".hanger > a").nth(5); - await createChecklist(page, nextNode, "A checklist title", [ + const getNextNode = () => page.locator(".hanger > a").last(); + + await createChecklist(page, getNextNode(), "A checklist title", [ "Checklist item 1", "Second checklist item", "The third checklist item", ]); - nextNode = page.locator(".hanger > a").nth(7); - await createTextInput(page, nextNode, "Tell us about your trees."); - - nextNode = page.locator(".hanger > a").nth(8); - await createNumberInput(page, nextNode, "How old are you?", "years"); - - nextNode = page.locator(".hanger > a").nth(9); - await createDateInput(page, nextNode, "When is your birthday?"); + await createTextInput(page, getNextNode(), "Tell us about your trees."); + await createNumberInput(page, getNextNode(), "How old are you?", "years"); + await createDateInput(page, getNextNode(), "When is your birthday?"); - nextNode = page.locator(".hanger > a").nth(10); await createAddressInput( page, - nextNode, + getNextNode(), "What is your address?", - "some data field", + "some data field" ); - nextNode = page.locator(".hanger > a").nth(11); await createContactInput( page, - nextNode, + getNextNode(), "What is your contact info?", - "some data field", + "some data field" ); - nextNode = page.locator(".hanger > a").nth(12); - await createTaskList(page, nextNode, "What you should do next", [ + await createTaskList(page, getNextNode(), "What you should do next", [ "Have a cup of tea", "Continue through this flow", ]); - nextNode = page.locator(".hanger > a").nth(13); - await createFindProperty(page, nextNode); - - nextNode = page.locator(".hanger > a").nth(14); - await createDrawBoundary(page, nextNode); - - nextNode = page.locator(".hanger > a").nth(15); - await createPlanningConstraints(page, nextNode); - - nextNode = page.locator(".hanger > a").nth(16); - await createFileUpload(page, nextNode, "some data field"); + await createFindProperty(page, getNextNode()); + await createDrawBoundary(page, getNextNode()); + await createPlanningConstraints(page, getNextNode()); + await createFileUpload(page, getNextNode(), "some data field"); - nextNode = page.locator(".hanger > a").nth(17); - await createNextSteps(page, nextNode, [ + await createNextSteps(page, getNextNode(), [ "A possible next step", "Another option", ]); - nextNode = page.locator(".hanger > a").nth(19); - await createReview(page, nextNode); + await createReview(page, getNextNode()); const nodes = page.locator(".card"); await expect(nodes.getByText(questionText)).toBeVisible(); @@ -227,7 +210,7 @@ test.describe("Navigation", () => { await expect(nodes.getByText("Next steps")).toBeVisible(); await expect( - nodes.getByText("Check your answers before sending your application"), + nodes.getByText("Check your answers before sending your application") ).toBeVisible(); }); @@ -242,7 +225,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -276,11 +259,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }), + page.getByRole("heading", { level: 1, name: "Offline" }) ).toBeVisible(); }); @@ -299,7 +282,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully"), + page.getByText("Service settings updated successfully") ).toBeVisible(); // Exit back to main Editor page @@ -322,13 +305,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }), + page.locator("h1", { hasText: "Yes! this is a test" }) ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -336,7 +319,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }), + page.locator("h1", { hasText: "Sorry, this is a test" }) ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index 4ba616b79b..64efb20cdd 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -23,7 +23,7 @@ const createBaseComponent = async ( locatingNode: Locator, type: PlanXEditorComponent, title?: string, - options?: string[], + options?: string[] ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -136,27 +136,27 @@ export const createQuestionWithOptions = async ( page: Page, locatingNode: Locator, questionText: string, - options: string[], + options: string[] ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.QUESTION, questionText, - options, + options ); }; export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string, + noticeText: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.NOTICE, - noticeText, + noticeText ); }; @@ -164,27 +164,27 @@ export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[], + checklistOptions: string[] ) => { - createBaseComponent( + await createBaseComponent( page, locatingNode, PlanXEditorComponent.CHECKLIST, checklistTitle, - checklistOptions, + checklistOptions ); }; export const createTextInput = async ( page: Page, locatingNode: Locator, - inputTitle: string, + inputTitle: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.TEXT, - inputTitle, + inputTitle ); }; @@ -192,27 +192,27 @@ export const createNumberInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputUnits: string, + inputUnits: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.NUMBER, inputTitle, - [inputUnits], + [inputUnits] ); }; export const createDateInput = async ( page: Page, locatingNode: Locator, - inputTitle: string, + inputTitle: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.DATE, - inputTitle, + inputTitle ); }; @@ -220,14 +220,14 @@ export const createAddressInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string, + inputDataField: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.ADDRESS, inputTitle, - [inputDataField], + [inputDataField] ); }; @@ -235,14 +235,14 @@ export const createContactInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string, + inputDataField: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.CONTACT, inputTitle, - [inputDataField], + [inputDataField] ); }; @@ -250,14 +250,14 @@ export const createTaskList = async ( page: Page, locatingNode: Locator, title: string, - taskListOptions: string[], + taskListOptions: string[] ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.TASKLIST, title, - taskListOptions, + taskListOptions ); }; @@ -269,18 +269,18 @@ export const createFindProperty = async (page: Page, locatingNode: Locator) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.FIND_PROPERTY, + PlanXEditorComponent.FIND_PROPERTY ); }; export const createPlanningConstraints = async ( page: Page, - locatingNode: Locator, + locatingNode: Locator ) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.PLANNING_CONSTRAINTS, + PlanXEditorComponent.PLANNING_CONSTRAINTS ); }; @@ -288,42 +288,42 @@ export const createDrawBoundary = async (page: Page, locatingNode: Locator) => { await createBaseComponent( page, locatingNode, - PlanXEditorComponent.DRAW_BOUNDARY, + PlanXEditorComponent.DRAW_BOUNDARY ); }; export const createNextSteps = async ( page: Page, locatingNode: Locator, - nextSteps: string[], + nextSteps: string[] ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.NEXT_STEPS, undefined, - nextSteps, + nextSteps ); }; export const createFileUpload = async ( page: Page, locatingNode: Locator, - dataField: string, + dataField: string ) => { await createBaseComponent( page, locatingNode, PlanXEditorComponent.FILE_UPLOAD, undefined, - [dataField], + [dataField] ); }; async function createComponentOptions( options: string[], buttonText: string, - page: Page, + page: Page ) { let index = 0; for (const option of options) { From e3eaf8d875c7e86eb0a5f18b473e91bdc28a4c8e Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:32:43 +0100 Subject: [PATCH 28/39] Simplify button selector and use existing enum from planx-core --- .../ui-driven/src/helpers/addComponent.ts | 105 ++++++------------ 1 file changed, 34 insertions(+), 71 deletions(-) diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index 64efb20cdd..6e206d0519 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -1,58 +1,41 @@ +import { ComponentType } from "@opensystemslab/planx-core/types"; import { Locator, Page } from "@playwright/test"; -enum PlanXEditorComponent { - QUESTION = "Question", - NOTICE = "Notice", - CHECKLIST = "Checklist", - TEXT = "Text Input", - NUMBER = "Number Input", - DATE = "Date Input", - ADDRESS = "Address Input", - CONTACT = "Contact Input", - TASKLIST = "Task List", - REVIEW = "Review", - FIND_PROPERTY = "Find property", - PLANNING_CONSTRAINTS = "Planning constraints", - DRAW_BOUNDARY = "Draw boundary", - NEXT_STEPS = "Next steps", - FILE_UPLOAD = "File Upload", -} - const createBaseComponent = async ( page: Page, locatingNode: Locator, - type: PlanXEditorComponent, + type: ComponentType, title?: string, options?: string[] ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); - await page.locator("select").selectOption({ label: type }); + await page.locator("select").selectOption({ value: type.toString() }); switch (type) { - case PlanXEditorComponent.QUESTION: + case ComponentType.Question: await page.getByPlaceholder("Text").fill(title || ""); if (options) { await createComponentOptions(options, "add new", page); } break; - case PlanXEditorComponent.NOTICE: + case ComponentType.Notice: await page.getByPlaceholder("Notice").fill(title || ""); break; - case PlanXEditorComponent.CHECKLIST: + case ComponentType.Checklist: await page.getByPlaceholder("Text").fill(title || ""); if (options) { await createComponentOptions(options, "add new option", page); } break; - case PlanXEditorComponent.TEXT: + case ComponentType.TextInput: await page.getByPlaceholder("Title").fill(title || ""); break; - case PlanXEditorComponent.NUMBER: + case ComponentType.NumberInput: await page.getByPlaceholder("Title").fill(title || ""); await page.getByPlaceholder("eg square metres").fill(options?.[0] || ""); break; - case PlanXEditorComponent.DATE: + case ComponentType.DateInput: await page.getByPlaceholder("Title").fill(title || ""); // fill with hardcoded dates for now await page.locator("id=undefined-min-day").fill("01"); @@ -62,15 +45,15 @@ const createBaseComponent = async ( await page.locator("id=undefined-max-month").fill("12"); await page.locator("id=undefined-max-year").fill("2199"); break; - case PlanXEditorComponent.ADDRESS: + case ComponentType.AddressInput: await page.getByPlaceholder("Title").fill(title || ""); await page.getByPlaceholder("Data Field").fill(options?.[0] || ""); break; - case PlanXEditorComponent.CONTACT: + case ComponentType.ContactInput: await page.getByPlaceholder("Title").fill(title || ""); await page.getByPlaceholder("Data Field").fill(options?.[0] || ""); break; - case PlanXEditorComponent.TASKLIST: + case ComponentType.TaskList: await page.getByPlaceholder("Main Title").fill(title || ""); if (options) { let index = 0; @@ -84,19 +67,19 @@ const createBaseComponent = async ( } } break; - case PlanXEditorComponent.REVIEW: + case ComponentType.Review: // Don't need to change anything so dummy click await page .getByPlaceholder("Check your answers before sending your application") .click(); break; - case PlanXEditorComponent.FIND_PROPERTY: + case ComponentType.FindProperty: break; - case PlanXEditorComponent.PLANNING_CONSTRAINTS: + case ComponentType.PlanningConstraints: break; - case PlanXEditorComponent.DRAW_BOUNDARY: + case ComponentType.DrawBoundary: break; - case PlanXEditorComponent.NEXT_STEPS: + case ComponentType.NextSteps: if (options) { let index = 0; for (const option of options) { @@ -109,7 +92,7 @@ const createBaseComponent = async ( } } break; - case PlanXEditorComponent.FILE_UPLOAD: + case ComponentType.FileUpload: await page.getByPlaceholder("Data Field").fill(options?.[0] || ""); break; @@ -117,19 +100,7 @@ const createBaseComponent = async ( throw new Error(`Unsupported type: ${type}`); } - // convert type name to lowercase, with dashes if there are spaces - // or custom name if it doesn't fit the pattern - const buttonName = - type === PlanXEditorComponent.FIND_PROPERTY - ? "find-property-merged" - : type.toLowerCase().replace(/\s/g, "-"); - - await page - .locator("button") - .filter({ - hasText: `Create ${buttonName}`, - }) - .click(); + await page.locator('button[form="modal"][type="submit"]').click(); }; export const createQuestionWithOptions = async ( @@ -141,7 +112,7 @@ export const createQuestionWithOptions = async ( await createBaseComponent( page, locatingNode, - PlanXEditorComponent.QUESTION, + ComponentType.Question, questionText, options ); @@ -155,7 +126,7 @@ export const createNotice = async ( await createBaseComponent( page, locatingNode, - PlanXEditorComponent.NOTICE, + ComponentType.Notice, noticeText ); }; @@ -169,7 +140,7 @@ export const createChecklist = async ( await createBaseComponent( page, locatingNode, - PlanXEditorComponent.CHECKLIST, + ComponentType.Checklist, checklistTitle, checklistOptions ); @@ -183,7 +154,7 @@ export const createTextInput = async ( await createBaseComponent( page, locatingNode, - PlanXEditorComponent.TEXT, + ComponentType.TextInput, inputTitle ); }; @@ -197,7 +168,7 @@ export const createNumberInput = async ( await createBaseComponent( page, locatingNode, - PlanXEditorComponent.NUMBER, + ComponentType.NumberInput, inputTitle, [inputUnits] ); @@ -211,7 +182,7 @@ export const createDateInput = async ( await createBaseComponent( page, locatingNode, - PlanXEditorComponent.DATE, + ComponentType.DateInput, inputTitle ); }; @@ -225,7 +196,7 @@ export const createAddressInput = async ( await createBaseComponent( page, locatingNode, - PlanXEditorComponent.ADDRESS, + ComponentType.AddressInput, inputTitle, [inputDataField] ); @@ -240,7 +211,7 @@ export const createContactInput = async ( await createBaseComponent( page, locatingNode, - PlanXEditorComponent.CONTACT, + ComponentType.ContactInput, inputTitle, [inputDataField] ); @@ -255,22 +226,18 @@ export const createTaskList = async ( await createBaseComponent( page, locatingNode, - PlanXEditorComponent.TASKLIST, + ComponentType.TaskList, title, taskListOptions ); }; export const createReview = async (page: Page, locatingNode: Locator) => { - await createBaseComponent(page, locatingNode, PlanXEditorComponent.REVIEW); + await createBaseComponent(page, locatingNode, ComponentType.Review); }; export const createFindProperty = async (page: Page, locatingNode: Locator) => { - await createBaseComponent( - page, - locatingNode, - PlanXEditorComponent.FIND_PROPERTY - ); + await createBaseComponent(page, locatingNode, ComponentType.FindProperty); }; export const createPlanningConstraints = async ( @@ -280,16 +247,12 @@ export const createPlanningConstraints = async ( await createBaseComponent( page, locatingNode, - PlanXEditorComponent.PLANNING_CONSTRAINTS + ComponentType.PlanningConstraints ); }; export const createDrawBoundary = async (page: Page, locatingNode: Locator) => { - await createBaseComponent( - page, - locatingNode, - PlanXEditorComponent.DRAW_BOUNDARY - ); + await createBaseComponent(page, locatingNode, ComponentType.DrawBoundary); }; export const createNextSteps = async ( @@ -300,7 +263,7 @@ export const createNextSteps = async ( await createBaseComponent( page, locatingNode, - PlanXEditorComponent.NEXT_STEPS, + ComponentType.NextSteps, undefined, nextSteps ); @@ -314,7 +277,7 @@ export const createFileUpload = async ( await createBaseComponent( page, locatingNode, - PlanXEditorComponent.FILE_UPLOAD, + ComponentType.FileUpload, undefined, [dataField] ); From 658b48e040d5d525352617d58a49dbf2afb6669b Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:38:12 +0100 Subject: [PATCH 29/39] Add linting to husky for e2e dir --- api.planx.uk/.husky/pre-commit | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api.planx.uk/.husky/pre-commit b/api.planx.uk/.husky/pre-commit index 2dca158161..2bf3989f94 100755 --- a/api.planx.uk/.husky/pre-commit +++ b/api.planx.uk/.husky/pre-commit @@ -2,3 +2,5 @@ . "$(dirname -- "$0")/_/husky.sh" cd api.planx.uk pnpm dlx lint-staged +cd ../e2e +pnpm dlx lint-staged From 24d2bd6e5799566c03da8fdb50280d1b76f4ae4a Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:38:32 +0100 Subject: [PATCH 30/39] Test lint From a58a216827e77e46d68b51b9922f7ad079720c50 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:39:46 +0100 Subject: [PATCH 31/39] Fix linting --- .../src/create-flow/create-flow.spec.ts | 28 +++++----- .../ui-driven/src/helpers/addComponent.ts | 52 +++++++++---------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index 53763248d3..0db5895bcc 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -69,7 +69,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)) + (req) => (isRepeatedRequestMade = isGetUserRequest(req)), ); Promise.all([ @@ -127,7 +127,7 @@ test.describe("Navigation", () => { "No", ]); await expect( - page.locator("a").filter({ hasText: questionText }) + page.locator("a").filter({ hasText: questionText }), ).toBeVisible(); // Add a notice to the "Yes" path @@ -137,7 +137,7 @@ test.describe("Navigation", () => { await createNotice( page, yesBranch.locator(".hanger > a"), - yesBranchNoticeText + yesBranchNoticeText, ); // Add a notice to the "No" path @@ -146,7 +146,7 @@ test.describe("Navigation", () => { await createNotice( page, noBranch.locator(".hanger > a"), - noBranchNoticeText + noBranchNoticeText, ); const getNextNode = () => page.locator(".hanger > a").last(); @@ -165,14 +165,14 @@ test.describe("Navigation", () => { page, getNextNode(), "What is your address?", - "some data field" + "some data field", ); await createContactInput( page, getNextNode(), "What is your contact info?", - "some data field" + "some data field", ); await createTaskList(page, getNextNode(), "What you should do next", [ @@ -210,7 +210,7 @@ test.describe("Navigation", () => { await expect(nodes.getByText("Next steps")).toBeVisible(); await expect( - nodes.getByText("Check your answers before sending your application") + nodes.getByText("Check your answers before sending your application"), ).toBeVisible(); }); @@ -225,7 +225,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -259,11 +259,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }) + page.getByRole("heading", { level: 1, name: "Offline" }), ).toBeVisible(); }); @@ -282,7 +282,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully") + page.getByText("Service settings updated successfully"), ).toBeVisible(); // Exit back to main Editor page @@ -305,13 +305,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }) + page.locator("h1", { hasText: "Yes! this is a test" }), ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -319,7 +319,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }) + page.locator("h1", { hasText: "Sorry, this is a test" }), ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index 6e206d0519..d91438770a 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -6,7 +6,7 @@ const createBaseComponent = async ( locatingNode: Locator, type: ComponentType, title?: string, - options?: string[] + options?: string[], ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -107,27 +107,27 @@ export const createQuestionWithOptions = async ( page: Page, locatingNode: Locator, questionText: string, - options: string[] + options: string[], ) => { await createBaseComponent( page, locatingNode, ComponentType.Question, questionText, - options + options, ); }; export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string + noticeText: string, ) => { await createBaseComponent( page, locatingNode, ComponentType.Notice, - noticeText + noticeText, ); }; @@ -135,27 +135,27 @@ export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[] + checklistOptions: string[], ) => { await createBaseComponent( page, locatingNode, ComponentType.Checklist, checklistTitle, - checklistOptions + checklistOptions, ); }; export const createTextInput = async ( page: Page, locatingNode: Locator, - inputTitle: string + inputTitle: string, ) => { await createBaseComponent( page, locatingNode, ComponentType.TextInput, - inputTitle + inputTitle, ); }; @@ -163,27 +163,27 @@ export const createNumberInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputUnits: string + inputUnits: string, ) => { await createBaseComponent( page, locatingNode, ComponentType.NumberInput, inputTitle, - [inputUnits] + [inputUnits], ); }; export const createDateInput = async ( page: Page, locatingNode: Locator, - inputTitle: string + inputTitle: string, ) => { await createBaseComponent( page, locatingNode, ComponentType.DateInput, - inputTitle + inputTitle, ); }; @@ -191,14 +191,14 @@ export const createAddressInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string + inputDataField: string, ) => { await createBaseComponent( page, locatingNode, ComponentType.AddressInput, inputTitle, - [inputDataField] + [inputDataField], ); }; @@ -206,14 +206,14 @@ export const createContactInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string + inputDataField: string, ) => { await createBaseComponent( page, locatingNode, ComponentType.ContactInput, inputTitle, - [inputDataField] + [inputDataField], ); }; @@ -221,14 +221,14 @@ export const createTaskList = async ( page: Page, locatingNode: Locator, title: string, - taskListOptions: string[] + taskListOptions: string[], ) => { await createBaseComponent( page, locatingNode, ComponentType.TaskList, title, - taskListOptions + taskListOptions, ); }; @@ -242,12 +242,12 @@ export const createFindProperty = async (page: Page, locatingNode: Locator) => { export const createPlanningConstraints = async ( page: Page, - locatingNode: Locator + locatingNode: Locator, ) => { await createBaseComponent( page, locatingNode, - ComponentType.PlanningConstraints + ComponentType.PlanningConstraints, ); }; @@ -258,35 +258,35 @@ export const createDrawBoundary = async (page: Page, locatingNode: Locator) => { export const createNextSteps = async ( page: Page, locatingNode: Locator, - nextSteps: string[] + nextSteps: string[], ) => { await createBaseComponent( page, locatingNode, ComponentType.NextSteps, undefined, - nextSteps + nextSteps, ); }; export const createFileUpload = async ( page: Page, locatingNode: Locator, - dataField: string + dataField: string, ) => { await createBaseComponent( page, locatingNode, ComponentType.FileUpload, undefined, - [dataField] + [dataField], ); }; async function createComponentOptions( options: string[], buttonText: string, - page: Page + page: Page, ) { let index = 0; for (const option of options) { From 7debbb6cb67292ca6b0345905ffc0d611daa541d Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Mon, 16 Sep 2024 16:38:20 +0100 Subject: [PATCH 32/39] Start to use page object model for editor --- .../src/{create-flow => }/create-flow.spec.ts | 57 ++++++++----------- .../ui-driven/src/helpers/addComponent.ts | 52 ++++++++--------- e2e/tests/ui-driven/src/pages/Editor.ts | 39 +++++++++++++ 3 files changed, 90 insertions(+), 58 deletions(-) rename e2e/tests/ui-driven/src/{create-flow => }/create-flow.spec.ts (87%) create mode 100644 e2e/tests/ui-driven/src/pages/Editor.ts diff --git a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow.spec.ts similarity index 87% rename from e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts rename to e2e/tests/ui-driven/src/create-flow.spec.ts index 0db5895bcc..38a28abe12 100644 --- a/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow.spec.ts @@ -1,4 +1,5 @@ import { Browser, expect, test } from "@playwright/test"; +import { PlaywrightEditor } from "./pages/Editor"; import { createAddressInput, createChecklist, @@ -11,23 +12,22 @@ import { createNotice, createNumberInput, createPlanningConstraints, - createQuestionWithOptions, createReview, createTaskList, createTextInput, -} from "../helpers/addComponent"; -import type { Context } from "../helpers/context"; +} from "./helpers/addComponent"; +import type { Context } from "./helpers/context"; import { contextDefaults, setUpTestContext, tearDownTestContext, -} from "../helpers/context"; -import { getTeamPage } from "../helpers/getPage"; +} from "./helpers/context"; +import { getTeamPage } from "./helpers/getPage"; import { createAuthenticatedSession, isGetUserRequest, -} from "../helpers/globalHelpers"; -import { answerQuestion, clickContinue } from "../helpers/userActions"; +} from "./helpers/globalHelpers"; +import { answerQuestion, clickContinue } from "./helpers/userActions"; test.describe("Navigation", () => { let context: Context = { @@ -69,7 +69,7 @@ test.describe("Navigation", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)), + (req) => (isRepeatedRequestMade = isGetUserRequest(req)) ); Promise.all([ @@ -113,22 +113,15 @@ test.describe("Navigation", () => { teamName: context.team.name, }); + const editor = new PlaywrightEditor(page); + page.on("dialog", (dialog) => dialog.accept(serviceProps.name)); - await page.locator("button", { hasText: "Add a new service" }).click(); + await editor.addNewService(); // update context to allow flow to be torn down context.flow = { ...serviceProps }; - const firstNode = page.locator("li.hanger > a").first(); - - const questionText = "Is this a test?"; - await createQuestionWithOptions(page, firstNode, questionText, [ - "Yes", - "No", - ]); - await expect( - page.locator("a").filter({ hasText: questionText }), - ).toBeVisible(); + await editor.createQuestion(); // Add a notice to the "Yes" path const yesBranch = page.locator("#flow .card .options .option").nth(0); @@ -137,7 +130,7 @@ test.describe("Navigation", () => { await createNotice( page, yesBranch.locator(".hanger > a"), - yesBranchNoticeText, + yesBranchNoticeText ); // Add a notice to the "No" path @@ -146,7 +139,7 @@ test.describe("Navigation", () => { await createNotice( page, noBranch.locator(".hanger > a"), - noBranchNoticeText, + noBranchNoticeText ); const getNextNode = () => page.locator(".hanger > a").last(); @@ -165,14 +158,14 @@ test.describe("Navigation", () => { page, getNextNode(), "What is your address?", - "some data field", + "some data field" ); await createContactInput( page, getNextNode(), "What is your contact info?", - "some data field", + "some data field" ); await createTaskList(page, getNextNode(), "What you should do next", [ @@ -193,7 +186,7 @@ test.describe("Navigation", () => { await createReview(page, getNextNode()); const nodes = page.locator(".card"); - await expect(nodes.getByText(questionText)).toBeVisible(); + editor.inspectNodes(); await expect(nodes.getByText(yesBranchNoticeText)).toBeVisible(); await expect(nodes.getByText(noBranchNoticeText)).toBeVisible(); await expect(nodes.getByText("Checklist item 1")).toBeVisible(); @@ -210,7 +203,7 @@ test.describe("Navigation", () => { await expect(nodes.getByText("Next steps")).toBeVisible(); await expect( - nodes.getByText("Check your answers before sending your application"), + nodes.getByText("Check your answers before sending your application") ).toBeVisible(); }); @@ -225,7 +218,7 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -259,11 +252,11 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }), + page.getByRole("heading", { level: 1, name: "Offline" }) ).toBeVisible(); }); @@ -282,7 +275,7 @@ test.describe("Navigation", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully"), + page.getByText("Service settings updated successfully") ).toBeVisible(); // Exit back to main Editor page @@ -305,13 +298,13 @@ test.describe("Navigation", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }), + page.locator("h1", { hasText: "Yes! this is a test" }) ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -319,7 +312,7 @@ test.describe("Navigation", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }), + page.locator("h1", { hasText: "Sorry, this is a test" }) ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index d91438770a..6e206d0519 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -6,7 +6,7 @@ const createBaseComponent = async ( locatingNode: Locator, type: ComponentType, title?: string, - options?: string[], + options?: string[] ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -107,27 +107,27 @@ export const createQuestionWithOptions = async ( page: Page, locatingNode: Locator, questionText: string, - options: string[], + options: string[] ) => { await createBaseComponent( page, locatingNode, ComponentType.Question, questionText, - options, + options ); }; export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string, + noticeText: string ) => { await createBaseComponent( page, locatingNode, ComponentType.Notice, - noticeText, + noticeText ); }; @@ -135,27 +135,27 @@ export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[], + checklistOptions: string[] ) => { await createBaseComponent( page, locatingNode, ComponentType.Checklist, checklistTitle, - checklistOptions, + checklistOptions ); }; export const createTextInput = async ( page: Page, locatingNode: Locator, - inputTitle: string, + inputTitle: string ) => { await createBaseComponent( page, locatingNode, ComponentType.TextInput, - inputTitle, + inputTitle ); }; @@ -163,27 +163,27 @@ export const createNumberInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputUnits: string, + inputUnits: string ) => { await createBaseComponent( page, locatingNode, ComponentType.NumberInput, inputTitle, - [inputUnits], + [inputUnits] ); }; export const createDateInput = async ( page: Page, locatingNode: Locator, - inputTitle: string, + inputTitle: string ) => { await createBaseComponent( page, locatingNode, ComponentType.DateInput, - inputTitle, + inputTitle ); }; @@ -191,14 +191,14 @@ export const createAddressInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string, + inputDataField: string ) => { await createBaseComponent( page, locatingNode, ComponentType.AddressInput, inputTitle, - [inputDataField], + [inputDataField] ); }; @@ -206,14 +206,14 @@ export const createContactInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string, + inputDataField: string ) => { await createBaseComponent( page, locatingNode, ComponentType.ContactInput, inputTitle, - [inputDataField], + [inputDataField] ); }; @@ -221,14 +221,14 @@ export const createTaskList = async ( page: Page, locatingNode: Locator, title: string, - taskListOptions: string[], + taskListOptions: string[] ) => { await createBaseComponent( page, locatingNode, ComponentType.TaskList, title, - taskListOptions, + taskListOptions ); }; @@ -242,12 +242,12 @@ export const createFindProperty = async (page: Page, locatingNode: Locator) => { export const createPlanningConstraints = async ( page: Page, - locatingNode: Locator, + locatingNode: Locator ) => { await createBaseComponent( page, locatingNode, - ComponentType.PlanningConstraints, + ComponentType.PlanningConstraints ); }; @@ -258,35 +258,35 @@ export const createDrawBoundary = async (page: Page, locatingNode: Locator) => { export const createNextSteps = async ( page: Page, locatingNode: Locator, - nextSteps: string[], + nextSteps: string[] ) => { await createBaseComponent( page, locatingNode, ComponentType.NextSteps, undefined, - nextSteps, + nextSteps ); }; export const createFileUpload = async ( page: Page, locatingNode: Locator, - dataField: string, + dataField: string ) => { await createBaseComponent( page, locatingNode, ComponentType.FileUpload, undefined, - [dataField], + [dataField] ); }; async function createComponentOptions( options: string[], buttonText: string, - page: Page, + page: Page ) { let index = 0; for (const option of options) { diff --git a/e2e/tests/ui-driven/src/pages/Editor.ts b/e2e/tests/ui-driven/src/pages/Editor.ts new file mode 100644 index 0000000000..4b6d279228 --- /dev/null +++ b/e2e/tests/ui-driven/src/pages/Editor.ts @@ -0,0 +1,39 @@ +import { expect, type Locator, type Page } from "@playwright/test"; +import { createQuestionWithOptions } from "../helpers/addComponent"; + +export class PlaywrightEditor { + readonly page: Page; + readonly addNewServiceButton: Locator; + readonly firstNode: Locator; + readonly questionText: string; + + constructor(page: Page) { + this.page = page; + this.addNewServiceButton = page.locator("button", { + hasText: "Add a new service", + }); + this.firstNode = page.locator("li.hanger > a").first(); + this.questionText = "Is this a test?"; + } + + async addNewService() { + await this.addNewServiceButton.click(); + } + + async createQuestion() { + await createQuestionWithOptions( + this.page, + this.firstNode, + this.questionText, + ["Yes", "No"] + ); + await expect( + this.page.locator("a").filter({ hasText: this.questionText }) + ).toBeVisible(); + } + + async inspectNodes() { + const nodes = this.page.locator(".card"); + await expect(nodes.getByText(this.questionText)).toBeVisible(); + } +} From 1eca97b65e7d844994349c02491b91255740b822 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Mon, 16 Sep 2024 17:16:18 +0100 Subject: [PATCH 33/39] Fully use Editor class - moves complexity around --- e2e/tests/ui-driven/src/create-flow.spec.ts | 126 ++++------------ e2e/tests/ui-driven/src/pages/Editor.ts | 151 ++++++++++++++++++-- 2 files changed, 173 insertions(+), 104 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow.spec.ts index 38a28abe12..50faa2c188 100644 --- a/e2e/tests/ui-driven/src/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow.spec.ts @@ -1,21 +1,4 @@ import { Browser, expect, test } from "@playwright/test"; -import { PlaywrightEditor } from "./pages/Editor"; -import { - createAddressInput, - createChecklist, - createContactInput, - createDateInput, - createDrawBoundary, - createFileUpload, - createFindProperty, - createNextSteps, - createNotice, - createNumberInput, - createPlanningConstraints, - createReview, - createTaskList, - createTextInput, -} from "./helpers/addComponent"; import type { Context } from "./helpers/context"; import { contextDefaults, @@ -28,6 +11,7 @@ import { isGetUserRequest, } from "./helpers/globalHelpers"; import { answerQuestion, clickContinue } from "./helpers/userActions"; +import { PlaywrightEditor } from "./pages/Editor"; test.describe("Navigation", () => { let context: Context = { @@ -122,89 +106,39 @@ test.describe("Navigation", () => { context.flow = { ...serviceProps }; await editor.createQuestion(); - - // Add a notice to the "Yes" path - const yesBranch = page.locator("#flow .card .options .option").nth(0); - - const yesBranchNoticeText = "Yes! this is a test"; - await createNotice( - page, - yesBranch.locator(".hanger > a"), - yesBranchNoticeText - ); - - // Add a notice to the "No" path - const noBranch = page.locator("#flow .card .options .option").nth(1); - const noBranchNoticeText = "Sorry, this is a test"; - await createNotice( - page, - noBranch.locator(".hanger > a"), - noBranchNoticeText - ); - - const getNextNode = () => page.locator(".hanger > a").last(); - - await createChecklist(page, getNextNode(), "A checklist title", [ + await editor.createNoticeOnEachBranch(); + await editor.createChecklist(); + await editor.createTextInput(); + await editor.createNumberInput(); + await editor.createDateInput(); + await editor.createAddressInput(); + await editor.createContactInput(); + await editor.createTaskList(); + await editor.createFindProperty(); + await editor.createDrawBoundary(); + await editor.createPlanningConstraints(); + await editor.createFileUpload(); + await editor.createNextSteps(); + await editor.createReview(); + + await expect(editor.nodeList).toContainText([ + "Is this a test?", + "Yes! this is a test", + "Sorry, this is a test", "Checklist item 1", - "Second checklist item", - "The third checklist item", - ]); - - await createTextInput(page, getNextNode(), "Tell us about your trees."); - await createNumberInput(page, getNextNode(), "How old are you?", "years"); - await createDateInput(page, getNextNode(), "When is your birthday?"); - - await createAddressInput( - page, - getNextNode(), + "Tell us about your trees.", + "How old are you?", + "When is your birthday?", "What is your address?", - "some data field" - ); - - await createContactInput( - page, - getNextNode(), "What is your contact info?", - "some data field" - ); - - await createTaskList(page, getNextNode(), "What you should do next", [ - "Have a cup of tea", - "Continue through this flow", + "What you should do next", + "Find property", + "Confirm your location plan", + "Planning constraints", + "File upload", + "Next steps", + "Check your answers before sending your application", ]); - - await createFindProperty(page, getNextNode()); - await createDrawBoundary(page, getNextNode()); - await createPlanningConstraints(page, getNextNode()); - await createFileUpload(page, getNextNode(), "some data field"); - - await createNextSteps(page, getNextNode(), [ - "A possible next step", - "Another option", - ]); - - await createReview(page, getNextNode()); - - const nodes = page.locator(".card"); - editor.inspectNodes(); - await expect(nodes.getByText(yesBranchNoticeText)).toBeVisible(); - await expect(nodes.getByText(noBranchNoticeText)).toBeVisible(); - await expect(nodes.getByText("Checklist item 1")).toBeVisible(); - await expect(nodes.getByText("Tell us about your trees.")).toBeVisible(); - await expect(nodes.getByText("How old are you?")).toBeVisible(); - await expect(nodes.getByText("When is your birthday?")).toBeVisible(); - await expect(nodes.getByText("What is your address?")).toBeVisible(); - await expect(nodes.getByText("What is your contact info?")).toBeVisible(); - await expect(nodes.getByText("What you should do next")).toBeVisible(); - await expect(nodes.getByText("Find property")).toBeVisible(); - await expect(nodes.getByText("Confirm your location plan")).toBeVisible(); - await expect(nodes.getByText("Planning constraints")).toBeVisible(); - await expect(nodes.getByText("File upload")).toBeVisible(); - - await expect(nodes.getByText("Next steps")).toBeVisible(); - await expect( - nodes.getByText("Check your answers before sending your application") - ).toBeVisible(); }); test("Cannot preview an unpublished flow", async ({ diff --git a/e2e/tests/ui-driven/src/pages/Editor.ts b/e2e/tests/ui-driven/src/pages/Editor.ts index 4b6d279228..4551d5e8ff 100644 --- a/e2e/tests/ui-driven/src/pages/Editor.ts +++ b/e2e/tests/ui-driven/src/pages/Editor.ts @@ -1,11 +1,34 @@ import { expect, type Locator, type Page } from "@playwright/test"; -import { createQuestionWithOptions } from "../helpers/addComponent"; +import { + createAddressInput, + createChecklist, + createContactInput, + createDateInput, + createDrawBoundary, + createFileUpload, + createFindProperty, + createNextSteps, + createNotice, + createNumberInput, + createPlanningConstraints, + createQuestionWithOptions, + createReview, + createTaskList, + createTextInput, +} from "../helpers/addComponent"; export class PlaywrightEditor { readonly page: Page; readonly addNewServiceButton: Locator; readonly firstNode: Locator; - readonly questionText: string; + readonly yesBranch: Locator; + readonly noBranch: Locator; + readonly nodeList: Locator; + readonly answers: { + questionText: string; + yesBranchNoticeText: string; + noBranchNoticeText: string; + }; constructor(page: Page) { this.page = page; @@ -13,7 +36,14 @@ export class PlaywrightEditor { hasText: "Add a new service", }); this.firstNode = page.locator("li.hanger > a").first(); - this.questionText = "Is this a test?"; + this.yesBranch = page.locator("#flow .card .options .option").nth(0); + this.noBranch = page.locator("#flow .card .options .option").nth(1); + this.nodeList = page.locator(".card"); + this.answers = { + questionText: "Is this a test?", + yesBranchNoticeText: "Yes! this is a test", + noBranchNoticeText: "Sorry, this is a test", + }; } async addNewService() { @@ -24,16 +54,121 @@ export class PlaywrightEditor { await createQuestionWithOptions( this.page, this.firstNode, - this.questionText, + this.answers.questionText, ["Yes", "No"] ); await expect( - this.page.locator("a").filter({ hasText: this.questionText }) + this.page.locator("a").filter({ hasText: this.answers.questionText }) ).toBeVisible(); } - async inspectNodes() { - const nodes = this.page.locator(".card"); - await expect(nodes.getByText(this.questionText)).toBeVisible(); + async createNoticeOnEachBranch() { + // Add a notice to the "Yes" path + await createNotice( + this.page, + this.yesBranch.locator(".hanger > a"), + this.answers.yesBranchNoticeText + ); + // Add a notice to the "No" path + await createNotice( + this.page, + this.noBranch.locator(".hanger > a"), + this.answers.noBranchNoticeText + ); + + await expect( + this.page.locator("a").filter({ hasText: this.answers.questionText }) + ).toBeVisible(); + } + + getNextNode() { + return this.page.locator(".hanger > a").last(); + } + + async createChecklist() { + await createChecklist(this.page, this.getNextNode(), "A checklist title", [ + "Checklist item 1", + "Second checklist item", + "The third checklist item", + ]); + } + + async createTextInput() { + await createTextInput( + this.page, + this.getNextNode(), + "Tell us about your trees." + ); + } + + async createNumberInput() { + await createNumberInput( + this.page, + this.getNextNode(), + "How old are you?", + "years" + ); + } + + async createDateInput() { + await createDateInput( + this.page, + this.getNextNode(), + "When is your birthday?" + ); + } + + async createAddressInput() { + await createAddressInput( + this.page, + this.getNextNode(), + "What is your address?", + "some data field" + ); + } + + async createContactInput() { + await createContactInput( + this.page, + this.getNextNode(), + "What is your contact info?", + "some data field" + ); + } + + async createTaskList() { + await createTaskList( + this.page, + this.getNextNode(), + "What you should do next", + ["Have a cup of tea", "Continue through this flow"] + ); + } + + async createFindProperty() { + await createFindProperty(this.page, this.getNextNode()); + } + + async createDrawBoundary() { + await createDrawBoundary(this.page, this.getNextNode()); + } + + async createPlanningConstraints() { + await createPlanningConstraints(this.page, this.getNextNode()); + } + + async createFileUpload() { + await createFileUpload(this.page, this.getNextNode(), "some data field"); + } + + async createNextSteps() { + await createNextSteps(this.page, this.getNextNode(), [ + "A possible next step", + "Another option", + ]); + } + + async createReview() { + await createReview(this.page, this.getNextNode()); } } From f92d16ecf64f5554e3a4a651166270c254a98aa9 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Tue, 17 Sep 2024 12:01:52 +0100 Subject: [PATCH 34/39] Split create-flow test into two --- e2e/tests/ui-driven/src/create-flow.spec.ts | 113 +++++++++---------- e2e/tests/ui-driven/src/refresh-page.spec.ts | 85 ++++++++++++++ 2 files changed, 139 insertions(+), 59 deletions(-) create mode 100644 e2e/tests/ui-driven/src/refresh-page.spec.ts diff --git a/e2e/tests/ui-driven/src/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow.spec.ts index 50faa2c188..fe2bc95b7d 100644 --- a/e2e/tests/ui-driven/src/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow.spec.ts @@ -6,14 +6,11 @@ import { tearDownTestContext, } from "./helpers/context"; import { getTeamPage } from "./helpers/getPage"; -import { - createAuthenticatedSession, - isGetUserRequest, -} from "./helpers/globalHelpers"; +import { createAuthenticatedSession } from "./helpers/globalHelpers"; import { answerQuestion, clickContinue } from "./helpers/userActions"; import { PlaywrightEditor } from "./pages/Editor"; -test.describe("Navigation", () => { +test.describe("Flow creation, publish and preview", () => { let context: Context = { ...contextDefaults, }; @@ -35,60 +32,58 @@ test.describe("Navigation", () => { test.afterAll(async () => { await tearDownTestContext(context); }); - - test("user data persists on page refresh @regression", async ({ - browser, - }) => { - const page = await createAuthenticatedSession({ - browser, - userId: context.user!.id!, - }); - - const initialRequest = page.waitForRequest(isGetUserRequest); - - Promise.all([await page.goto("/"), await initialRequest]); - - const team = page.locator("h3", { hasText: context.team.name }); - - let isRepeatedRequestMade = false; - page.on( - "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)) - ); - - Promise.all([ - await team.click(), - expect(isRepeatedRequestMade).toBe(false), - ]); - - const reloadRequest = page.waitForRequest(isGetUserRequest); - - Promise.all([await page.reload(), await reloadRequest]); - }); - - test("team data persists on page refresh @regression", async ({ - browser, - }) => { - const page = await createAuthenticatedSession({ - browser, - userId: context.user!.id!, - }); - - await page.goto("/"); - const team = page.locator("h3", { hasText: context.team.name }); - await team.click(); - - const teamSlugInHeader = page.getByRole("link", { - name: context.team.slug, - }); - await expect(teamSlugInHeader).toBeVisible(); - - await page.reload(); - await expect(teamSlugInHeader).toBeVisible(); - - await page.goBack(); - await expect(teamSlugInHeader).toBeHidden(); - }); + // browser, + // }) => { + // const page = await createAuthenticatedSession({ + // browser, + // userId: context.user!.id!, + // }); + + // const initialRequest = page.waitForRequest(isGetUserRequest); + + // Promise.all([await page.goto("/"), await initialRequest]); + + // const team = page.locator("h3", { hasText: context.team.name }); + + // let isRepeatedRequestMade = false; + // page.on( + // "request", + // (req) => (isRepeatedRequestMade = isGetUserRequest(req)) + // ); + + // Promise.all([ + // await team.click(), + // expect(isRepeatedRequestMade).toBe(false), + // ]); + + // const reloadRequest = page.waitForRequest(isGetUserRequest); + + // Promise.all([await page.reload(), await reloadRequest]); + // }); + + // test("team data persists on page refresh @regression", async ({ + // browser, + // }) => { + // const page = await createAuthenticatedSession({ + // browser, + // userId: context.user!.id!, + // }); + + // await page.goto("/"); + // const team = page.locator("h3", { hasText: context.team.name }); + // await team.click(); + + // const teamSlugInHeader = page.getByRole("link", { + // name: context.team.slug, + // }); + // await expect(teamSlugInHeader).toBeVisible(); + + // await page.reload(); + // await expect(teamSlugInHeader).toBeVisible(); + + // await page.goBack(); + // await expect(teamSlugInHeader).toBeHidden(); + // }); test("Create a flow", async ({ browser }) => { const page = await getTeamPage({ diff --git a/e2e/tests/ui-driven/src/refresh-page.spec.ts b/e2e/tests/ui-driven/src/refresh-page.spec.ts new file mode 100644 index 0000000000..883e0424dd --- /dev/null +++ b/e2e/tests/ui-driven/src/refresh-page.spec.ts @@ -0,0 +1,85 @@ +import { expect, test } from "@playwright/test"; +import type { Context } from "./helpers/context"; +import { + contextDefaults, + setUpTestContext, + tearDownTestContext, +} from "./helpers/context"; +import { + createAuthenticatedSession, + isGetUserRequest, +} from "./helpers/globalHelpers"; + +test.describe("Refresh page", () => { + let context: Context = { + ...contextDefaults, + }; + + test.beforeAll(async () => { + try { + context = await setUpTestContext(context); + } catch (error) { + // ensure proper teardown if setup fails + await tearDownTestContext(context); + throw error; + } + }); + + test.afterAll(async () => { + await tearDownTestContext(context); + }); + + test("user data persists on page refresh @regression", async ({ + browser, + }) => { + const page = await createAuthenticatedSession({ + browser, + userId: context.user!.id!, + }); + + const initialRequest = page.waitForRequest(isGetUserRequest); + + Promise.all([await page.goto("/"), await initialRequest]); + + const team = page.locator("h3", { hasText: context.team.name }); + + let isRepeatedRequestMade = false; + page.on( + "request", + (req) => (isRepeatedRequestMade = isGetUserRequest(req)) + ); + + Promise.all([ + await team.click(), + expect(isRepeatedRequestMade).toBe(false), + ]); + + const reloadRequest = page.waitForRequest(isGetUserRequest); + + Promise.all([await page.reload(), await reloadRequest]); + }); + + test("team data persists on page refresh @regression", async ({ + browser, + }) => { + const page = await createAuthenticatedSession({ + browser, + userId: context.user!.id!, + }); + + await page.goto("/"); + const team = page.locator("h3", { hasText: context.team.name }); + await team.click(); + + const teamSlugInHeader = page.getByRole("link", { + name: context.team.slug, + }); + await expect(teamSlugInHeader).toBeVisible(); + + await page.reload(); + await expect(teamSlugInHeader).toBeVisible(); + + await page.goBack(); + await expect(teamSlugInHeader).toBeHidden(); + }); +}); From 11ab75a4f6da7243c9898f86992354a3fc176e1c Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Tue, 17 Sep 2024 12:02:33 +0100 Subject: [PATCH 35/39] Lint fix --- e2e/tests/ui-driven/src/create-flow.spec.ts | 14 ++--- .../ui-driven/src/helpers/addComponent.ts | 52 +++++++++---------- e2e/tests/ui-driven/src/pages/Editor.ts | 22 ++++---- e2e/tests/ui-driven/src/refresh-page.spec.ts | 2 +- 4 files changed, 45 insertions(+), 45 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow.spec.ts index fe2bc95b7d..961eca6dea 100644 --- a/e2e/tests/ui-driven/src/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow.spec.ts @@ -147,7 +147,7 @@ test.describe("Flow creation, publish and preview", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect(page.getByText("Not Found")).toBeVisible(); @@ -181,11 +181,11 @@ test.describe("Flow creation, publish and preview", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await expect( - page.getByRole("heading", { level: 1, name: "Offline" }) + page.getByRole("heading", { level: 1, name: "Offline" }), ).toBeVisible(); }); @@ -204,7 +204,7 @@ test.describe("Flow creation, publish and preview", () => { page.getByLabel("Offline").click(); page.getByRole("button", { name: "Save", disabled: false }).click(); await expect( - page.getByText("Service settings updated successfully") + page.getByText("Service settings updated successfully"), ).toBeVisible(); // Exit back to main Editor page @@ -227,13 +227,13 @@ test.describe("Flow creation, publish and preview", () => { }); await page.goto( - `/${context.team.slug}/${serviceProps.slug}/published?analytics=false` + `/${context.team.slug}/${serviceProps.slug}/published?analytics=false`, ); await answerQuestion({ page, title: "Is this a test?", answer: "Yes" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Yes! this is a test" }) + page.locator("h1", { hasText: "Yes! this is a test" }), ).toBeVisible(); await page.getByTestId("backButton").click(); @@ -241,7 +241,7 @@ test.describe("Flow creation, publish and preview", () => { await answerQuestion({ page, title: "Is this a test?", answer: "No" }); await clickContinue({ page }); await expect( - page.locator("h1", { hasText: "Sorry, this is a test" }) + page.locator("h1", { hasText: "Sorry, this is a test" }), ).toBeVisible(); }); }); diff --git a/e2e/tests/ui-driven/src/helpers/addComponent.ts b/e2e/tests/ui-driven/src/helpers/addComponent.ts index 6e206d0519..d91438770a 100644 --- a/e2e/tests/ui-driven/src/helpers/addComponent.ts +++ b/e2e/tests/ui-driven/src/helpers/addComponent.ts @@ -6,7 +6,7 @@ const createBaseComponent = async ( locatingNode: Locator, type: ComponentType, title?: string, - options?: string[] + options?: string[], ) => { await locatingNode.click(); await page.getByRole("dialog").waitFor(); @@ -107,27 +107,27 @@ export const createQuestionWithOptions = async ( page: Page, locatingNode: Locator, questionText: string, - options: string[] + options: string[], ) => { await createBaseComponent( page, locatingNode, ComponentType.Question, questionText, - options + options, ); }; export const createNotice = async ( page: Page, locatingNode: Locator, - noticeText: string + noticeText: string, ) => { await createBaseComponent( page, locatingNode, ComponentType.Notice, - noticeText + noticeText, ); }; @@ -135,27 +135,27 @@ export const createChecklist = async ( page: Page, locatingNode: Locator, checklistTitle: string, - checklistOptions: string[] + checklistOptions: string[], ) => { await createBaseComponent( page, locatingNode, ComponentType.Checklist, checklistTitle, - checklistOptions + checklistOptions, ); }; export const createTextInput = async ( page: Page, locatingNode: Locator, - inputTitle: string + inputTitle: string, ) => { await createBaseComponent( page, locatingNode, ComponentType.TextInput, - inputTitle + inputTitle, ); }; @@ -163,27 +163,27 @@ export const createNumberInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputUnits: string + inputUnits: string, ) => { await createBaseComponent( page, locatingNode, ComponentType.NumberInput, inputTitle, - [inputUnits] + [inputUnits], ); }; export const createDateInput = async ( page: Page, locatingNode: Locator, - inputTitle: string + inputTitle: string, ) => { await createBaseComponent( page, locatingNode, ComponentType.DateInput, - inputTitle + inputTitle, ); }; @@ -191,14 +191,14 @@ export const createAddressInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string + inputDataField: string, ) => { await createBaseComponent( page, locatingNode, ComponentType.AddressInput, inputTitle, - [inputDataField] + [inputDataField], ); }; @@ -206,14 +206,14 @@ export const createContactInput = async ( page: Page, locatingNode: Locator, inputTitle: string, - inputDataField: string + inputDataField: string, ) => { await createBaseComponent( page, locatingNode, ComponentType.ContactInput, inputTitle, - [inputDataField] + [inputDataField], ); }; @@ -221,14 +221,14 @@ export const createTaskList = async ( page: Page, locatingNode: Locator, title: string, - taskListOptions: string[] + taskListOptions: string[], ) => { await createBaseComponent( page, locatingNode, ComponentType.TaskList, title, - taskListOptions + taskListOptions, ); }; @@ -242,12 +242,12 @@ export const createFindProperty = async (page: Page, locatingNode: Locator) => { export const createPlanningConstraints = async ( page: Page, - locatingNode: Locator + locatingNode: Locator, ) => { await createBaseComponent( page, locatingNode, - ComponentType.PlanningConstraints + ComponentType.PlanningConstraints, ); }; @@ -258,35 +258,35 @@ export const createDrawBoundary = async (page: Page, locatingNode: Locator) => { export const createNextSteps = async ( page: Page, locatingNode: Locator, - nextSteps: string[] + nextSteps: string[], ) => { await createBaseComponent( page, locatingNode, ComponentType.NextSteps, undefined, - nextSteps + nextSteps, ); }; export const createFileUpload = async ( page: Page, locatingNode: Locator, - dataField: string + dataField: string, ) => { await createBaseComponent( page, locatingNode, ComponentType.FileUpload, undefined, - [dataField] + [dataField], ); }; async function createComponentOptions( options: string[], buttonText: string, - page: Page + page: Page, ) { let index = 0; for (const option of options) { diff --git a/e2e/tests/ui-driven/src/pages/Editor.ts b/e2e/tests/ui-driven/src/pages/Editor.ts index 4551d5e8ff..accbd03265 100644 --- a/e2e/tests/ui-driven/src/pages/Editor.ts +++ b/e2e/tests/ui-driven/src/pages/Editor.ts @@ -55,10 +55,10 @@ export class PlaywrightEditor { this.page, this.firstNode, this.answers.questionText, - ["Yes", "No"] + ["Yes", "No"], ); await expect( - this.page.locator("a").filter({ hasText: this.answers.questionText }) + this.page.locator("a").filter({ hasText: this.answers.questionText }), ).toBeVisible(); } @@ -67,17 +67,17 @@ export class PlaywrightEditor { await createNotice( this.page, this.yesBranch.locator(".hanger > a"), - this.answers.yesBranchNoticeText + this.answers.yesBranchNoticeText, ); // Add a notice to the "No" path await createNotice( this.page, this.noBranch.locator(".hanger > a"), - this.answers.noBranchNoticeText + this.answers.noBranchNoticeText, ); await expect( - this.page.locator("a").filter({ hasText: this.answers.questionText }) + this.page.locator("a").filter({ hasText: this.answers.questionText }), ).toBeVisible(); } @@ -97,7 +97,7 @@ export class PlaywrightEditor { await createTextInput( this.page, this.getNextNode(), - "Tell us about your trees." + "Tell us about your trees.", ); } @@ -106,7 +106,7 @@ export class PlaywrightEditor { this.page, this.getNextNode(), "How old are you?", - "years" + "years", ); } @@ -114,7 +114,7 @@ export class PlaywrightEditor { await createDateInput( this.page, this.getNextNode(), - "When is your birthday?" + "When is your birthday?", ); } @@ -123,7 +123,7 @@ export class PlaywrightEditor { this.page, this.getNextNode(), "What is your address?", - "some data field" + "some data field", ); } @@ -132,7 +132,7 @@ export class PlaywrightEditor { this.page, this.getNextNode(), "What is your contact info?", - "some data field" + "some data field", ); } @@ -141,7 +141,7 @@ export class PlaywrightEditor { this.page, this.getNextNode(), "What you should do next", - ["Have a cup of tea", "Continue through this flow"] + ["Have a cup of tea", "Continue through this flow"], ); } diff --git a/e2e/tests/ui-driven/src/refresh-page.spec.ts b/e2e/tests/ui-driven/src/refresh-page.spec.ts index 883e0424dd..309d09c0f4 100644 --- a/e2e/tests/ui-driven/src/refresh-page.spec.ts +++ b/e2e/tests/ui-driven/src/refresh-page.spec.ts @@ -46,7 +46,7 @@ test.describe("Refresh page", () => { let isRepeatedRequestMade = false; page.on( "request", - (req) => (isRepeatedRequestMade = isGetUserRequest(req)) + (req) => (isRepeatedRequestMade = isGetUserRequest(req)), ); Promise.all([ From 0b4d8bff72d74bb1d0c5f480e0fa7134dbba26b9 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Tue, 17 Sep 2024 13:03:32 +0100 Subject: [PATCH 36/39] Add to user facing test --- e2e/tests/ui-driven/src/create-flow.spec.ts | 124 ++++++++++-------- .../ui-driven/src/helpers/userActions.ts | 56 ++++++++ 2 files changed, 127 insertions(+), 53 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow.spec.ts index 961eca6dea..c2c730d867 100644 --- a/e2e/tests/ui-driven/src/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow.spec.ts @@ -7,7 +7,17 @@ import { } from "./helpers/context"; import { getTeamPage } from "./helpers/getPage"; import { createAuthenticatedSession } from "./helpers/globalHelpers"; -import { answerQuestion, clickContinue } from "./helpers/userActions"; +import { + answerAddressInput, + answerChecklist, + answerContactInput, + answerDateInput, + answerFindProperty, + answerNumberInput, + answerQuestion, + answerTextInput, + clickContinue, +} from "./helpers/userActions"; import { PlaywrightEditor } from "./pages/Editor"; test.describe("Flow creation, publish and preview", () => { @@ -32,58 +42,6 @@ test.describe("Flow creation, publish and preview", () => { test.afterAll(async () => { await tearDownTestContext(context); }); - // browser, - // }) => { - // const page = await createAuthenticatedSession({ - // browser, - // userId: context.user!.id!, - // }); - - // const initialRequest = page.waitForRequest(isGetUserRequest); - - // Promise.all([await page.goto("/"), await initialRequest]); - - // const team = page.locator("h3", { hasText: context.team.name }); - - // let isRepeatedRequestMade = false; - // page.on( - // "request", - // (req) => (isRepeatedRequestMade = isGetUserRequest(req)) - // ); - - // Promise.all([ - // await team.click(), - // expect(isRepeatedRequestMade).toBe(false), - // ]); - - // const reloadRequest = page.waitForRequest(isGetUserRequest); - - // Promise.all([await page.reload(), await reloadRequest]); - // }); - - // test("team data persists on page refresh @regression", async ({ - // browser, - // }) => { - // const page = await createAuthenticatedSession({ - // browser, - // userId: context.user!.id!, - // }); - - // await page.goto("/"); - // const team = page.locator("h3", { hasText: context.team.name }); - // await team.click(); - - // const teamSlugInHeader = page.getByRole("link", { - // name: context.team.slug, - // }); - // await expect(teamSlugInHeader).toBeVisible(); - - // await page.reload(); - // await expect(teamSlugInHeader).toBeVisible(); - - // await page.goBack(); - // await expect(teamSlugInHeader).toBeHidden(); - // }); test("Create a flow", async ({ browser }) => { const page = await getTeamPage({ @@ -243,5 +201,65 @@ test.describe("Flow creation, publish and preview", () => { await expect( page.locator("h1", { hasText: "Sorry, this is a test" }), ).toBeVisible(); + await clickContinue({ page }); + await answerChecklist({ + page, + title: "A checklist title", + answers: ["Checklist item 1", "Second checklist item"], + }); + await clickContinue({ page }); + await expect( + page.locator("p", { hasText: "Tell us about your trees." }), + ).toBeVisible(); + await answerTextInput(page, { answer: "My trees are lovely" }); + await clickContinue({ page }); + await expect( + page.locator("p", { hasText: "How old are you?" }), + ).toBeVisible(); + await answerNumberInput(page, { answer: 30 }); + await clickContinue({ page }); + await expect( + page.locator("h1", { hasText: "When is your birthday?" }), + ).toBeVisible(); + await answerDateInput(page, { day: 30, month: 12, year: 1980 }); + await clickContinue({ page }); + + await expect( + page.locator("h1", { hasText: "What is your address?" }), + ).toBeVisible(); + await answerAddressInput(page, { + addressLineOne: "1 Silver Street", + town: "Bamburgh", + postcode: "BG1 2SS", + }); + await clickContinue({ page }); + + await expect( + page.locator("h1", { hasText: "What is your contact info?" }), + ).toBeVisible(); + await answerContactInput(page, { + firstName: "Freddie", + lastName: "Mercury", + phoneNumber: "01234 555555", + email: "freddie@queen.com", + }); + await clickContinue({ page }); + + await expect( + page.locator("h1", { hasText: "What you should do next" }), + ).toBeVisible(); + await expect( + page.locator("h2", { hasText: "Have a cup of tea" }), + ).toBeVisible(); + await expect( + page.locator("h2", { hasText: "Continue through this flow" }), + ).toBeVisible(); + await clickContinue({ page }); + + await expect( + page.locator("h1", { hasText: "Find the property" }), + ).toBeVisible(); + await answerFindProperty(page); + await clickContinue({ page }); }); }); diff --git a/e2e/tests/ui-driven/src/helpers/userActions.ts b/e2e/tests/ui-driven/src/helpers/userActions.ts index bb2a048125..f49738cdec 100644 --- a/e2e/tests/ui-driven/src/helpers/userActions.ts +++ b/e2e/tests/ui-driven/src/helpers/userActions.ts @@ -232,3 +232,59 @@ export async function answerContactInput( await page.getByLabel("Phone number").fill(phoneNumber); await page.getByLabel("Email address").fill(email); } + +export async function answerTextInput( + page: Page, + { + answer, + }: { + answer: string; + }, +) { + await page.locator("label div input[type='text']").fill(answer); +} + +export async function answerNumberInput( + page: Page, + { + answer, + }: { + answer: number; + }, +) { + await page.locator("label div input[type='number']").fill(answer.toString()); +} + +export async function answerDateInput( + page: Page, + { + day, + month, + year, + }: { + day: number; + month: number; + year: number; + }, +) { + await page.getByLabel("Day").fill(day.toString()); + await page.getByLabel("Month").fill(month.toString()); + await page.getByLabel("Year").fill(year.toString()); +} + +export async function answerAddressInput( + page: Page, + { + addressLineOne, + town, + postcode, + }: { + addressLineOne: string; + town: string; + postcode: string; + }, +) { + await page.getByLabel("Address line 1").fill(addressLineOne); + await page.getByLabel("Town").fill(town); + await page.getByLabel("Postcode").fill(postcode); +} From 6a992f966c3bcaab0f231f20ed55536ca5f0779e Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Tue, 17 Sep 2024 15:28:38 +0100 Subject: [PATCH 37/39] Refactor continue and expectedQuestion into helpers --- e2e/tests/ui-driven/src/create-flow.spec.ts | 45 ++++++++++--------- .../ui-driven/src/helpers/userActions.ts | 40 +++++++++++++++++ 2 files changed, 63 insertions(+), 22 deletions(-) diff --git a/e2e/tests/ui-driven/src/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow.spec.ts index c2c730d867..b1bab94fda 100644 --- a/e2e/tests/ui-driven/src/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow.spec.ts @@ -202,37 +202,41 @@ test.describe("Flow creation, publish and preview", () => { page.locator("h1", { hasText: "Sorry, this is a test" }), ).toBeVisible(); await clickContinue({ page }); + await answerChecklist({ page, title: "A checklist title", answers: ["Checklist item 1", "Second checklist item"], }); await clickContinue({ page }); - await expect( - page.locator("p", { hasText: "Tell us about your trees." }), - ).toBeVisible(); - await answerTextInput(page, { answer: "My trees are lovely" }); - await clickContinue({ page }); - await expect( - page.locator("p", { hasText: "How old are you?" }), - ).toBeVisible(); - await answerNumberInput(page, { answer: 30 }); - await clickContinue({ page }); - await expect( - page.locator("h1", { hasText: "When is your birthday?" }), - ).toBeVisible(); - await answerDateInput(page, { day: 30, month: 12, year: 1980 }); - await clickContinue({ page }); - await expect( - page.locator("h1", { hasText: "What is your address?" }), - ).toBeVisible(); + await answerTextInput(page, { + expectedQuestion: "Tell us about your trees.", + answer: "My trees are lovely", + continueToNext: true, + }); + + await answerNumberInput(page, { + expectedQuestion: "How old are you?", + answer: 30, + continueToNext: true, + }); + + await answerDateInput(page, { + expectedQuestion: "When is your birthday?", + day: 30, + month: 12, + year: 1980, + continueToNext: true, + }); + await answerAddressInput(page, { + expectedQuestion: "What is your address?", addressLineOne: "1 Silver Street", town: "Bamburgh", postcode: "BG1 2SS", + continueToNext: true, }); - await clickContinue({ page }); await expect( page.locator("h1", { hasText: "What is your contact info?" }), @@ -256,9 +260,6 @@ test.describe("Flow creation, publish and preview", () => { ).toBeVisible(); await clickContinue({ page }); - await expect( - page.locator("h1", { hasText: "Find the property" }), - ).toBeVisible(); await answerFindProperty(page); await clickContinue({ page }); }); diff --git a/e2e/tests/ui-driven/src/helpers/userActions.ts b/e2e/tests/ui-driven/src/helpers/userActions.ts index f49738cdec..bfa801e474 100644 --- a/e2e/tests/ui-driven/src/helpers/userActions.ts +++ b/e2e/tests/ui-driven/src/helpers/userActions.ts @@ -196,6 +196,9 @@ export async function submitCardDetails(page: Page) { export async function answerFindProperty(page: Page) { await setupOSMockResponse(page); + await expect( + page.locator("h1", { hasText: "Find the property" }), + ).toBeVisible(); await page.getByLabel("Postcode").fill("SW1 1AA"); await page.getByLabel("Select an address").click(); await page.getByRole("option").first().click(); @@ -236,55 +239,92 @@ export async function answerContactInput( export async function answerTextInput( page: Page, { + expectedQuestion, answer, + continueToNext, }: { + expectedQuestion: string; answer: string; + continueToNext: boolean; }, ) { + await expect(page.locator("p", { hasText: expectedQuestion })).toBeVisible(); await page.locator("label div input[type='text']").fill(answer); + if (continueToNext) { + await clickContinue({ page }); + } } export async function answerNumberInput( page: Page, { + expectedQuestion, answer, + continueToNext, }: { + expectedQuestion: string; answer: number; + continueToNext: boolean; }, ) { + await expect(page.locator("p", { hasText: expectedQuestion })).toBeVisible(); await page.locator("label div input[type='number']").fill(answer.toString()); + if (continueToNext) { + await clickContinue({ page }); + } } export async function answerDateInput( page: Page, { + expectedQuestion, + day, month, year, + continueToNext, }: { + expectedQuestion: string; + day: number; month: number; year: number; + continueToNext: boolean; }, ) { + await expect(page.locator("h1", { hasText: expectedQuestion })).toBeVisible(); await page.getByLabel("Day").fill(day.toString()); await page.getByLabel("Month").fill(month.toString()); await page.getByLabel("Year").fill(year.toString()); + + if (continueToNext) { + await clickContinue({ page }); + } } export async function answerAddressInput( page: Page, { + expectedQuestion, + addressLineOne, town, postcode, + continueToNext, }: { + expectedQuestion: string; + addressLineOne: string; town: string; postcode: string; + continueToNext: boolean; }, ) { + await expect(page.locator("h1", { hasText: expectedQuestion })).toBeVisible(); await page.getByLabel("Address line 1").fill(addressLineOne); await page.getByLabel("Town").fill(town); await page.getByLabel("Postcode").fill(postcode); + if (continueToNext) { + await clickContinue({ page }); + } } From 7da5771202bd16c6b43730b2f795c8e0c2bbf40f Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:05:41 +0100 Subject: [PATCH 38/39] Move mocks around --- e2e/tests/ui-driven/src/helpers/userActions.ts | 16 ++-------------- .../ui-driven/src/mocks/osPlacesResponse.ts | 13 +++++++++++++ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/e2e/tests/ui-driven/src/helpers/userActions.ts b/e2e/tests/ui-driven/src/helpers/userActions.ts index bfa801e474..51c7cbe7ca 100644 --- a/e2e/tests/ui-driven/src/helpers/userActions.ts +++ b/e2e/tests/ui-driven/src/helpers/userActions.ts @@ -1,6 +1,6 @@ import type { Locator, Page } from "@playwright/test"; import { expect } from "@playwright/test"; -import { mockOSPlacesResponse } from "../mocks/osPlacesResponse"; +import { setupOSMockResponse } from "../mocks/osPlacesResponse"; import type { Context } from "./context"; import { findSessionId, getGraphQLClient } from "./context"; import { TEST_EMAIL, log, waitForDebugLog } from "./globalHelpers"; @@ -121,7 +121,7 @@ export async function answerChecklist({ title: string; answers: string[]; }) { - const checklist = await page.getByRole("heading").filter({ + const checklist = page.getByRole("heading").filter({ hasText: title, }); await expect(checklist).toBeVisible(); @@ -204,18 +204,6 @@ export async function answerFindProperty(page: Page) { await page.getByRole("option").first().click(); } -async function setupOSMockResponse(page: Page) { - const ordnanceSurveryPlacesEndpoint = new RegExp( - /proxy\/ordnance-survey\/search\/places\/v1\/postcode\/*/, - ); - await page.route(ordnanceSurveryPlacesEndpoint, async (route) => { - await route.fulfill({ - status: 200, - body: JSON.stringify(mockOSPlacesResponse), - }); - }); -} - export async function answerContactInput( page: Page, { diff --git a/e2e/tests/ui-driven/src/mocks/osPlacesResponse.ts b/e2e/tests/ui-driven/src/mocks/osPlacesResponse.ts index 6b6baf7d4b..5f4547be28 100644 --- a/e2e/tests/ui-driven/src/mocks/osPlacesResponse.ts +++ b/e2e/tests/ui-driven/src/mocks/osPlacesResponse.ts @@ -1,3 +1,5 @@ +import { Page } from "@playwright/test"; + export const mockOSPlacesResponse = { header: { uri: "https://api.os.uk/search/places/v1/postcode?postcode=SW1%201AA&dataset=LPI&maxResults=100&output_srs=EPSG%3A4326&lr=EN&offset=0", @@ -58,3 +60,14 @@ export const mockOSPlacesResponse = { }, ], }; +export async function setupOSMockResponse(page: Page) { + const ordnanceSurveryPlacesEndpoint = new RegExp( + /proxy\/ordnance-survey\/search\/places\/v1\/postcode\/*/, + ); + await page.route(ordnanceSurveryPlacesEndpoint, async (route) => { + await route.fulfill({ + status: 200, + body: JSON.stringify(mockOSPlacesResponse), + }); + }); +} From 2218b0c581169bab11beb6a8f2c7ae52483093f1 Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:56:43 +0100 Subject: [PATCH 39/39] Undo move create-flow for cleaner PR --- .../src/{ => create-flow}/create-flow.spec.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename e2e/tests/ui-driven/src/{ => create-flow}/create-flow.spec.ts (96%) diff --git a/e2e/tests/ui-driven/src/create-flow.spec.ts b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts similarity index 96% rename from e2e/tests/ui-driven/src/create-flow.spec.ts rename to e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts index b1bab94fda..d3eda04740 100644 --- a/e2e/tests/ui-driven/src/create-flow.spec.ts +++ b/e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts @@ -1,12 +1,12 @@ import { Browser, expect, test } from "@playwright/test"; -import type { Context } from "./helpers/context"; +import type { Context } from "../helpers/context"; import { contextDefaults, setUpTestContext, tearDownTestContext, -} from "./helpers/context"; -import { getTeamPage } from "./helpers/getPage"; -import { createAuthenticatedSession } from "./helpers/globalHelpers"; +} from "../helpers/context"; +import { getTeamPage } from "../helpers/getPage"; +import { createAuthenticatedSession } from "../helpers/globalHelpers"; import { answerAddressInput, answerChecklist, @@ -17,8 +17,8 @@ import { answerQuestion, answerTextInput, clickContinue, -} from "./helpers/userActions"; -import { PlaywrightEditor } from "./pages/Editor"; +} from "../helpers/userActions"; +import { PlaywrightEditor } from "../pages/Editor"; test.describe("Flow creation, publish and preview", () => { let context: Context = {