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); }); });