From 478fa91004049f40f596b4922d5f9959653a45c4 Mon Sep 17 00:00:00 2001 From: Robert Bond Date: Wed, 12 Aug 2026 13:58:07 +0000 Subject: [PATCH] Merged PR 2558: updated link on awaiting submission view allin welsh updated link on awaiting submission view allin welsh Related work items: #24389 --- components/myportal/viewall.js | 6 +- package.json | 8 +- playwright.config.js | 31 + pwe2e/tests/apiTests/api.spec.js | 82 +++ pwe2e/tests/loggedin/downloadFile.spec.js | 27 + pwe2e/tests/loggedin/newappeal.spec.js | 149 ++++ pwe2e/tests/loggedin/raiserep.spec.js | 729 ++++++++++++++++++++ pwe2e/tests/loggedin/s78newappeal.spec.js | 377 ++++++++++ pwe2e/tests/loggedout/downloadFile.spec.js | 26 + pwe2e/tests/loggedout/homepageLinks.spec.js | 35 + pwe2e/tests/loggedout/searches.spec.js | 208 ++++++ pwe2e/tests/test-1.spec.ts | 71 ++ pwe2e/tests/test-2.spec.ts | 5 + pwe2e/tests/test-3.spec.ts | 22 + 14 files changed, 1769 insertions(+), 7 deletions(-) create mode 100644 playwright.config.js create mode 100644 pwe2e/tests/apiTests/api.spec.js create mode 100644 pwe2e/tests/loggedin/downloadFile.spec.js create mode 100644 pwe2e/tests/loggedin/newappeal.spec.js create mode 100644 pwe2e/tests/loggedin/raiserep.spec.js create mode 100644 pwe2e/tests/loggedin/s78newappeal.spec.js create mode 100644 pwe2e/tests/loggedout/downloadFile.spec.js create mode 100644 pwe2e/tests/loggedout/homepageLinks.spec.js create mode 100644 pwe2e/tests/loggedout/searches.spec.js create mode 100644 pwe2e/tests/test-1.spec.ts create mode 100644 pwe2e/tests/test-2.spec.ts create mode 100644 pwe2e/tests/test-3.spec.ts diff --git a/components/myportal/viewall.js b/components/myportal/viewall.js index f9ca297a..8792a793 100644 --- a/components/myportal/viewall.js +++ b/components/myportal/viewall.js @@ -212,12 +212,8 @@ const ViewAllResults = (props) => { } if (isAwaitingSubmissionDetails) { - if (router.locale === "cy") { - return "/fymhorth/parhauigyflwyno"; - } - return ( - "/myportal" + + (router.locale === "cy" ? "/fymhorth" : "/myportal") + "/" + getFormCollectionByID(item.pinswg_appealcasetype).UrlName + "?lpa=" + diff --git a/package.json b/package.json index e1a965f5..23eac9f2 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,11 @@ "start": "NODE_ENV=production node_modules/next/dist/bin/next start", "lint": "next lint", "prisma:generate": "prisma generate", - "test:reps": "cd '../../welsh government/Playwright/Playwright' && npx playwright test tests/loggedin/raiserep.spec.js --headed", - "test:newappeal": "cd '../../welsh government/Playwright/Playwright' && npx playwright test tests/loggedin/newappeal.spec.js --headed" + "test": "playwright test", + "test:reps": "playwright test pwe2e/tests/loggedin/raiserep.spec.js --headed", + "test:newappeal": "playwright test pwe2e/tests/loggedin/newappeal.spec.js --headed", + "test:headed": "playwright test --headed", + "install:browsers": "playwright install chromium" }, "browser": { "child_process": false @@ -102,6 +105,7 @@ "@react-pdf/pdfkit": "3.1.0" }, "devDependencies": { + "@playwright/test": "^1.62.1", "eslint": "^8.57.0", "eslint-config-next": "^14.2.28", "eslint-plugin-jsdoc": "^48.2.3", diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 00000000..4c886a50 --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,31 @@ +import { defineConfig } from "@playwright/test"; +import dotenv from "dotenv"; + +const isCI = !!process.env.CI; + +// Load .env silently in CI to avoid logs +dotenv.config({ debug: !isCI }); + +export default defineConfig({ + testDir: "./pwe2e/tests", + timeout: 180000, + retries: isCI ? 1 : 0, + projects: [ + { + name: "chromium", + timeout: 180000, + use: { + browserName: "chromium", + headless: isCI, + baseURL: process.env.BASE_URL || "http://localhost:3000", + screenshot: "only-on-failure", + video: "retain-on-failure", + trace: "retain-on-failure" + } + } + ], + reporter: [ + ["html", { open: isCI ? "never" : "on-failure" }], + ["junit", { outputFile: "test-results/results.xml" }] + ] +}); diff --git a/pwe2e/tests/apiTests/api.spec.js b/pwe2e/tests/apiTests/api.spec.js new file mode 100644 index 00000000..0e5847bd --- /dev/null +++ b/pwe2e/tests/apiTests/api.spec.js @@ -0,0 +1,82 @@ +import { test, expect } from "@playwright/test"; + +// TO DO - build PEDW API playwright harness + +// Define a base URL for the mock API +const BASE_URL = process.env.BASE_URL; + +test.describe("User API Validation Suite", () => { + // 1. GET Request Example + test("should fetch a list of users and validate headers", async ({ + request, + }) => { + const response = await request.get(`${BASE_URL}/users/1`); + + // Assert the HTTP status code is 200 OK + expect(response.ok()).toBeTruthy(); + expect(response.status()).toBe(200); + + // Parse the JSON response body + const responseBody = await response.json(); + + // Assert specific data fields inside the JSON + expect(responseBody.id).toBe(1); + expect(responseBody.name).toBe("Leanne Graham"); + expect(responseBody.email).toBe("Sincere@april.biz"); + + // Assert response headers + expect(response.headers()["content-type"]).toContain("application/json"); + }); + + // 2. POST Request Example + test("should successfully create a new post", async ({ request }) => { + const response = await request.post(`${BASE_URL}/posts`, { + headers: { + "Content-type": "application/json; charset=UTF-8", + // 'Authorization': `Bearer ${process.env.API_TOKEN}` <-- Example for CI/CD tokens + }, + data: { + title: "Automation Test", + body: "Testing APIs with Playwright in DevOps pipelines.", + userId: 99, + }, + }); + + // Assert creation status code (201 Created) + expect(response.status()).toBe(201); + + const responseBody = await response.json(); + + // Assert the API returned our sent data with a new generated ID + expect(responseBody).toMatchObject({ + title: "Automation Test", + body: "Testing APIs with Playwright in DevOps pipelines.", + userId: 99, + id: 101, // Mock database increments ID to 101 + }); + }); + + // 3. PUT Request Example (Update) + test("should update an existing post", async ({ request }) => { + const response = await request.put(`${BASE_URL}/posts/1`, { + data: { + id: 1, + title: "Updated Title", + body: "Updated body content", + userId: 1, + }, + }); + + expect(response.ok()).toBeTruthy(); + const responseBody = await response.json(); + expect(responseBody.title).toBe("Updated Title"); + }); + + // 4. DELETE Request Example + test("should delete a post", async ({ request }) => { + const response = await request.delete(`${BASE_URL}/posts/1`); + + // Assert successful deletion response status + expect(response.status()).toBe(200); + }); +}); diff --git a/pwe2e/tests/loggedin/downloadFile.spec.js b/pwe2e/tests/loggedin/downloadFile.spec.js new file mode 100644 index 00000000..5ccc673d --- /dev/null +++ b/pwe2e/tests/loggedin/downloadFile.spec.js @@ -0,0 +1,27 @@ +import { test, expect } from "@playwright/test"; +import { login } from "../../helpers"; + +test("Download a file when logged in", async ({ page }) => { + await login(page); + + await page + .getByRole("textbox", { name: "Use the case reference or DNS" }) + .click(); + await page + .getByRole("textbox", { name: "Use the case reference or DNS" }) + .fill("gaerwen"); + await page.getByRole("button", { name: "Submit search" }).click(); + // await page + // .getByRole("link", { name: "DNS3276735 - Gaerwen Wind Farm (test)" }) + // .click(); + + await page.locator("dd a.govuk-link--no-underline").first().click(); + + const downloadPromise = page.waitForEvent("download"); + + await page.locator(".documentLink").first().click(); + + const download = await downloadPromise; + await page.getByText("Download complete!").click(); + await page.getByRole("link", { name: "Home" }).click(); +}); diff --git a/pwe2e/tests/loggedin/newappeal.spec.js b/pwe2e/tests/loggedin/newappeal.spec.js new file mode 100644 index 00000000..53ecfc51 --- /dev/null +++ b/pwe2e/tests/loggedin/newappeal.spec.js @@ -0,0 +1,149 @@ +import { test, expect } from "@playwright/test"; +import path from "path"; +import { login } from "../../helpers"; + +const date = new Date(); +date.setDate(date.getDate() - 1); // 👈 subtract 1 day + +const day = date.getDate(); + +const ordinal = (n) => { + if (n > 3 && n < 21) return "th"; + switch (n % 10) { + case 1: + return "st"; + case 2: + return "nd"; + case 3: + return "rd"; + default: + return "th"; + } +}; + +const weekday = date.toLocaleDateString("en-GB", { weekday: "long" }); +const month = date.toLocaleDateString("en-GB", { month: "long" }); + +const finalDate = `${weekday}, ${month} ${day}${ordinal(day)}`; + +test("Login and Raise new appeal", async ({ page }) => { + await login(page); + + // create new appeal + await page.getByRole("link", { name: "Make a new appeal" }).click(); + await page + .getByRole("textbox", { name: "Appellant or organisation's" }) + .click(); + await page + .getByRole("textbox", { name: "Appellant or organisation's" }) + .fill("Bob"); + await page.getByText("Appellant", { exact: true }).click(); + await page.getByText("English").click(); + await page.getByText("I confirm that I am making an").click(); + await page.getByRole("button", { name: "Continue" }).click(); + await page + .getByLabel("Select your Local Planning") + .selectOption("b9aee444-6ff0-eb11-aac7-00224800be9c"); + await page.getByLabel("Select a case type").selectOption("846040000"); + await page.getByRole("button", { name: "Continue" }).click(); + + await page.getByRole("button", { name: "Continue" }).click(); + + const step2 = page.locator('[aria-controls="step-panel-appeal-details"]'); + await step2.click(); + + await page.getByRole("textbox", { name: "Development description" }).click(); + await page + .getByRole("textbox", { name: "Development description" }) + .fill("Something in here"); + await page.getByText("No", { exact: true }).click(); + await page + .getByRole("textbox", { name: "LPA's application reference" }) + .click(); + await page + .getByRole("textbox", { name: "LPA's application reference" }) + .fill("AB1234"); + await page.getByRole("textbox", { name: "Application form dated" }).click(); + + await page.getByRole("option", { name: `Choose ${finalDate}` }).click(); + await page.getByLabel("Reason for appeal").selectOption("846040005"); + await page.getByRole("textbox", { name: "Site address line 1" }).click(); + await page + .getByRole("textbox", { name: "Site address line 1" }) + .fill("1 High Road"); + await page.getByRole("textbox", { name: "Site address line 1" }).press("Tab"); + await page.getByRole("textbox", { name: "Site address line 2" }).press("Tab"); + await page + .getByRole("textbox", { name: "Site address town" }) + .fill("Big town"); + await page.getByRole("textbox", { name: "Site address town" }).press("Tab"); + await page + .getByRole("textbox", { name: "Site address county" }) + .fill("Cardiff"); + await page.getByRole("textbox", { name: "Site address county" }).press("Tab"); + await page + .getByRole("textbox", { name: "Site address postcode" }) + .fill("cf21 3qa"); + await page + .getByRole("textbox", { name: "Site address postcode" }) + .press("Tab"); + await page + .getByRole("textbox", { name: "Size of overall appeal site (" }) + .fill("1"); + await page + .getByRole("textbox", { name: "Size of overall appeal site (" }) + .press("Tab"); + await page + .getByRole("textbox", { name: "What is the area of floor" }) + .fill("1"); + await page + .getByRole("textbox", { name: "What is the area of floor" }) + .press("Tab"); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByText("Certificate A - (for sole").click(); + await page.getByText("(a) None of the land to which").click(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByText("No").first().click(); + await page.getByText("No").nth(1).click(); + await page.getByText("No").nth(2).click(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByText("No").first().click(); + await page.getByRole("button", { name: "Continue" }).click(); + // await page + // .getByRole("paragraph") + // .filter({ hasText: /^$/ }) + // .click(); + await page.locator(".ql-editor").fill("something here"); + await page.getByText("No").nth(1).click(); + await page + .getByLabel("What procedure would you like") + .selectOption("846040000"); + await page.getByText("No").nth(3).click(); + await page.getByText("No").nth(4).click(); + await page.getByText("YesNo").nth(2).click(); + await page.getByRole("button", { name: "Continue" }).click(); + // upload files to statement of case + const files = [ + path.resolve(__dirname, "../../testFiles/file1.pdf"), + path.resolve(__dirname, "../../testFiles/file2.pdf"), + ]; + + const fileInput = page.locator("#pinswg_fileUpload"); + + await fileInput.setInputFiles(files); + const uploadedFiles = page.locator("h4", { hasText: "Uploaded files" }); + + await expect(uploadedFiles).toBeVisible({ timeout: 60000 }); + + await page.getByRole("button", { name: "Submit" }).click(); + await page + .getByRole("checkbox", { name: "I wish to download a copy of the form" }) + .check(); + await page + .getByRole("checkbox", { name: "I confirm that all sections" }) + .check(); + await page.getByText("Submit appeal").click(); + await page.getByRole("link", { name: "Home" }).click(); + await page.goto("/"); + await page.waitForTimeout(20000); +}); diff --git a/pwe2e/tests/loggedin/raiserep.spec.js b/pwe2e/tests/loggedin/raiserep.spec.js new file mode 100644 index 00000000..08aaac1e --- /dev/null +++ b/pwe2e/tests/loggedin/raiserep.spec.js @@ -0,0 +1,729 @@ +import { test, expect } from "@playwright/test"; +import { + login, + loginAgent, + generatePlanningText, + loginLPA, + loginIP, +} from "../../helpers"; +import path from "path"; + +const today = new Date(); + +// Add ordinal (st, nd, rd, th) +const day = today.getDate(); +const ordinal = (n) => { + if (n > 3 && n < 21) return "th"; + switch (n % 10) { + case 1: + return "st"; + case 2: + return "nd"; + case 3: + return "rd"; + default: + return "th"; + } +}; +const weekday = today.toLocaleDateString("en-GB", { weekday: "long" }); +const month = today.toLocaleDateString("en-GB", { month: "long" }); + +const finalDate = `${weekday}, ${month} ${day}${ordinal(day)}`; + +const finalCommentsCase = process.env.FINAL_COMMENTS; +const statementCase = process.env.STATEMENT; +const questionnaireCase = process.env.QUESTIONNAIRE; +const consultationCase = process.env.CONSULTATION; + +test("Raise Final Comments representation as an Appellant", async ({ + page, +}) => { + await login(page); + + await page.getByRole("textbox", { name: "Use the case reference" }).click(); + console.log("Value to fill:", finalCommentsCase); + await page + .getByRole("textbox", { name: "Use the case reference" }) + .fill(finalCommentsCase); + await page.getByRole("button", { name: "Submit search" }).click(); + // await page + // .getByRole("link", { name: "DNS3276735 - Gaerwen Wind Farm (test)" }) + // .click(); + + await page.locator("dd a.govuk-link--no-underline").first().click(); + + await page.getByRole("link", { name: "Make representation" }).click(); + + await page.getByRole("radio", { name: "Appellant" }).check(); + + await page.getByRole("button", { name: "Continue" }).click(); + + await page.getByRole("radio", { name: "Final comments" }).check(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("paragraph").filter({ hasText: /^$/ }).click(); + await page + .getByRole("group") + .locator("div") + .filter({ hasText: "Please note that all" }) + .locator("div") + .nth(3) + .fill("s"); + await page + .locator("div") + .filter({ hasText: /^s$/ }) + .nth(2) + .fill("some text to go here....."); + + const files = [ + path.resolve(__dirname, "../../testFiles/file1.pdf"), + path.resolve(__dirname, "../../testFiles/file2.pdf"), + ]; + + const fileInput = page.locator("#representationDocuments"); + + await fileInput.setInputFiles(files); + const uploadedFiles = page.locator("h4", { hasText: "Uploaded files" }); + + await expect(uploadedFiles).toBeVisible({ timeout: 60000 }); + + await page.getByRole("button", { name: "Continue" }).click(); + await page + .getByRole("checkbox", { name: "I confirm I have read the" }) + .check(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("link", { name: "Continue" }).click(); + await page.goto("/"); + + await page.waitForTimeout(2000); +}); + +test("Raise Final Comments representation as an Interested Party", async ({ + page, +}) => { + await loginIP(page); + //raise rep + + await page.getByRole("textbox", { name: "Use the case reference" }).click(); + await page + .getByRole("textbox", { name: "Use the case reference" }) + .fill(finalCommentsCase); + await page.getByRole("button", { name: "Submit search" }).click(); + + await page.locator("dd a.govuk-link--no-underline").first().click(); + + await page.getByRole("link", { name: "Make representation" }).click(); + + await page.getByRole("radio", { name: "Interested Party" }).check(); + + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("radio", { name: "Final comments" }).check(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByText("Yes").click(); + await page + .getByRole("textbox", { name: "Enter the name of the company" }) + .click(); + await page + .getByRole("textbox", { name: "Enter the name of the company" }) + .fill("Big Company"); + await page.getByRole("paragraph").filter({ hasText: /^$/ }).click(); + await page.locator(".ql-editor").fill(generatePlanningText(5)); + + const files = [ + path.resolve(__dirname, "../../testFiles/file1.pdf"), + path.resolve(__dirname, "../../testFiles/file2.pdf"), + ]; + + const fileInput = page.locator("#representationDocuments"); + + await fileInput.setInputFiles(files); + const uploadedFiles = page.locator("h4", { hasText: "Uploaded files" }); + + await expect(uploadedFiles).toBeVisible({ timeout: 60000 }); + + await page.getByRole("button", { name: "Continue" }).click(); + await page + .getByRole("checkbox", { name: "I confirm I have read the" }) + .check(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("link", { name: "Continue" }).click(); + await page.goto("/"); + + await page.waitForTimeout(2000); +}); + +test("Raise Statement representation as an Interested Party", async ({ + page, +}) => { + await loginIP(page); + //raise rep + + await page.getByRole("textbox", { name: "Use the case reference" }).click(); + await page + .getByRole("textbox", { name: "Use the case reference" }) + .fill(statementCase); + await page.getByRole("button", { name: "Submit search" }).click(); + + await page.locator("dd a.govuk-link--no-underline").first().click(); + + await page.getByRole("link", { name: "Make representation" }).click(); + + await page.getByRole("radio", { name: "Interested Party" }).check(); + + await page.getByRole("button", { name: "Continue" }).click(); + + await page.getByRole("radio", { name: "Statement" }).check(); + await page.getByRole("button", { name: "Continue" }).click(); + + await page.getByText("Yes").click(); + await page + .getByRole("textbox", { name: "Enter the name of the company" }) + .click(); + await page + .getByRole("textbox", { name: "Enter the name of the company" }) + .fill("Big Company"); + await page.getByRole("paragraph").filter({ hasText: /^$/ }).click(); + await page.locator(".ql-editor").fill(generatePlanningText(5)); + + const files = [ + path.resolve(__dirname, "../../testFiles/file1.pdf"), + path.resolve(__dirname, "../../testFiles/file2.pdf"), + ]; + + const fileInput = page.locator("#representationDocuments"); + + await fileInput.setInputFiles(files); + const uploadedFiles = page.locator("h4", { hasText: "Uploaded files" }); + + await expect(uploadedFiles).toBeVisible({ timeout: 60000 }); + + await page.getByRole("button", { name: "Continue" }).click(); + await page + .getByRole("checkbox", { name: "I confirm I have read the" }) + .check(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("link", { name: "Continue" }).click(); + await page.goto("/"); + + await page.waitForTimeout(2000); +}); + +test("Raise Consultation Response as an Interested Party", async ({ page }) => { + await loginIP(page); + //raise rep + + await page.getByRole("textbox", { name: "Use the case reference" }).click(); + await page + .getByRole("textbox", { name: "Use the case reference" }) + .fill(consultationCase); + await page.getByRole("button", { name: "Submit search" }).click(); + + await page.locator("dd a.govuk-link--no-underline").first().click(); + + await page + .getByRole("link", { name: "Submit a Consultation Response" }) + .click(); + + await page.getByRole("radio", { name: "Interested Party" }).check(); + + await page.getByRole("button", { name: "Continue" }).click(); + + await page.getByRole("radio", { name: "Consultation Response" }).check(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByText("Yes").click(); + await page + .getByRole("textbox", { name: "Enter the name of the company" }) + .click(); + await page + .getByRole("textbox", { name: "Enter the name of the company" }) + .fill("Big Company"); + await page.getByRole("paragraph").filter({ hasText: /^$/ }).click(); + await page.locator(".ql-editor").fill(generatePlanningText(5)); + + const files = [ + path.resolve(__dirname, "../../testFiles/file1.pdf"), + path.resolve(__dirname, "../../testFiles/file2.pdf"), + ]; + + const fileInput = page.locator("#representationDocuments"); + + await fileInput.setInputFiles(files); + const uploadedFiles = page.locator("h4", { hasText: "Uploaded files" }); + + await expect(uploadedFiles).toBeVisible({ timeout: 60000 }); + + await page.getByRole("button", { name: "Continue" }).click(); + await page + .getByRole("checkbox", { name: "I confirm I have read the" }) + .check(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("link", { name: "Continue" }).click(); + await page.goto("/"); + + await page.waitForTimeout(2000); +}); + +test("Raise LIR as an Interested Party", async ({ page }) => { + await loginIP(page); + //raise rep + + await page.getByRole("textbox", { name: "Use the case reference" }).click(); + await page + .getByRole("textbox", { name: "Use the case reference" }) + .fill(consultationCase); + await page.getByRole("button", { name: "Submit search" }).click(); + + await page.locator("dd a.govuk-link--no-underline").first().click(); + + await page + .getByRole("link", { name: "Submit a Consultation Response" }) + .click(); + + await page.getByRole("radio", { name: "Interested Party" }).check(); + + await page.getByRole("button", { name: "Continue" }).click(); + + await page.getByRole("radio", { name: "Local Impact Report" }).check(); + await page.getByRole("button", { name: "Continue" }).click(); + + await page.getByText("Yes").click(); + await page + .getByRole("textbox", { name: "Enter the name of the company" }) + .click(); + await page + .getByRole("textbox", { name: "Enter the name of the company" }) + .fill("Big Company"); + + const files = [ + path.resolve(__dirname, "../../testFiles/file1.pdf"), + path.resolve(__dirname, "../../testFiles/file2.pdf"), + ]; + + const fileInput = page.locator("#representationDocuments"); + + await fileInput.setInputFiles(files); + const uploadedFiles = page.locator("h4", { hasText: "Uploaded files" }); + + await expect(uploadedFiles).toBeVisible({ timeout: 60000 }); + + await page.getByRole("button", { name: "Continue" }).click(); + await page + .getByRole("checkbox", { name: "I confirm I have read the" }) + .check(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("link", { name: "Continue" }).click(); + await page.goto("/"); + + await page.waitForTimeout(2000); +}); + +test("Raise MIR as an Interested Party", async ({ page }) => { + await loginIP(page); + //raise rep + + await page.getByRole("textbox", { name: "Use the case reference" }).click(); + await page + .getByRole("textbox", { name: "Use the case reference" }) + .fill(consultationCase); + await page.getByRole("button", { name: "Submit search" }).click(); + + await page.locator("dd a.govuk-link--no-underline").first().click(); + + await page + .getByRole("link", { name: "Submit a Consultation Response" }) + .click(); + + await page.getByRole("radio", { name: "Interested Party" }).check(); + + await page.getByRole("button", { name: "Continue" }).click(); + + await page.getByRole("radio", { name: "Marine Impact Report" }).check(); + await page.getByRole("button", { name: "Continue" }).click(); + + await page.getByText("Yes").click(); + await page + .getByRole("textbox", { name: "Enter the name of the company" }) + .click(); + await page + .getByRole("textbox", { name: "Enter the name of the company" }) + .fill("Big Company"); + + const files = [ + path.resolve(__dirname, "../../testFiles/file1.pdf"), + path.resolve(__dirname, "../../testFiles/file2.pdf"), + ]; + + const fileInput = page.locator("#representationDocuments"); + + await fileInput.setInputFiles(files); + const uploadedFiles = page.locator("h4", { hasText: "Uploaded files" }); + + await expect(uploadedFiles).toBeVisible({ timeout: 60000 }); + + await page.getByRole("button", { name: "Continue" }).click(); + await page + .getByRole("checkbox", { name: "I confirm I have read the" }) + .check(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("link", { name: "Continue" }).click(); + await page.goto("/"); + + await page.waitForTimeout(2000); +}); + +test("Raise Final Comments representation as an Agent", async ({ page }) => { + await loginAgent(page); + //raise rep + + const acceptButton = page.getByRole("button", { name: "Accept cookies" }); + + if (await acceptButton.isVisible()) { + await acceptButton.click(); + } + + await page.getByRole("textbox", { name: "Use the case reference" }).click(); + await page + .getByRole("textbox", { name: "Use the case reference" }) + .fill(finalCommentsCase); + await page.getByRole("button", { name: "Submit search" }).click(); + + await page.locator("dd a.govuk-link--no-underline").first().click(); + + await page.getByRole("link", { name: "Make representation" }).click(); + + await page.getByRole("radio", { name: "Agent" }).check(); + + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("radio", { name: "Final comments" }).check(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByText("Yes").click(); + await page + .getByRole("textbox", { name: "Enter the name of the company" }) + .click(); + await page + .getByRole("textbox", { name: "Enter the name of the company" }) + .fill("Big Company"); + await page.getByRole("paragraph").filter({ hasText: /^$/ }).click(); + + await page.locator(".ql-editor").fill(generatePlanningText(4)); + + const files = [ + path.resolve(__dirname, "../../testFiles/file1.pdf"), + path.resolve(__dirname, "../../testFiles/file2.pdf"), + ]; + + const fileInput = page.locator("#representationDocuments"); + + await fileInput.setInputFiles(files); + const uploadedFiles = page.locator("h4", { hasText: "Uploaded files" }); + + await expect(uploadedFiles).toBeVisible({ timeout: 60000 }); + + await page.getByRole("button", { name: "Continue" }).click(); + await page + .getByRole("checkbox", { name: "I confirm I have read the" }) + .check(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("link", { name: "Continue" }).click(); + await page.goto("/"); + + await page.waitForTimeout(2000); +}); + +test("Raise Statement representation as an Agent", async ({ page }) => { + await loginAgent(page); + //raise rep + + await page.getByRole("textbox", { name: "Use the case reference" }).click(); + await page + .getByRole("textbox", { name: "Use the case reference" }) + .fill(statementCase); + await page.getByRole("button", { name: "Submit search" }).click(); + + await page.locator("dd a.govuk-link--no-underline").first().click(); + + await page.getByRole("link", { name: "Make representation" }).click(); + + await page.getByRole("radio", { name: "Agent" }).check(); + + await page.getByRole("button", { name: "Continue" }).click(); + + await page.getByRole("radio", { name: "Statement" }).check(); + await page.getByRole("button", { name: "Continue" }).click(); + + await page.getByText("Yes").click(); + await page + .getByRole("textbox", { name: "Enter the name of the company" }) + .click(); + await page + .getByRole("textbox", { name: "Enter the name of the company" }) + .fill("Big Company"); + await page.getByRole("paragraph").filter({ hasText: /^$/ }).click(); + await page.locator(".ql-editor").fill(generatePlanningText(5)); + + const files = [ + path.resolve(__dirname, "../../testFiles/file1.pdf"), + path.resolve(__dirname, "../../testFiles/file2.pdf"), + ]; + + const fileInput = page.locator("#representationDocuments"); + + await fileInput.setInputFiles(files); + const uploadedFiles = page.locator("h4", { hasText: "Uploaded files" }); + + await expect(uploadedFiles).toBeVisible({ timeout: 60000 }); + + await page.getByRole("button", { name: "Continue" }).click(); + await page + .getByRole("checkbox", { name: "I confirm I have read the" }) + .check(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("link", { name: "Continue" }).click(); + await page.goto("/"); + + await page.waitForTimeout(2000); +}); + +test("Raise questionnaire as an LPA", async ({ page }) => { + await loginLPA(page); + + await page.getByRole("textbox", { name: "Use the case reference" }).click(); + await page + .getByRole("textbox", { name: "Use the case reference" }) + .fill(questionnaireCase); + await page.getByRole("textbox", { name: "Use the case reference," }).click(); + await page + .getByRole("textbox", { name: "Use the case reference," }) + .fill(questionnaireCase); + await page.getByRole("button", { name: "Submit Search" }).click(); + await page.getByRole("link", { name: questionnaireCase }).click(); + // await page.getByRole("button", { name: "Submit search" }).click(); + + //await page.locator("dd a.govuk-link--no-underline").first().click(); + + await page.getByRole("link", { name: "Make representation" }).click(); + + await page.getByRole("radio", { name: "Questionnaire" }).check(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("combobox").selectOption("Written representations"); + await page.getByText("No").first().click(); + await page.getByText("No").nth(1).click(); + await page.getByText("No").nth(2).click(); + await page.getByText("Yes").nth(2).click(); + await page.getByText("No").nth(3).click(); + await page.getByRole("paragraph").filter({ hasText: /^$/ }).click(); + // await page.locator(".ql-editor").fill(generatePlanningText(2)); + // await page + // .locator("div") + // .filter({ hasText: /^w$/ }) + // .nth(2) + // .fill(generatePlanningText(2)); + await page.getByRole("button", { name: "Continue" }).click(); + await page.locator(".ql-editor").first().fill(generatePlanningText(2)); + await page.locator(".ql-editor").nth(1).fill(generatePlanningText(2)); + await page.locator(".ql-editor").nth(2).fill(generatePlanningText(2)); + + // // await page.getByRole("paragraph").nth(4).click(); + // await page + // .locator(".ql-editor.ql-blank") + // .first() + // .fill(generatePlanningText(2)); + // await page.getByRole("paragraph").filter({ hasText: /^$/ }).click(); + // await page + // .locator(".ql-editor.ql-blank") + // .first() + // .fill(generatePlanningText(2)); + // await page.locator(".ql-editor.ql-blank").fill("w e"); + // await page + // .locator("div") + // .filter({ hasText: /^w e$/ }) + // .nth(2) + // .fill(generatePlanningText(2)); + // await page.getByRole("paragraph").filter({ hasText: /^$/ }).click(); + // await page + // .locator("div") + // .filter({ hasText: /^w e$/ }) + // .nth(2) + // .fill(generatePlanningText(2)); + await page.getByText("No", { exact: true }).first().click(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByText("No").first().click(); + await page.getByText("No").nth(1).click(); + await page.getByText("No").nth(2).click(); + await page.getByText("No").nth(3).click(); + await page.getByText("No").nth(4).click(); + await page.getByText("No").nth(5).click(); + await page + .locator( + "div:nth-child(8) > .govuk-form-group > .govuk-fieldset > .govuk-radios > div:nth-child(2) > .govuk-radios__item > .govuk-label", + ) + .click(); + await page + .locator( + "div:nth-child(9) > .govuk-fieldset > .govuk-radios > div:nth-child(2) > .govuk-label", + ) + .click(); + await page + .locator( + "div:nth-child(10) > .govuk-form-group > .govuk-fieldset > .govuk-radios > div:nth-child(2) > .govuk-radios__item > .govuk-label", + ) + .click(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByText("No").nth(2).click(); + await page.getByText("No").nth(3).click(); + await page.getByText("No").nth(5).click(); + await page.getByText("No", { exact: true }).nth(3).click(); + await page.getByText("No", { exact: true }).nth(4).click(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByText("Yes").first().click(); + await page.getByText("Yes").nth(1).click(); + await page.getByText("No", { exact: true }).nth(2).click(); + await page.getByText("Yes").nth(2).click(); + await page.getByText("No", { exact: true }).nth(3).click(); + await page.getByText("No", { exact: true }).nth(4).click(); + await page.getByText("No", { exact: true }).nth(5).click(); + await page + .locator( + "div:nth-child(9) > .govuk-form-group > .govuk-fieldset > .govuk-radios > div:nth-child(2) > .govuk-radios__item > .govuk-label", + ) + .click(); + await page + .locator( + "div:nth-child(10) > .govuk-form-group > .govuk-fieldset > .govuk-radios > div:nth-child(2) > .govuk-radios__item > .govuk-label", + ) + .click(); + await page.getByRole("button", { name: "Continue" }).click(); + // await page.getByText("Drag and drop your files here").click(); + + const files = [ + path.resolve(__dirname, "../../testFiles/file1.pdf"), + path.resolve(__dirname, "../../testFiles/file2.pdf"), + ]; + + const fileInput = page.locator("#representationDocuments"); + + await fileInput.setInputFiles(files); + const uploadedFiles = page.locator("h4", { hasText: "Uploaded files" }); + + await expect(uploadedFiles).toBeVisible({ timeout: 60000 }); + + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("textbox", { name: "Signed" }).click(); + await page.getByRole("textbox", { name: "Signed" }).fill("Bob Bob"); + await page.getByRole("textbox", { name: "Date" }).click(); + await page.getByRole("option", { name: `Choose ${finalDate}` }).click(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByText("I wish to download a copy of").click(); + await page + .getByRole("checkbox", { name: "I confirm I have read the" }) + .check(); + const downloadPromise = page.waitForEvent("download"); + await page.getByRole("button", { name: "Continue" }).click(); + const download = await downloadPromise; + await page.getByRole("link", { name: "Continue" }).click(); + + await page.goto("/"); + + await page.waitForTimeout(2000); +}); + +test("Raise Statement representation as an LPA", async ({ page }) => { + await loginLPA(page); + //raise rep + + await page.getByRole("textbox", { name: "Use the case reference" }).click(); + await page + .getByRole("textbox", { name: "Use the case reference" }) + .fill(statementCase); + await page.getByRole("button", { name: "Submit search" }).click(); + + await page.locator("dd a.govuk-link--no-underline").first().click(); + + await page.getByRole("link", { name: "Make representation" }).click(); + + await page.getByRole("radio", { name: "Statement" }).check(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("paragraph").filter({ hasText: /^$/ }).click(); + await page.locator(".ql-editor").fill(generatePlanningText(5)); + + const files = [ + path.resolve(__dirname, "../../testFiles/file1.pdf"), + path.resolve(__dirname, "../../testFiles/file2.pdf"), + ]; + + const fileInput = page.locator("#representationDocuments"); + + await fileInput.setInputFiles(files); + const uploadedFiles = page.locator("h4", { hasText: "Uploaded files" }); + + await expect(uploadedFiles).toBeVisible({ timeout: 60000 }); + + await page.getByRole("button", { name: "Continue" }).click(); + await page + .getByRole("checkbox", { name: "I confirm I have read the" }) + .check(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("link", { name: "Continue" }).click(); + await page.goto("/"); + + await page.waitForTimeout(2000); +}); + +test("Raise LIR as an LPA", async ({ page }) => { + await loginLPA(page); + //raise rep + + await page.getByRole("textbox", { name: "Use the case reference" }).click(); + await page + .getByRole("textbox", { name: "Use the case reference" }) + .fill(consultationCase); + await page.getByRole("button", { name: "Submit search" }).click(); + + await page.locator("dd a.govuk-link--no-underline").first().click(); + + await page + .getByRole("link", { name: "Submit a Consultation Response" }) + .click(); + + await page.getByRole("radio", { name: "Local Impact Report" }).check(); + await page.getByRole("button", { name: "Continue" }).click(); + + await page.getByRole("paragraph").filter({ hasText: /^$/ }).click(); + await page.locator(".ql-editor").fill(generatePlanningText(5)); + const files = [ + path.resolve(__dirname, "../../testFiles/file1.pdf"), + path.resolve(__dirname, "../../testFiles/file2.pdf"), + ]; + + const fileInput = page.locator("#representationDocuments"); + + await fileInput.setInputFiles(files); + const uploadedFiles = page.locator("h4", { hasText: "Uploaded files" }); + + await expect(uploadedFiles).toBeVisible({ timeout: 60000 }); + + await page.getByRole("button", { name: "Continue" }).click(); + await page + .getByRole("checkbox", { name: "I confirm I have read the" }) + .check(); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("link", { name: "Continue" }).click(); + await page.goto("/"); + + await page.waitForTimeout(2000); +}); + +test("Open Saved Rep as an Interested Party", async ({ page }) => { + await loginIP(page); + //raise rep + + const firstCaseLink = page + .locator("#my-representations-card .govuk-link--no-underline") + .first(); + + await firstCaseLink.click(); + + await page.getByRole("button", { name: "Save and exit" }).click(); + await page.goto("/"); + + await page.waitForTimeout(2000); +}); diff --git a/pwe2e/tests/loggedin/s78newappeal.spec.js b/pwe2e/tests/loggedin/s78newappeal.spec.js new file mode 100644 index 00000000..6f9629ea --- /dev/null +++ b/pwe2e/tests/loggedin/s78newappeal.spec.js @@ -0,0 +1,377 @@ +import { test, expect } from "@playwright/test"; +import path from "path"; +import * as XLSX from "xlsx"; + +import { + login, + fillYesNoConditional, + fillQuillEditor, + toDDMMYYYY, +} from "../../helpers"; + +const workbook = XLSX.readFile("./testFiles/newappeals_s78.xlsx"); +const sheetName = workbook.SheetNames[0]; +const data = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName], { + defval: "", + cellDates: true, +}); + +// console.log("sheetName :", sheetName); +// console.log("Loaded rows:", data); + +data.forEach((row, index) => { + test(`Import new appeal for ${row["Appellant / Organisation"]}`, async ({ + page, + }) => { + await login(page); + + await page.getByRole("link", { name: "Make a new appeal" }).click(); + await page + .getByRole("textbox", { name: "Appellant or organisation's" }) + .click(); + await page + .getByRole("textbox", { name: "Appellant or organisation's" }) + .fill(row["Appellant / Organisation"]); + + await page + .getByRole("radio", { name: row["Appellant / Agent Radio Button"] }) + .click(); + + await page.locator("#pinswg_appellantfirstname").fill(row["First Name"]); + await page.locator("#pinswg_appellantlastname").fill(row["Last Name"]); + await page.locator("#pinswg_appellantaddress").fill(row["Address"]); + await page + .locator("#pinswg_appellantemailaddress") + .fill(row["Email Address"]); + await page + .locator("#pinswg_appellantphonenumber") + .fill(row["Telephone Number"]); + + // Excel value: "English" or "Welsh" + const languageText = row["Language Preference Radio Button"]?.trim(); + + // Map Excel text to radio button value + const radioValueLang = languageText === "Welsh" ? "846040001" : "846040000"; + + // Select the radio by name and value + await page + .locator( + `input[name="pinswg_languagepreference"][value="${radioValueLang}"]` + ) + .check(); + + if (row["Appellant / Agent Radio Button"] == "Agent") { + await page.locator("#pinswg_agentcompanyname").fill(row["Company Name"]); + await page + .locator("#pinswg_agentreference") + .fill(row["Agent's Reference"]); + + await page.locator("#pinswg_agentfirstname").fill(row["First Name_1"]); + await page.locator("#pinswg_agentlastname").fill(row["Last Name_1"]); + await page.locator("#pinswg_agentaddress").fill(row["Address_1"]); + await page + .locator("#pinswg_agentemailaddress") + .fill(row["Email Address_1"]); + await page + .locator("#pinswg_agentphonenumber") + .fill(row["Telephone Number_1"]); + + const agentLanguageText = + row["Agent Language Preference Radio Button"]?.trim(); + const agentRadioValue = + agentLanguageText === "Welsh" ? "846040001" : "846040000"; + + await page + .locator( + `input[name="pinswg_agentlanguagepreference"][value="${agentRadioValue}"]` + ) + .check(); + } + await page.getByText("I confirm that I am making an").click(); + await page.getByRole("button", { name: "Continue" }).click(); + await page + .getByLabel("Select your Local Planning") + .selectOption({ label: row["Select LPA"] }); + await page.getByLabel("Select a case type").selectOption("846040000"); + await page.getByRole("button", { name: "Continue" }).click(); + await page.getByRole("button", { name: "Continue" }).click(); + + const step2 = page.locator('[aria-controls="step-panel-appeal-details"]'); + await step2.click(); + + await page + .getByRole("textbox", { name: "Development description" }) + .click(); + await page + .getByRole("textbox", { name: "Development description" }) + .fill(row["Development Description"]); + await page.getByText("No", { exact: true }).click(); + await page + .getByRole("textbox", { name: "LPA's application reference" }) + .fill(row["LPA's Application Ref Num"]); + + await page + .locator("#pinswg_dateofapplication") + .fill(toDDMMYYYY(row["App Form Dated"])); + await page + .getByLabel("Reason for appeal") + .selectOption({ label: row["Reason for Appeal"] }); + + await page.getByRole("textbox", { name: "Site address line 1" }).click(); + await page + .getByRole("textbox", { name: "Site address line 1" }) + .fill(row["Site Address Line 1"]); + await page + .getByRole("textbox", { name: "Site address line 2" }) + .fill(row["Site Address Line 2"]); + await page + .getByRole("textbox", { name: "Site address town" }) + .fill(row["Site Address Town"]); + await page + .getByRole("textbox", { name: "Site address county" }) + .fill(row["Site Address County"]); + + await page + .getByRole("textbox", { name: "Site address postcode" }) + .fill(row["Site Address Postcode"]); + + await page + .getByRole("textbox", { name: "Size of overall appeal site (" }) + .fill(String(row["Size of Overall Appeal Site"])); + + await page + .getByRole("textbox", { name: "What is the area of floor" }) + .fill(String(row["Floor Space (Sq Metres)"])); + await page.getByRole("button", { name: "Continue" }).click(); + + // Ownership Certificate selection and conditional fields + const ownershipMap = { + "Certificate A": "846040001", + "Certificate B": "846040002", + "Certificate C": "846040003", + "Certificate D": "846040003", // C and D share same option + }; + + const cert = row["Ownership Certificate"]?.trim(); + const value = ownershipMap[cert]; + + if (!value) throw new Error(`Unknown ownership certificate: ${cert}`); + + // Select the certificate radio + await page + .locator(`input[name="pinswg_ownershipcertificate"][value="${value}"]`) + .check(); + + // Conditional fields for Certificate B + if (cert === "Certificate B") { + // Wait for Owner Name input to appear (dynamic rendering) + const ownerInput = page.locator( + 'input[id^="pinswg_owners"][id$=".pinswg_owners"]' + ); + await ownerInput.waitFor({ state: "visible", timeout: 5000 }); + await ownerInput.fill(row["Owner Name"] || ""); + + // Wait for Date Served input to appear + const dateServedInput = page.locator( + ".react-datepicker__input-container input" + ); + await dateServedInput.waitFor({ state: "visible", timeout: 5000 }); + await dateServedInput.fill(toDDMMYYYY(row["Date Served"]) || ""); + } + + // Ownership Certificate selection and conditional fields + const agHoldingMap = { + "(a)": "846040002", + "(b) (i)": "846040001", + "(b) (ii)": "846040000", + }; + + const agriculturalHolding = row["Agricultural Holding"]?.trim(); + const agValue = agHoldingMap[agriculturalHolding]; + + if (!agValue) throw new Error(`Unknown ownership certificate: ${cert}`); + + // Select the certificate radio + await page + .locator(`input[name="pinswg_agriculturalholding"][value="${agValue}"]`) + .check(); + + // Conditional fields for b (ii) + if (agriculturalHolding === "(b) (ii)") { + // Wait for Owner Name input to appear (dynamic rendering) + const tenantInput = page.locator( + 'input[id^="pinswg_agriculturaltenantname"][id$=".pinswg_agriculturaltenantname"]' + ); + await tenantInput.waitFor({ state: "visible", timeout: 5000 }); + await tenantInput.fill(row["Tenant Name"] || ""); + + // Wait for Date Served input to appear + const dateServedInput = page.locator( + ".react-datepicker__input-container input" + ); + await dateServedInput.waitFor({ state: "visible", timeout: 5000 }); + await dateServedInput.fill(toDDMMYYYY(row["Date Served_1"]) || ""); + } + + await page.getByRole("button", { name: "Continue" }).click(); + + // Site viewable from road + await fillYesNoConditional( + page, + "pinswg_siteviewablefromroad", + row["See Relevant Parts?"], + null, + null + ); + + // Inspector needed on site + await fillYesNoConditional( + page, + "pinswg_inspectorneededonsite", + row["Essential to Enter?"], + "#pinswg_whyisinspectorneededonsite", + row["Please Explain (Enter)"] + ); + + // Health & safety issues + await fillYesNoConditional( + page, + "pinswg_healthandsafetyissuesonsite", + row["Any H&S Issues?"], + "#pinswg_outlinehealthandsafetyissuesonsite", + row["Set out H&S Issues"] + ); + + await page.getByRole("button", { name: "Continue" }).click(); + + // Step 5 Other Appeals + + await fillYesNoConditional( + page, + "pinswg_additionalappealsmade", // radio name + row["Any Other Appeals?"], // "Y" or blank from Excel + "#pinswg_additionalappealsmadedetails", // Quill editor selector + row["Other Appeal Details"], // text to fill + false // flag that it's a Quill editor + ); + + await page.getByRole("button", { name: "Continue" }).click(); + + // Step 6 Submission of Case + + // Q.1 + const subnissionOfCaseInput = page.locator(".ql-editor"); + await subnissionOfCaseInput.waitFor({ state: "visible", timeout: 100000 }); + await subnissionOfCaseInput.fill(row["Submit Statement of Case"]); + + // Q.2 + const attachSocValue = (row["Attached Separate Case Doc?"] || "") + .trim() + .toUpperCase(); + + const radioValue = attachSocValue === "Y" ? "true" : "false"; + + // Select the radio + await page + .locator(`input[name="pinswg_attachsoc"][value="${radioValue}"]`) + .check(); + + // Q.3 + const procedureValue = row["What Procedure?"]?.trim(); // e.g., "Written representation" + + // Wait for the dropdown to appear + const procedureSelect = page.locator("#pinswg_procedure"); + await procedureSelect.waitFor({ state: "visible", timeout: 5000 }); + + // Select by label (the visible text) + await procedureSelect.selectOption({ label: procedureValue }); + + // If not "Written representation", fill the explanation box + if ( + procedureValue && + procedureValue.toLowerCase() !== "written representation" + ) { + const explanation = + row["Procedure Explanation"] || + "Default explanation: A hearing/inquiry is required due to case complexity and witnesses."; + + const textArea = page.locator("#pinswg_chosenprocedureexplanation"); + await textArea.waitFor({ state: "visible", timeout: 5000 }); + await textArea.fill(explanation); + } + + // Q.4 + await fillYesNoConditional( + page, + "pinswg_costappliedforindicator", // radio name + row["Cost Applied For"], // "Y" or blank from Excel + ".ql-editor", // Quill editor selector + row["Cost Application"], // text to fill + true // flag that it's a Quill editor + ); + + // Q.5 + await fillYesNoConditional( + page, + "pinswg_s106unilateralsubmitted", // radio name + row["Submit S106?"] // "Y" or blank from Excel + ); + + await page.getByRole("button", { name: "Continue" }).click(); + + // Step 7 Upload Documents + + const files = [ + path.resolve(__dirname, "../../testFiles/file1.pdf"), + path.resolve(__dirname, "../../testFiles/file2.pdf"), + ]; + + const firstInputFiles = [ + path.resolve(__dirname, "../../testFiles/file1.pdf"), + ]; + + const fileInputs = [ + "#pinswg_fileUpload", + "#pinswg_fileUpload1", + "#pinswg_fileUpload2", + "#pinswg_fileUpload3", + "#pinswg_fileUpload4", + "#pinswg_fileUpload5", + "#pinswg_fileUpload6", + "#pinswg_fileUpload7", + "#pinswg_fileUpload8", + "#pinswg_fileUpload9", + "#pinswg_fileUpload10", + "#pinswg_fileUpload11", + "#pinswg_fileUpload12", + "#pinswg_fileUpload13", + "#pinswg_fileUpload14", + "#pinswg_fileUpload15", + ]; + + for (let i = 0; i < fileInputs.length; i++) { + const selector = fileInputs[i]; + if (i === 0) { + // First input gets its specific files + await page.locator(selector).setInputFiles(firstInputFiles); + } else { + // All other inputs get the common files + await page.locator(selector).setInputFiles(files); + } + const uploadedFiles = page + .locator("h4", { hasText: "Uploaded files" }) + .first(); + await expect(uploadedFiles).toBeVisible({ timeout: 60000 }); + } + + await page.getByRole("button", { name: "Submit" }).click(); + + // Check and Confirm + await page + .getByRole("checkbox", { name: "I confirm that all sections" }) + .check(); + await page.getByText("Submit appeal").click(); + + await page.waitForTimeout(20000); + }); +}); diff --git a/pwe2e/tests/loggedout/downloadFile.spec.js b/pwe2e/tests/loggedout/downloadFile.spec.js new file mode 100644 index 00000000..28d654e3 --- /dev/null +++ b/pwe2e/tests/loggedout/downloadFile.spec.js @@ -0,0 +1,26 @@ +import { test, expect } from "@playwright/test"; + +test("Download a file", async ({ page }) => { + await page.goto("/"); + await page.getByRole("button", { name: "Accept cookies" }).click(); + await page + .getByRole("textbox", { name: "Use the case reference or DNS" }) + .click(); + await page + .getByRole("textbox", { name: "Use the case reference or DNS" }) + .fill("gaerwen"); + await page.getByRole("button", { name: "Submit search" }).click(); + // await page + // .getByRole("link", { name: "DNS3276735 - Gaerwen Wind Farm (test)" }) + // .click(); + + await page.locator("dd a.govuk-link--no-underline").first().click(); + + const downloadPromise = page.waitForEvent("download"); + + await page.locator(".documentLink").first().click(); + + const download = await downloadPromise; + await page.getByText("Download complete!").click(); + await page.getByRole("link", { name: "Home" }).click(); +}); diff --git a/pwe2e/tests/loggedout/homepageLinks.spec.js b/pwe2e/tests/loggedout/homepageLinks.spec.js new file mode 100644 index 00000000..15c945e9 --- /dev/null +++ b/pwe2e/tests/loggedout/homepageLinks.spec.js @@ -0,0 +1,35 @@ +import { test, expect } from "@playwright/test"; +import { handleSearchResults } from "../../helpers/index"; + +test("Check for DNS applications", async ({ page }) => { + await page.goto("/"); + await page.getByRole("button", { name: "Accept cookies" }).click(); + await page.getByRole("link", { name: "View all DNS applications" }).click(); + + async function handleSearchResults(page) { + const results = page.locator("dd a.govuk-link--no-underline"); + + await Promise.race([ + results.first().waitFor({ state: "visible", timeout: 15000 }), + ]); + + if ((await results.count()) > 0) { + await results.first().click({ timeout: 15000 }); + await expect(page.locator("#main-content")).toContainText("Reference:", { + timeout: 15000, + }); + return "clicked"; + } else if ((await noResultsMessage.count()) > 0) { + console.log("ℹ️ No results found. Test passes."); + return "no-results"; + } else { + throw new Error( + "⚠️ Unexpected search page state: neither results nor zero-results message found." + ); + } + } + + // Handle search results safely + const outcome = await handleSearchResults(page); + console.log(`Search outcome for DNS":`, outcome); +}); diff --git a/pwe2e/tests/loggedout/searches.spec.js b/pwe2e/tests/loggedout/searches.spec.js new file mode 100644 index 00000000..4966be7c --- /dev/null +++ b/pwe2e/tests/loggedout/searches.spec.js @@ -0,0 +1,208 @@ +import { test, expect } from "@playwright/test"; +import * as XLSX from "xlsx"; +import { handleSearchResults } from "../../helpers/index"; +test.use({ headless: true }); + +test("Basic search", async ({ page }) => { + await page.goto("/"); + await page.getByRole("button", { name: "Accept cookies" }).click(); + await page + .getByRole("textbox", { name: "Use the case reference or DNS" }) + .click(); + await page + .getByRole("textbox", { name: "Use the case reference or DNS" }) + .fill("cas-"); + await page.getByRole("button", { name: "Submit search" }).click(); + + // Handle search results safely + const outcome = await handleSearchResults(page); + console.log(`Search outcome for basic search":`, outcome); +}); + +const workbook = XLSX.readFile("./testFiles/appealList.xlsx"); +const sheetName = workbook.SheetNames[0]; +const rawData = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName], { + defval: "", +}); + +const data = rawData + .map((row) => ({ + Values: row["Values"]?.trim(), + AppealTypes: row["AppealType"]?.trim(), + })) + .filter((r) => r.Values && r.AppealTypes); + +test.describe("Advanced Search Appeal Types", () => { + if (data.length === 0) { + test("No data found in spreadsheet", async () => { + console.warn("No valid rows found in appealList.xlsx"); + }); + } + + data.forEach((row, index) => { + test(`Advanced search - ${row.AppealTypes} #${index + 1}`, async ({ + page, + }) => { + await page.goto("/"); + + const acceptButton = page.getByRole("button", { name: "Accept cookies" }); + if (await acceptButton.isVisible()) await acceptButton.click(); + + await page.getByRole("link", { name: "Advanced search" }).click(); + await page.getByLabel("Select a case type").selectOption(row.Values); + await page.getByRole("button", { name: "Search" }).click(); + + // Handle search results safely + const outcome = await handleSearchResults(page); + console.log(`Search outcome for "${row.AppealTypes}":`, outcome); + }); + }); +}); + +const sheetName2 = workbook.SheetNames[1]; +const rawData2 = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName2], { + defval: "", +}); + +const data2 = rawData2 + .map((row) => ({ + Values: row["Values"]?.trim(), + LPA: row["LPA"]?.trim(), + })) + .filter((r) => r.Values && r.LPA); + +test.describe("Advanced Search LPA", () => { + if (data2.length === 0) { + test("No data found in spreadsheet", async () => { + console.warn("⚠️ No valid rows found in lpaList.xlsx"); + }); + } + + data2.forEach((row, index) => { + test(`Advanced search LPA - ${row.LPA} #${index + 1}`, async ({ page }) => { + await page.goto("/"); + + const acceptButton = page.getByRole("button", { name: "Accept cookies" }); + if (await acceptButton.isVisible()) await acceptButton.click(); + + await page.getByRole("link", { name: "Advanced search" }).click(); + await page + .getByLabel("Local Planning Authority") + .selectOption(row.Values); + await page.getByRole("button", { name: "Search" }).click(); + + // Handle search results safely + const outcome = await handleSearchResults(page); + console.log(`Search outcome for "${row.LPA}":`, outcome); + }); + }); +}); + +const sheetName3 = workbook.SheetNames[2]; +const rawData3 = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName3], { + defval: "", +}); + +const data3 = rawData3 + .map((row) => ({ + Values: row["Values"], + Status: row["Status"]?.trim(), + })) + .filter((r) => r.Values && r.Status); + +test.describe("Advanced Search Case Status", () => { + if (data3.length === 0) { + test("No data found in spreadsheet", async () => { + console.warn("⚠️ No valid rows found in lpaList.xlsx"); + }); + } + + data3.forEach((row, index) => { + test(`Advanced search Case Status - ${row.Status} #${index + 1}`, async ({ + page, + }) => { + await page.goto("/"); + + const acceptButton = page.getByRole("button", { name: "Accept cookies" }); + if (await acceptButton.isVisible()) await acceptButton.click(); + + await page.getByRole("link", { name: "Advanced search" }).click(); + await page.getByLabel("Case Status").selectOption(String(row.Values)); + await page.getByRole("button", { name: "Search" }).click(); + + // Handle search results safely + const outcome = await handleSearchResults(page); + console.log(`Search outcome for "${row.Status}":`, outcome); + }); + }); +}); + +test.describe("Address search", () => { + test("Address search AddressLine1", async ({ page }) => { + await page.goto("/"); + const acceptButton = page.getByRole("button", { name: "Accept cookies" }); + if (await acceptButton.isVisible()) await acceptButton.click(); + await page.getByRole("link", { name: "Address search" }).click(); + await page.locator("#addressline1").click(); + await page.locator("#addressline1").fill("street"); + await page.getByRole("button", { name: "Search" }).click(); + + // Handle search results safely + const outcome = await handleSearchResults(page); + console.log(`Search outcome for AddressLine1":`, outcome); + }); + test("Address search Town", async ({ page }) => { + await page.goto("/"); + const acceptButton = page.getByRole("button", { name: "Accept cookies" }); + if (await acceptButton.isVisible()) await acceptButton.click(); + await page.getByRole("link", { name: "Address search" }).click(); + await page.locator("#town").click(); + await page.locator("#town").fill("Cardiff"); + await page.getByRole("button", { name: "Search" }).click(); + + // Handle search results safely + const outcome = await handleSearchResults(page); + console.log(`Search outcome for Town":`, outcome); + }); + test("Address search County", async ({ page }) => { + await page.goto("/"); + const acceptButton = page.getByRole("button", { name: "Accept cookies" }); + if (await acceptButton.isVisible()) await acceptButton.click(); + await page.getByRole("link", { name: "Address search" }).click(); + await page.locator("#county").click(); + await page.locator("#county").fill("Cardiff"); + await page.getByRole("button", { name: "Search" }).click(); + + // Handle search results safely + const outcome = await handleSearchResults(page); + console.log(`Search outcome for County":`, outcome); + }); + test("Address search Postcode", async ({ page }) => { + await page.goto("/"); + const acceptButton = page.getByRole("button", { name: "Accept cookies" }); + if (await acceptButton.isVisible()) await acceptButton.click(); + await page.getByRole("link", { name: "Address search" }).click(); + await page.locator("#postcode").click(); + await page.locator("#postcode").fill("cf24"); + await page.getByRole("button", { name: "Search" }).click(); + + // Handle search results safely + const outcome = await handleSearchResults(page); + console.log(`Search outcome for Postcode":`, outcome); + }); + test("Address search Appellant or Applicant", async ({ page }) => { + await page.goto("/"); + const acceptButton = page.getByRole("button", { name: "Accept cookies" }); + if (await acceptButton.isVisible()) await acceptButton.click(); + await page.getByRole("link", { name: "Address search" }).click(); + await page.locator("#appellantname").click(); + await page.locator("#appellantname").fill("David"); + await page.locator("#addressline1").click(); + await page.locator("#addressline1").fill("Street"); + await page.getByRole("button", { name: "Search" }).click(); + + // Handle search results safely + const outcome = await handleSearchResults(page); + console.log(`Search outcome for Postcode":`, outcome); + }); +}); diff --git a/pwe2e/tests/test-1.spec.ts b/pwe2e/tests/test-1.spec.ts new file mode 100644 index 00000000..b915e038 --- /dev/null +++ b/pwe2e/tests/test-1.spec.ts @@ -0,0 +1,71 @@ +import { test, expect } from '@playwright/test'; + +test('test', async ({ page }) => { + await page.locator('body').click(); + await page.goto('http://localhost:3000/auth/error?error=Verification + await page.getByRole('link', { name: 'Home', exact: true }).click(); + await page.goto('http://localhost:3000/myportal'); + await page.goto('http://localhost:3000/myportal/case/CAS-01085-K4R4X0?q=CAS-01085-K4R4X0&adv=false'); + await page.getByRole('link', { name: 'Make representation' }).click(); + await page.getByRole('combobox').selectOption('Questionnaire'); + await page.getByRole('combobox').selectOption('Written representations'); + await page.getByText('No').first().click(); + await page.getByText('No').nth(1).click(); + await page.getByText('No').nth(2).click(); + await page.getByText('Yes').nth(2).click(); + await page.getByText('No').nth(3).click(); + await page.getByRole('paragraph').filter({ hasText: /^$/ }).click(); + await page.locator('.ql-editor').fill('w'); + await page.locator('div').filter({ hasText: /^w$/ }).nth(2).fill('wdwdwdwdwdw '); + await page.getByRole('button', { name: 'Continue' }).click(); + await page.locator('.ql-editor').first().click(); + await page.locator('.ql-editor').first().fill('wwwqwqeq wefqewr qwerq'); + await page.getByRole('paragraph').nth(4).click(); + await page.locator('.ql-editor.ql-blank').first().fill('qweqwe qwrqrt qwe qweqwqwe'); + await page.getByRole('paragraph').filter({ hasText: /^$/ }).click(); + await page.locator('.ql-editor.ql-blank').first().fill('qweqwe qwrqrt qwe qweqwqweq'); + await page.locator('.ql-editor.ql-blank').fill('w e'); + await page.locator('div').filter({ hasText: /^w e$/ }).nth(2).fill('w eqewrqwe q'); + await page.getByRole('paragraph').filter({ hasText: /^$/ }).click(); + await page.locator('div').filter({ hasText: /^w e$/ }).nth(2).fill('w eqewrqwe qweqwe '); + await page.getByText('No').nth(1).click(); + await page.getByText('No').nth(2).click(); + await page.getByText('No').nth(3).click(); + await page.getByText('No').nth(4).click(); + await page.getByText('No').nth(5).click(); + await page.locator('div:nth-child(8) > .govuk-form-group > .govuk-fieldset > .govuk-radios > div:nth-child(2) > .govuk-radios__item > .govuk-label').click(); + await page.locator('div:nth-child(9) > .govuk-fieldset > .govuk-radios > div:nth-child(2) > .govuk-label').click(); + await page.locator('div:nth-child(10) > .govuk-form-group > .govuk-fieldset > .govuk-radios > div:nth-child(2) > .govuk-radios__item > .govuk-label').click(); + await page.getByRole('button', { name: 'Continue' }).click(); + await page.getByText('No').nth(2).click(); + await page.getByText('No').nth(3).click(); + await page.getByText('No').nth(5).click(); + await page.getByText('No', { exact: true }).nth(3).click(); + await page.getByText('No', { exact: true }).nth(4).click(); + await page.getByRole('button', { name: 'Continue' }).click(); + await page.getByRole('group', { name: '26. A copy of the applicant’s' }).click(); + await page.getByText('No').nth(4).click(); + await page.getByText('Yes').nth(1).click(); + await page.getByText('No', { exact: true }).nth(2).click(); + await page.getByText('Yes').nth(2).click(); + await page.getByText('No', { exact: true }).nth(3).click(); + await page.getByText('No', { exact: true }).nth(4).click(); + await page.getByText('No', { exact: true }).nth(5).click(); + await page.locator('div:nth-child(9) > .govuk-form-group > .govuk-fieldset > .govuk-radios > div:nth-child(2) > .govuk-radios__item > .govuk-label').click(); + await page.locator('div:nth-child(10) > .govuk-form-group > .govuk-fieldset > .govuk-radios > div:nth-child(2) > .govuk-radios__item > .govuk-label').click(); + await page.getByRole('button', { name: 'Continue' }).click(); + await page.getByText('Drag and drop your files here').click(); + await page.getByRole('button', { name: 'Continue' }).click(); + await page.getByRole('textbox', { name: 'Signed' }).click(); + await page.getByRole('textbox', { name: 'Signed' }).fill('Bob Bob'); + await page.getByRole('textbox', { name: 'Date' }).click(); + await page.getByRole('option', { name: 'Choose Tuesday, April 14th,' }).click(); + await page.getByRole('button', { name: 'Continue' }).click(); + await page.getByText('I wish to download a copy of').click(); + await page.getByRole('checkbox', { name: 'I confirm I have read the' }).check(); + const downloadPromise = page.waitForEvent('download'); + await page.getByRole('button', { name: 'Continue' }).click(); + const download = await downloadPromise; + await page.getByRole('link', { name: 'Continue' }).click(); + await page.goto('http://localhost:3000/myportal');'); +}); \ No newline at end of file diff --git a/pwe2e/tests/test-2.spec.ts b/pwe2e/tests/test-2.spec.ts new file mode 100644 index 00000000..5b3d48cf --- /dev/null +++ b/pwe2e/tests/test-2.spec.ts @@ -0,0 +1,5 @@ +import { test, expect } from '@playwright/test'; + +test('test', async ({ page }) => { + // Recording... +}); \ No newline at end of file diff --git a/pwe2e/tests/test-3.spec.ts b/pwe2e/tests/test-3.spec.ts new file mode 100644 index 00000000..9f3b9b0e --- /dev/null +++ b/pwe2e/tests/test-3.spec.ts @@ -0,0 +1,22 @@ +import { test, expect } from "@playwright/test"; + +test("test", async ({ page }) => { + // Recording... + await page.goto("http://localhost:3000/auth/error?error=Verification"); + await page.getByRole("link", { name: "Home", exact: true }).click(); + await page.goto("http://localhost:3000/myportal"); + await page.getByRole("heading", { name: "Representations awaiting" }).click(); + + const containerLink = page + .locator("div", { hasText: "Representations awaiting" }) + .locator("a") + .first() + .click(); + + await page.getByText("CAS-01085-K4R4X0").first().click(); + await page.goto( + "http://localhost:3000/myportal/representation?case=CAS-01085-K4R4X0&state=edit&created=2026-04-15-12%3A32%3A23_-_LPA_-_Questionnaire_-_CardiffCouncil", + ); + await page.getByRole("button", { name: "Save and exit" }).click(); + await page.goto("http://localhost:3000/myportal"); +});