diff --git a/applications/Unity.AutoUI/cypress.pipeline.env.json b/applications/Unity.AutoUI/cypress.pipeline.env.json index c31a550fe..f39f50d81 100644 --- a/applications/Unity.AutoUI/cypress.pipeline.env.json +++ b/applications/Unity.AutoUI/cypress.pipeline.env.json @@ -4,5 +4,8 @@ "test1username": "{{test1username}}", "test1password": "{{test1password}}", "test2username": "{{test2username}}", - "test2password": "{{test2password}}" + "test2password": "{{test2password}}", + "TEST_EMAIL_TO": "{{test_email_to}}", + "TEST_EMAIL_CC": "{{test_email_CC}}", + "TEST_EMAIL_BCC": "{{test_email_BCC}}" } \ No newline at end of file diff --git a/applications/Unity.AutoUI/cypress/e2e/ApplicationsActionBar.cy.ts b/applications/Unity.AutoUI/cypress/e2e/ApplicationsActionBar.cy.ts new file mode 100644 index 000000000..6d3cdd6ea --- /dev/null +++ b/applications/Unity.AutoUI/cypress/e2e/ApplicationsActionBar.cy.ts @@ -0,0 +1,647 @@ +/// + +import { loginIfNeeded } from "../support/auth"; + +describe("Unity Login and check data from CHEFS", () => { + const STANDARD_TIMEOUT = 20000; + + function switchToDefaultGrantsProgramIfAvailable() { + cy.get("body").then(($body) => { + const hasUserInitials = $body.find(".unity-user-initials").length > 0; + + if (!hasUserInitials) { + cy.log("Skipping tenant switch: no user initials menu found"); + return; + } + + cy.get(".unity-user-initials").click(); + + cy.get("body").then(($body2) => { + const switchLink = $body2 + .find("#user-dropdown a.dropdown-item") + .filter((_, el) => { + return (el.textContent || "").trim() === "Switch Grant Programs"; + }); + + if (switchLink.length === 0) { + cy.log( + 'Skipping tenant switch: "Switch Grant Programs" not present for this user/session', + ); + cy.get("body").click(0, 0); + return; + } + + cy.wrap(switchLink.first()).click(); + + cy.url({ timeout: STANDARD_TIMEOUT }).should( + "include", + "/GrantPrograms", + ); + + cy.get("#search-grant-programs", { timeout: STANDARD_TIMEOUT }) + .should("be.visible") + .clear() + .type("Default Grants Program"); + + // Flatten nested `within` usage to satisfy S2004 (limit nesting depth) + cy.contains( + "#UserGrantProgramsTable tbody tr", + "Default Grants Program", + { timeout: STANDARD_TIMEOUT }, + ) + .should("exist") + .within(() => { + cy.contains("button", "Select").should("be.enabled").click(); + }); + + cy.location("pathname", { timeout: STANDARD_TIMEOUT }).should((p) => { + expect( + p.indexOf("/GrantApplications") >= 0 || p.indexOf("/auth/") >= 0, + ).to.eq(true); + }); + }); + }); + } + + // TEST renders the Submission tab inside an open shadow root (Form.io). + // Enabling this makes cy.get / cy.contains pierce shadow DOM consistently across envs. + before(() => { + Cypress.config("includeShadowDom", true); + loginIfNeeded({ timeout: STANDARD_TIMEOUT }); + }); + + it("Switch to Default Grants Program if available", () => { + switchToDefaultGrantsProgramIfAvailable(); + }); + + it("Tests the existence and functionality of the Submitted Date From and Submitted Date To filters", () => { + const pad2 = (n: number) => String(n).padStart(2, "0"); + + const todayIsoLocal = () => { + const d = new Date(); + return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`; + }; + + const waitForRefresh = () => { + // S3923 fix: remove identical branches; assert spinner is hidden when present. + cy.get('div.spinner-grow[role="status"]', { + timeout: STANDARD_TIMEOUT, + }).then(($s) => { + cy.wrap($s) + .should("have.attr", "style") + .and("contain", "display: none"); + }); + }; + + // --- Submitted Date From --- + cy.get("input#submittedFromDate", { timeout: STANDARD_TIMEOUT }) + .click({ force: true }) + .clear({ force: true }) + .type("2022-01-01", { force: true }) + .trigger("change", { force: true }) + .blur({ force: true }) + .should("have.value", "2022-01-01"); + + waitForRefresh(); + + // --- Submitted Date To --- + const today = todayIsoLocal(); + + cy.get("input#submittedToDate", { timeout: STANDARD_TIMEOUT }) + .click({ force: true }) + .clear({ force: true }) + .type(today, { force: true }) + .trigger("change", { force: true }) + .blur({ force: true }) + .should("have.value", today); + + waitForRefresh(); + }); + + // With no rows selected verify the visibility of Filter, Export, Save View, and Columns. + it("Verifies the expected action buttons are visible when no rows are selected", () => { + cy.get("#GrantApplicationsTable", { timeout: STANDARD_TIMEOUT }).should( + "exist", + ); + + // Ensure we start from a clean selection state (0 selected) + // (Using same "select all / deselect all" toggle approach as the working 1-row test) + cy.get("div.dt-scroll-head thead input", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .click({ force: true }) + .click({ force: true }); + + cy.get("#GrantApplicationsTable tbody tr.selected", { + timeout: STANDARD_TIMEOUT, + }).should("have.length", 0); + + // Filter button (left action bar group) + cy.get("#btn-toggle-filter", { timeout: STANDARD_TIMEOUT }).should( + "be.visible", + ); + + // Right-side buttons + cy.contains( + "#dynamicButtonContainerId .dt-buttons button span", + "Export", + { timeout: STANDARD_TIMEOUT }, + ).should("be.visible"); + cy.contains("#dynamicButtonContainerId button.grp-savedStates", "Save View", { + timeout: STANDARD_TIMEOUT, + }).should("be.visible"); + cy.contains( + "#dynamicButtonContainerId .dt-buttons button span", + "Columns", + { timeout: STANDARD_TIMEOUT }, + ).should("be.visible"); + + // Optional sanity: action buttons that require selection should be disabled when none selected + cy.get("#externalLink", { timeout: STANDARD_TIMEOUT }).should( + "be.disabled", + ); // Open + cy.get("#assignApplication", { timeout: STANDARD_TIMEOUT }).should( + "be.disabled", + ); // Assign + cy.get("#approveApplications", { timeout: STANDARD_TIMEOUT }).should( + "be.disabled", + ); // Approve + cy.get("#tagApplication", { timeout: STANDARD_TIMEOUT }).should( + "be.disabled", + ); // Tags + cy.get("#applicationPaymentRequest", { + timeout: STANDARD_TIMEOUT, + }).should("be.disabled"); // Payment + cy.get("#applicationLink", { timeout: STANDARD_TIMEOUT }).should( + "be.disabled", + ); // Info + }); + + // With one row selected verify the visibility of Open, Assign, Approve, Tags, Payment, Info, Filter, Export, Save View, and Columns. + it("Verifies the expected action buttons are visible when only one row is selected", () => { + cy.get("#GrantApplicationsTable", { timeout: STANDARD_TIMEOUT }).should( + "exist", + ); + + //Ensure we start from a clean selection state (0 selected) + cy.get("div.dt-scroll-head thead input", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .click({ force: true }) + .click({ force: true }); + + cy.get("#GrantApplicationsTable tbody tr.selected", { + timeout: STANDARD_TIMEOUT, + }).should("have.length", 0); + + // Select exactly 1 row (click a non-link cell, matching your earlier helper logic) + cy.get("#GrantApplicationsTable tbody tr", { timeout: STANDARD_TIMEOUT }) + .should("have.length.greaterThan", 0) + .first() + .find("td") + .not(":has(a)") + .first() + .click({ force: true }); + + cy.get("#GrantApplicationsTable tbody tr.selected", { + timeout: STANDARD_TIMEOUT, + }).should("have.length", 1); + + // Action bar (left group) + cy.get("#app_custom_buttons", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .scrollIntoView(); + + // Left-side action buttons (actual IDs on this page) + cy.get("#externalLink", { timeout: STANDARD_TIMEOUT }).should("be.visible"); // Open + cy.get("#assignApplication", { timeout: STANDARD_TIMEOUT }).should( + "be.visible", + ); // Assign + cy.get("#approveApplications", { timeout: STANDARD_TIMEOUT }).should( + "be.visible", + ); // Approve + cy.get("#tagApplication", { timeout: STANDARD_TIMEOUT }).should("be.visible"); // Tags + cy.get("#applicationPaymentRequest", { + timeout: STANDARD_TIMEOUT, + }).should("be.visible"); // Payment + cy.get("#applicationLink", { timeout: STANDARD_TIMEOUT }).should( + "be.visible", + ); // Info + + // Filter button + cy.get("#btn-toggle-filter", { timeout: STANDARD_TIMEOUT }).should( + "be.visible", + ); + + // Right-side buttons + cy.contains( + "#dynamicButtonContainerId .dt-buttons button span", + "Export", + { timeout: STANDARD_TIMEOUT }, + ).should("be.visible"); + cy.contains("#dynamicButtonContainerId button.grp-savedStates", "Save View", { + timeout: STANDARD_TIMEOUT, + }).should("be.visible"); + cy.contains( + "#dynamicButtonContainerId .dt-buttons button span", + "Columns", + { timeout: STANDARD_TIMEOUT }, + ).should("be.visible"); + }); + + it("Verifies the expected action buttons are visible when two rows are selected", () => { + const BUTTON_TIMEOUT = 60000; + + // Ensure table has rows + cy.get(".dt-scroll-body tbody tr", { timeout: STANDARD_TIMEOUT }).should( + "have.length.greaterThan", + 1, + ); + + // Select two rows using non-link cells + const clickSelectableCell = (rowIdx: number, withCtrl = false) => { + cy.get(".dt-scroll-body tbody tr", { timeout: STANDARD_TIMEOUT }) + .eq(rowIdx) + .find("td") + .not(":has(a)") + .first() + .click({ force: true, ctrlKey: withCtrl }); + }; + clickSelectableCell(0); + clickSelectableCell(1, true); + + // ActionBar + cy.get("#app_custom_buttons", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .scrollIntoView(); + + // Click Payment + cy.get("#applicationPaymentRequest", { timeout: BUTTON_TIMEOUT }) + .should("be.visible") + .and("not.be.disabled") + .click({ force: true }); + + // Wait until modal is shown + cy.get("#payment-modal", { timeout: STANDARD_TIMEOUT }) + .should("be.visible") + .and("have.class", "show"); + + // Attempt graceful closes first + cy.get("body").type("{esc}", { force: true }); // Bootstrap listens to ESC + cy.get(".modal-backdrop", { timeout: STANDARD_TIMEOUT }).then(($bd) => { + if ($bd.length) { + cy.wrap($bd).click("topLeft", { force: true }); + } + }); + + // Try footer Cancel if available (avoid .catch on Cypress chainable) + cy.contains("#payment-modal .modal-footer button", "Cancel", { + timeout: STANDARD_TIMEOUT, + }).then(($btn) => { + if ($btn && $btn.length > 0) { + cy.wrap($btn).scrollIntoView().click({ force: true }); + } else { + cy.log("Cancel button not present, proceeding to hard-close fallback"); + } + }); + + // Use window API (if present), then hard-close fallback + cy.window().then((win: any) => { + try { + if (typeof win.closePaymentModal === "function") { + win.closePaymentModal(); + } + } catch { + /* ignore */ + } + + // HARD CLOSE: forcibly hide modal and remove backdrop + const $ = (win as any).jQuery || (win as any).$; + if ($) { + try { + $("#payment-modal") + .removeClass("show") + .attr("aria-hidden", "true") + .css("display", "none"); + $(".modal-backdrop").remove(); + $("body").removeClass("modal-open").css("overflow", ""); // restore scroll + } catch { + /* ignore */ + } + } + }); + + // Verify modal/backdrop gone (be tolerant: assert non-interference instead of visibility only) + cy.get("#payment-modal", { timeout: STANDARD_TIMEOUT }).should(($m) => { + const isHidden = !$m.is(":visible") || !$m.hasClass("show"); + expect(isHidden, "payment-modal hidden or not shown").to.eq(true); + }); + cy.get(".modal-backdrop", { timeout: STANDARD_TIMEOUT }).should("not.exist"); + + // Right-side buttons usable + cy.get("#dynamicButtonContainerId", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .scrollIntoView(); + + cy.contains("#dynamicButtonContainerId .dt-buttons button span", "Export", { + timeout: STANDARD_TIMEOUT, + }).should("be.visible"); + cy.contains( + "#dynamicButtonContainerId button.grp-savedStates", + "Save View", + { timeout: STANDARD_TIMEOUT }, + ).should("be.visible"); + cy.contains( + "#dynamicButtonContainerId .dt-buttons button span", + "Columns", + { timeout: STANDARD_TIMEOUT }, + ).should("be.visible"); + }); + + // Walk the Columns menu and toggle each column on, verifying the column is visible. + it("Verify all columns in the menu are visible when and toggled on.", () => { + const escapeRegex = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + + const clickColumnsItem = (label: string) => { + // Case-insensitive exact match so DEV "ID" and PROD "Id" both work + const re = new RegExp(`^\\s*${escapeRegex(label)}\\s*$`, "i"); + cy.contains("a.dropdown-item", re, { timeout: STANDARD_TIMEOUT }) + .should("exist") + .scrollIntoView() + .click({ force: true }); + }; + + const normalize = (s: string) => + (s || "").replace(/\s+/g, " ").trim().toLowerCase(); + + const getVisibleHeaderTitles = () => { + return cy + .get(".dt-scroll-head span.dt-column-title", { + timeout: STANDARD_TIMEOUT, + }) + .then(($els) => { + const titles = Cypress.$($els) + .toArray() + .map((el) => (el.textContent || "").replace(/\s+/g, " ").trim()) + .filter((t) => t.length > 0); + return titles; + }); + }; + + const assertVisibleHeadersInclude = (expected: string[]) => { + getVisibleHeaderTitles().then((titles) => { + const normTitles = titles.map(normalize); + expected.forEach((e) => { + const target = normalize(e); + expect( + normTitles, + `visible headers should include "${e}" (case-insensitive)`, + ).to.include(target); + }); + }); + }; + + const scrollX = (x: number) => { + cy.get(".dt-scroll-body", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .scrollTo(x, 0, { duration: 0, ensureScrollable: false }); + }; + + // Close any open dropdowns/modals first + cy.get("body").then(($body) => { + if ($body.find(".dt-button-background").length > 0) { + cy.get(".dt-button-background").click({ force: true }); + } + }); + + // Open the "Save View" dropdown + cy.get("button.grp-savedStates", { timeout: STANDARD_TIMEOUT }) + .should("be.visible") + .and("contain.text", "Save View") + .click(); + + // Click "Reset to Default View" + cy.contains("a.dropdown-item", "Reset to Default View", { + timeout: STANDARD_TIMEOUT, + }) + .should("exist") + .click({ force: true }); + + // Wait for table to rebuild after reset - check for default columns + cy.get(".dt-scroll-head span.dt-column-title", { + timeout: STANDARD_TIMEOUT, + }).should("have.length.gt", 5); + + // Open Columns menu + cy.contains("span", "Columns", { timeout: STANDARD_TIMEOUT }) + .should("be.visible") + .click(); + + // Wait for columns dropdown to be fully populated + cy.get("a.dropdown-item", { timeout: STANDARD_TIMEOUT }).should( + "have.length.gt", + 50, + ); + + clickColumnsItem("% of Total Project Budget"); + clickColumnsItem("Acquisition"); + clickColumnsItem("Applicant Electoral District"); + + clickColumnsItem("Applicant Id"); + clickColumnsItem("Applicant Id"); + + clickColumnsItem("Applicant Name"); + clickColumnsItem("Applicant Name"); + + clickColumnsItem("Approved Amount"); + clickColumnsItem("Approved Amount"); + + clickColumnsItem("Assessment Result"); + + clickColumnsItem("Assignee"); + clickColumnsItem("Assignee"); + + clickColumnsItem("Business Number"); + + clickColumnsItem("Category"); + clickColumnsItem("Category"); + + clickColumnsItem("City"); + + clickColumnsItem("Community"); + clickColumnsItem("Community"); + + clickColumnsItem("Community Population"); + clickColumnsItem("Contact Business Phone"); + clickColumnsItem("Contact Cell Phone"); + clickColumnsItem("Contact Email"); + clickColumnsItem("Contact Full Name"); + clickColumnsItem("Contact Title"); + clickColumnsItem("Decision Date"); + clickColumnsItem("Decline Rationale"); + clickColumnsItem("Due Date"); + clickColumnsItem("Due Diligence Status"); + clickColumnsItem("Economic Region"); + clickColumnsItem("Forestry Focus"); + clickColumnsItem("Forestry or Non-Forestry"); + clickColumnsItem("FYE Day"); + clickColumnsItem("FYE Month"); + clickColumnsItem("Indigenous"); + clickColumnsItem("Likelihood of Funding"); + clickColumnsItem("Non-Registered Organization Name"); + clickColumnsItem("Notes"); + clickColumnsItem("Org Book Status"); + clickColumnsItem("Organization Type"); + clickColumnsItem("Other Sector/Sub/Industry Description"); + clickColumnsItem("Owner"); + clickColumnsItem("Payout"); + clickColumnsItem("Place"); + clickColumnsItem("Project Electoral District"); + clickColumnsItem("Project End Date"); + + clickColumnsItem("Project Name"); + clickColumnsItem("Project Name"); + + clickColumnsItem("Project Start Date"); + clickColumnsItem("Project Summary"); + clickColumnsItem("Projected Funding Total"); + clickColumnsItem("Recommended Amount"); + clickColumnsItem("Red-Stop"); + clickColumnsItem("Regional District"); + clickColumnsItem("Registered Organization Name"); + clickColumnsItem("Registered Organization Number"); + + clickColumnsItem("Requested Amount"); + clickColumnsItem("Requested Amount"); + + clickColumnsItem("Risk Ranking"); + clickColumnsItem("Sector"); + clickColumnsItem("Signing Authority Business Phone"); + clickColumnsItem("Signing Authority Cell Phone"); + clickColumnsItem("Signing Authority Email"); + clickColumnsItem("Signing Authority Full Name"); + clickColumnsItem("Signing Authority Title"); + + clickColumnsItem("Status"); + clickColumnsItem("Status"); + + clickColumnsItem("Sub-Status"); + clickColumnsItem("Sub-Status"); + + clickColumnsItem("Submission #"); + clickColumnsItem("Submission #"); + + clickColumnsItem("Submission Date"); + clickColumnsItem("Submission Date"); + + clickColumnsItem("SubSector"); + + clickColumnsItem("Tags"); + clickColumnsItem("Tags"); + + clickColumnsItem("Total Paid Amount $"); + clickColumnsItem("Total Project Budget"); + clickColumnsItem("Total Score"); + clickColumnsItem("Unity Application Id"); + + // Close the menu and wait until the overlay is gone + cy.get("div.dt-button-background", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .click({ force: true }); + + cy.get("div.dt-button-background", { timeout: STANDARD_TIMEOUT }).should( + "not.exist", + ); + + // Assertions by horizontal scroll segments (human-style scan) + scrollX(0); + assertVisibleHeadersInclude([ + "Applicant Name", + "Category", + "Submission #", + "Submission Date", + "Status", + "Sub-Status", + "Community", + "Requested Amount", + "Approved Amount", + "Project Name", + "Applicant Id", + ]); + + scrollX(1500); + assertVisibleHeadersInclude([ + "Tags", + "Assignee", + "SubSector", + "Economic Region", + "Regional District", + "Registered Organization Number", + "Org Book Status", + ]); + + scrollX(3000); + assertVisibleHeadersInclude([ + "Project Start Date", + "Project End Date", + "Projected Funding Total", + "Total Paid Amount $", + "Project Electoral District", + "Applicant Electoral District", + ]); + + scrollX(4500); + assertVisibleHeadersInclude([ + "Forestry or Non-Forestry", + "Forestry Focus", + "Acquisition", + "City", + "Community Population", + "Likelihood of Funding", + "Total Score", + ]); + + scrollX(6000); + assertVisibleHeadersInclude([ + "Assessment Result", + "Recommended Amount", + "Due Date", + "Owner", + "Decision Date", + "Project Summary", + "Organization Type", + "Business Number", + ]); + + scrollX(7500); + assertVisibleHeadersInclude([ + "Due Diligence Status", + "Decline Rationale", + "Contact Full Name", + "Contact Title", + "Contact Email", + "Contact Business Phone", + "Contact Cell Phone", + ]); + + scrollX(9000); + assertVisibleHeadersInclude([ + "Signing Authority Full Name", + "Signing Authority Title", + "Signing Authority Email", + "Signing Authority Business Phone", + "Signing Authority Cell Phone", + "Place", + "Risk Ranking", + "Notes", + "Red-Stop", + "Indigenous", + "FYE Day", + "FYE Month", + "Payout", + "Unity Application Id", + ]); + }); + + it("Verify Logout", () => { + cy.logout(); + }); +}); \ No newline at end of file diff --git a/applications/Unity.AutoUI/cypress/e2e/basicEmail.cy.ts b/applications/Unity.AutoUI/cypress/e2e/basicEmail.cy.ts index 206f0c1bf..152081bae 100644 --- a/applications/Unity.AutoUI/cypress/e2e/basicEmail.cy.ts +++ b/applications/Unity.AutoUI/cypress/e2e/basicEmail.cy.ts @@ -1,229 +1,385 @@ -describe('Send an email', () => { - const TEST_EMAIL_TO = 'grantmanagementsupport@gov.bc.ca' - const TEST_EMAIL_CC = 'UnitySupport@gov.bc.ca' - const TEST_EMAIL_BCC = 'UNITYSUP@Victoria1.gov.bc.ca' - const TEMPLATE_NAME = 'Test Case 1' - const STANDARD_TIMEOUT = 20000 - - const now = new Date() - const timestamp = - now.getFullYear() + - '-' + - String(now.getMonth() + 1).padStart(2, '0') + - '-' + - String(now.getDate()).padStart(2, '0') + - ' ' + - String(now.getHours()).padStart(2, '0') + - ':' + - String(now.getMinutes()).padStart(2, '0') + - ':' + - String(now.getSeconds()).padStart(2, '0') - - const TEST_EMAIL_SUBJECT = `Smoke Test Email ${timestamp}` - - function switchToDefaultGrantsProgramIfAvailable() { - cy.get('body').then(($body) => { - // If we are already on GrantPrograms (or can navigate there), try. Otherwise skip quietly. - // Key point: never .should() an optional element. - const hasUserInitials = $body.find('.unity-user-initials').length > 0 - - if (!hasUserInitials) { - cy.log('Skipping tenant switch: no user initials menu found') - return +// cypress/e2e/basicEmail.cy.ts + +describe("Send an email", () => { + const TEST_EMAIL_TO = Cypress.env("TEST_EMAIL_TO") as string; + const TEST_EMAIL_CC = Cypress.env("TEST_EMAIL_CC") as string; + const TEST_EMAIL_BCC = Cypress.env("TEST_EMAIL_BCC") as string; + const TEMPLATE_NAME = "Test Case 1"; + const STANDARD_TIMEOUT = 20000; + + // Only suppress the noisy ResizeObserver error that Unity throws in TEST. + // Everything else should still fail the test. + Cypress.on("uncaught:exception", (err) => { + const msg = err && err.message ? err.message : ""; + if (msg.indexOf("ResizeObserver loop limit exceeded") >= 0) { + return false; + } + return true; + }); + + const now = new Date(); + const timestamp = + now.getFullYear() + + "-" + + String(now.getMonth() + 1).padStart(2, "0") + + "-" + + String(now.getDate()).padStart(2, "0") + + " " + + String(now.getHours()).padStart(2, "0") + + ":" + + String(now.getMinutes()).padStart(2, "0") + + ":" + + String(now.getSeconds()).padStart(2, "0"); + + const TEST_EMAIL_SUBJECT = `Smoke Test Email ${timestamp}`; + + function ensureLoggedInToGrantApplications() { + // Headless runs specs sequentially in the same browser process. + // Do not assume logged-out or logged-in. Detect UI state like chefsdata.cy.ts does. + cy.visit(Cypress.env("webapp.url")); + + cy.get("body", { timeout: STANDARD_TIMEOUT }).then(($body) => { + // Already authenticated + if ($body.find('button:contains("VIEW APPLICATIONS")').length > 0) { + cy.contains("VIEW APPLICATIONS", { timeout: STANDARD_TIMEOUT }).click({ + force: true, + }); + return; + } + + // Not authenticated + if ($body.find('button:contains("LOGIN")').length > 0) { + cy.contains("LOGIN", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .click({ force: true }); + + cy.get("body", { timeout: STANDARD_TIMEOUT }).then(($loginBody) => { + // IDIR chooser may or may not appear + if ($loginBody.find(':contains("IDIR")').length > 0) { + cy.contains("IDIR", { timeout: STANDARD_TIMEOUT }).click({ + force: true, + }); + } + + cy.get("body", { timeout: STANDARD_TIMEOUT }).then(($authBody) => { + // Only type creds if the login form is actually present + if ($authBody.find("#user").length > 0) { + cy.get("#user", { timeout: STANDARD_TIMEOUT }).type( + Cypress.env("test1username"), + ); + cy.get("#password", { timeout: STANDARD_TIMEOUT }).type( + Cypress.env("test1password"), + ); + cy.contains("Continue", { timeout: STANDARD_TIMEOUT }).click({ + force: true, + }); + } else { + cy.log("Already authenticated"); } - - cy.get('.unity-user-initials').click() - - cy.get('body').then(($body2) => { - const switchLink = $body2.find('#user-dropdown a.dropdown-item').filter((_, el) => { - return (el.textContent || '').trim() === 'Switch Grant Programs' - }) - - if (switchLink.length === 0) { - cy.log('Skipping tenant switch: "Switch Grant Programs" not present for this user/session') - // Close dropdown so it does not block clicks later - cy.get('body').click(0, 0) - return - } - - cy.wrap(switchLink.first()).click() - - cy.url({ timeout: STANDARD_TIMEOUT }).should('include', '/GrantPrograms') - - cy.get('#search-grant-programs', { timeout: STANDARD_TIMEOUT }) - .should('be.visible') - .clear() - .type('Default Grants Program') - - cy.get('#UserGrantProgramsTable', { timeout: STANDARD_TIMEOUT }) - .should('be.visible') - .within(() => { - cy.contains('tbody tr', 'Default Grants Program', { timeout: STANDARD_TIMEOUT }) - .should('exist') - .within(() => { - cy.contains('button', 'Select') - .should('be.enabled') - .click() - }) - }) - - cy.location('pathname', { timeout: STANDARD_TIMEOUT }).should((p) => { - expect(p.indexOf('/GrantApplications') >= 0 || p.indexOf('/auth/') >= 0).to.eq(true) - }) + }); + }); + + return; + } + + throw new Error("Unable to determine authentication state"); + }); + + cy.location("pathname", { timeout: 30000 }).should( + "include", + "/GrantApplications", + ); + } + + function switchToDefaultGrantsProgramIfAvailable() { + cy.get("body").then(($body) => { + const hasUserInitials = $body.find(".unity-user-initials").length > 0; + + if (!hasUserInitials) { + cy.log("Skipping tenant: no user initials menu found"); + return; + } + + cy.get(".unity-user-initials").click(); + + cy.get("body").then(($body2) => { + const switchLink = $body2 + .find("#user-dropdown a.dropdown-item") + .filter((_, el) => { + return (el.textContent || "").trim() === "Switch Grant Programs"; + }); + + if (switchLink.length === 0) { + cy.log( + 'Skipping tenant: "Switch Grant Programs" not present for this user/session', + ); + cy.get("body").click(0, 0); + return; + } + + cy.wrap(switchLink.first()).click(); + + cy.url({ timeout: STANDARD_TIMEOUT }).should( + "include", + "/GrantPrograms", + ); + + cy.get("#search-grant-programs", { timeout: STANDARD_TIMEOUT }) + .should("be.visible") + .clear() + .type("Default Grants Program"); + + cy.get("#UserGrantProgramsTable", { timeout: STANDARD_TIMEOUT }) + .should("be.visible") + .within(() => { + cy.contains("tbody tr", "Default Grants Program", { + timeout: STANDARD_TIMEOUT, }) - }) - } - - it('Login', () => { - cy.login() - }) - - it('Switch to Default Grants Program if available', () => { - switchToDefaultGrantsProgramIfAvailable() - }) - - it('Handle IDIR if required', () => { - cy.get('body').then(($body) => { - if ($body.find('#social-idir').length > 0) { - cy.get('#social-idir').should('be.visible').click() + .should("exist") + .within(() => { + cy.contains("button", "Select").should("be.enabled").click(); + }); + }); + + cy.location("pathname", { timeout: STANDARD_TIMEOUT }).should((p) => { + expect( + p.indexOf("/GrantApplications") >= 0 || p.indexOf("/auth/") >= 0, + ).to.eq(true); + }); + }); + }); + } + + function openSavedEmailFromHistoryBySubject(subject: string) { + cy.get("body", { timeout: STANDARD_TIMEOUT }).then(($body) => { + const historyTableById = $body.find("#EmailHistoryTable"); + if (historyTableById.length > 0) { + cy.get("#EmailHistoryTable", { timeout: STANDARD_TIMEOUT }) + .scrollIntoView() + .should("be.visible") + .within(() => { + cy.contains("td", subject, { timeout: STANDARD_TIMEOUT }) + .should("exist") + .click(); + }); + return; + } + + cy.contains("td", subject, { timeout: STANDARD_TIMEOUT }) + .should("exist") + .click(); + }); + } + + function confirmSendDialogIfPresent() { + cy.get("body", { timeout: STANDARD_TIMEOUT }).should(($b) => { + const hasBootstrapShownModal = $b.find(".modal.show").length > 0; + const hasSwal = $b.find(".swal2-container").length > 0; + const hasConfirmBtn = $b.find("#btn-confirm-send").length > 0; + expect(hasBootstrapShownModal || hasSwal || hasConfirmBtn).to.eq(true); + }); + + cy.get("body", { timeout: STANDARD_TIMEOUT }).then(($b) => { + const hasSwal = $b.find(".swal2-container").length > 0; + if (hasSwal) { + cy.get(".swal2-container", { timeout: STANDARD_TIMEOUT }).should( + "be.visible", + ); + cy.contains(".swal2-container", "Are you sure", { + timeout: STANDARD_TIMEOUT, + }).should("exist"); + + if ($b.find(".swal2-confirm").length > 0) { + cy.get(".swal2-confirm", { timeout: STANDARD_TIMEOUT }) + .should("be.visible") + .click(); + } else { + cy.contains(".swal2-container button", "Yes", { + timeout: STANDARD_TIMEOUT, + }).click(); + } + return; + } + + const hasBootstrapShownModal = $b.find(".modal.show").length > 0; + if (hasBootstrapShownModal) { + cy.get(".modal.show", { timeout: STANDARD_TIMEOUT }) + .should("be.visible") + .within(() => { + cy.contains("Are you sure you want to send this email?", { + timeout: STANDARD_TIMEOUT, + }).should("exist"); + + if (Cypress.$("#btn-confirm-send").length > 0) { + cy.get("#btn-confirm-send", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .should("be.visible") + .click(); + } else { + cy.contains("button", "Confirm", { + timeout: STANDARD_TIMEOUT, + }).click(); } - }) - - cy.location('pathname', { timeout: 30000 }).should('include', '/GrantApplications') - }) - - it('Open an application from the list', () => { - cy.url().should('include', '/GrantApplications') - - cy.get('#GrantApplicationsTable tbody a[href^="/GrantApplications/Details?ApplicationId="]', { timeout: STANDARD_TIMEOUT }) - .should('have.length.greaterThan', 0) - - cy.get('#GrantApplicationsTable tbody a[href^="/GrantApplications/Details?ApplicationId="]') - .first() - .click() - - cy.url().should('include', '/GrantApplications/Details') - }) - - it('Open Emails tab', () => { - cy.get('#emails-tab', { timeout: STANDARD_TIMEOUT }) - .should('exist') - .should('be.visible') - .click() - - cy.contains('Emails', { timeout: STANDARD_TIMEOUT }).should('exist') - cy.contains('Email History', { timeout: STANDARD_TIMEOUT }).should('exist') - }) - - it('Open New Email form', () => { - cy.get('#btn-new-email', { timeout: STANDARD_TIMEOUT }) - .should('exist') - .should('be.visible') - .click() - - cy.contains('Email To', { timeout: STANDARD_TIMEOUT }).should('exist') - }) - - it('Select Email Template', () => { - cy.intercept('GET', '/api/app/template/*/template-by-id').as('loadTemplate') - - cy.get('#template', { timeout: STANDARD_TIMEOUT }) - .should('exist') - .should('be.visible') - .select(TEMPLATE_NAME) - - cy.wait('@loadTemplate', { timeout: STANDARD_TIMEOUT }) - - cy.get('#template') - .find('option:selected') - .should('have.text', TEMPLATE_NAME) - }) - - it('Set Email To address', () => { - cy.get('#EmailTo', { timeout: STANDARD_TIMEOUT }) - .should('exist') - .should('be.visible') - .clear() - .type(TEST_EMAIL_TO) - - cy.get('#EmailTo').should('have.value', TEST_EMAIL_TO) - }) - - it('Set Email CC address', () => { - cy.get('#EmailCC', { timeout: STANDARD_TIMEOUT }) - .should('exist') - .should('be.visible') - .clear() - .type(TEST_EMAIL_CC) - - cy.get('#EmailCC').should('have.value', TEST_EMAIL_CC) - }) - - it('Set Email BCC address', () => { - cy.get('#EmailBCC', { timeout: STANDARD_TIMEOUT }) - .should('exist') - .should('be.visible') - .clear() - .type(TEST_EMAIL_BCC) - - cy.get('#EmailBCC').should('have.value', TEST_EMAIL_BCC) - }) - - it('Set Email Subject', () => { - cy.get('#EmailSubject', { timeout: STANDARD_TIMEOUT }) - .should('exist') - .should('be.visible') - .clear() - .type(TEST_EMAIL_SUBJECT) - - cy.get('#EmailSubject').should('have.value', TEST_EMAIL_SUBJECT) - }) - - it('Save the email', () => { - cy.get('#btn-save', { timeout: STANDARD_TIMEOUT }) - .should('exist') - .should('be.visible') - .click() - - cy.get('#btn-new-email', { timeout: STANDARD_TIMEOUT }).should('be.visible') - }) - - it('Select saved email from Email History', () => { - cy.contains('td.data-table-header', TEST_EMAIL_SUBJECT, { timeout: STANDARD_TIMEOUT }) - .should('exist') - .click() - - cy.get('#EmailTo', { timeout: STANDARD_TIMEOUT }).should('be.visible') - cy.get('#EmailCC').should('be.visible') - cy.get('#EmailBCC').should('be.visible') - cy.get('#EmailSubject').should('be.visible') - - cy.get('#btn-send').should('be.visible') - cy.get('#btn-save').should('be.visible') - }) - - it('Send the email', () => { - cy.get('#btn-send', { timeout: STANDARD_TIMEOUT }) - .should('exist') - .should('be.visible') - .click() - }) - - it('Confirm send email in modal', () => { - cy.get('#modal-content', { timeout: STANDARD_TIMEOUT }) - .should('exist') - .should('be.visible') - - cy.contains('Are you sure you want to send this email?', { timeout: STANDARD_TIMEOUT }) - .should('exist') - - cy.get('#btn-confirm-send', { timeout: STANDARD_TIMEOUT }) - .should('exist') - .should('be.visible') - .click() - }) - - it('Verify Logout', () => { - cy.logout() - }) -}) + }); + return; + } + + cy.get("#btn-confirm-send", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .click({ force: true }); + }); + } + + it("Login", () => { + ensureLoggedInToGrantApplications(); + }); + + it("Switch to Default Grants Program if available", () => { + switchToDefaultGrantsProgramIfAvailable(); + }); + + it("Handle IDIR if required", () => { + cy.get("body").then(($body) => { + if ($body.find("#social-idir").length > 0) { + cy.get("#social-idir").should("be.visible").click(); + } + }); + + cy.location("pathname", { timeout: 30000 }).should( + "include", + "/GrantApplications", + ); + }); + + it("Open an application from the list", () => { + cy.url().should("include", "/GrantApplications"); + + cy.get( + '#GrantApplicationsTable tbody a[href^="/GrantApplications/Details?ApplicationId="]', + { timeout: STANDARD_TIMEOUT }, + ).should("have.length.greaterThan", 0); + + cy.get( + '#GrantApplicationsTable tbody a[href^="/GrantApplications/Details?ApplicationId="]', + ) + .first() + .click(); + + cy.url().should("include", "/GrantApplications/Details"); + }); + + it("Open Emails tab", () => { + cy.get("#emails-tab", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .should("be.visible") + .click(); + + cy.contains("Emails", { timeout: STANDARD_TIMEOUT }).should("exist"); + cy.contains("Email History", { timeout: STANDARD_TIMEOUT }).should("exist"); + }); + + it("Open New Email form", () => { + cy.get("#btn-new-email", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .should("be.visible") + .click(); + + cy.contains("Email To", { timeout: STANDARD_TIMEOUT }).should("exist"); + }); + + it("Select Email Template", () => { + cy.intercept("GET", "/api/app/template/*/template-by-id").as( + "loadTemplate", + ); + + cy.get("#template", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .should("be.visible") + .select(TEMPLATE_NAME); + + cy.wait("@loadTemplate", { timeout: STANDARD_TIMEOUT }); + + cy.get("#template") + .find("option:selected") + .should("have.text", TEMPLATE_NAME); + }); + + it("Set Email To address", () => { + cy.get("#EmailTo", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .should("be.visible") + .clear() + .type(TEST_EMAIL_TO); + + cy.get("#EmailTo").should("have.value", TEST_EMAIL_TO); + }); + + it("Set Email CC address", () => { + cy.get("#EmailCC", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .should("be.visible") + .clear() + .type(TEST_EMAIL_CC); + + cy.get("#EmailCC").should("have.value", TEST_EMAIL_CC); + }); + + it("Set Email BCC address", () => { + cy.get("#EmailBCC", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .should("be.visible") + .clear() + .type(TEST_EMAIL_BCC); + + cy.get("#EmailBCC").should("have.value", TEST_EMAIL_BCC); + }); + + it("Set Email Subject", () => { + cy.get("#EmailSubject", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .should("be.visible") + .clear() + .type(TEST_EMAIL_SUBJECT); + + cy.get("#EmailSubject").should("have.value", TEST_EMAIL_SUBJECT); + }); + + it("Save the email", () => { + cy.get("#btn-save", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .should("be.visible") + .click(); + + cy.get("#btn-new-email", { timeout: STANDARD_TIMEOUT }).should( + "be.visible", + ); + }); + + it("Select saved email from Email History", () => { + openSavedEmailFromHistoryBySubject(TEST_EMAIL_SUBJECT); + + cy.get("#EmailTo", { timeout: STANDARD_TIMEOUT }).should("be.visible"); + cy.get("#EmailCC").should("be.visible"); + cy.get("#EmailBCC").should("be.visible"); + cy.get("#EmailSubject").should("be.visible"); + + cy.get("#btn-send", { timeout: STANDARD_TIMEOUT }).should("exist"); + cy.get("#btn-save", { timeout: STANDARD_TIMEOUT }).should("exist"); + }); + + it("Send the email", () => { + cy.get("#btn-send", { timeout: STANDARD_TIMEOUT }) + .should("exist") + .should("be.visible") + .should("not.be.disabled") + .click(); + }); + + it("Confirm send email in dialog", () => { + confirmSendDialogIfPresent(); + }); + + it("Verify Logout", () => { + cy.logout(); + }); +}); diff --git a/applications/Unity.AutoUI/cypress/e2e/chefsdata.cy.ts b/applications/Unity.AutoUI/cypress/e2e/chefsdata.cy.ts index 92baa0fb9..b9ec8948c 100644 --- a/applications/Unity.AutoUI/cypress/e2e/chefsdata.cy.ts +++ b/applications/Unity.AutoUI/cypress/e2e/chefsdata.cy.ts @@ -1,31 +1,39 @@ /// + +// cypress/e2e/chefsdata.cy.ts + describe('Unity Login and check data from CHEFS', () => { + const STANDARD_TIMEOUT = 20000 - it('Verify Login', () => { + // TEST renders the Submission tab inside an open shadow root (Form.io). + // Enabling this makes cy.get / cy.contains pierce shadow DOM consistently across envs. + before(() => { + Cypress.config('includeShadowDom', true) + }) + it('Verify Login', () => { // 1.) Always start from the base URL cy.visit(Cypress.env('webapp.url')) // 2.) Decide auth path based on visible UI - cy.get('body').then($body => { - + cy.get('body', { timeout: STANDARD_TIMEOUT }).then(($body) => { // Already authenticated if ($body.find('button:contains("VIEW APPLICATIONS")').length > 0) { - cy.contains('VIEW APPLICATIONS').click() + cy.contains('VIEW APPLICATIONS', { timeout: STANDARD_TIMEOUT }).click({ force: true }) return } // Not authenticated if ($body.find('button:contains("LOGIN")').length > 0) { - cy.contains('LOGIN').should('be.visible').click() - cy.contains('IDIR').should('be.visible').click() + cy.contains('LOGIN', { timeout: STANDARD_TIMEOUT }).should('exist').click({ force: true }) + cy.contains('IDIR', { timeout: STANDARD_TIMEOUT }).should('exist').click({ force: true }) - cy.get('body').then($loginBody => { + cy.get('body', { timeout: STANDARD_TIMEOUT }).then(($loginBody) => { // Perform IDIR login only if prompted - if ($loginBody.find('#user').length) { - cy.get('#user').type(Cypress.env('test1username')) - cy.get('#password').type(Cypress.env('test1password')) - cy.contains('Continue').should('exist').click() + if ($loginBody.find('#user').length > 0) { + cy.get('#user', { timeout: STANDARD_TIMEOUT }).type(Cypress.env('test1username')) + cy.get('#password', { timeout: STANDARD_TIMEOUT }).type(Cypress.env('test1password')) + cy.contains('Continue', { timeout: STANDARD_TIMEOUT }).should('exist').click({ force: true }) } else { cy.log('Already logged in') } @@ -39,220 +47,188 @@ describe('Unity Login and check data from CHEFS', () => { }) // 3.) Post-condition check - cy.url().should('include', '/GrantApplications') + cy.url({ timeout: STANDARD_TIMEOUT }).should('include', '/GrantApplications') }) - // 19.) Verify that the info panel populates with mapped data + // Verify that the details panel populates with mapped data it('Verify the UI is populated with valid data from CHEFS', () => { - - cy.getSubmissionDetail('confirmationID').then(id => { cy.log(`Confirmation ID: ${id}`); }); + cy.getSubmissionDetail('confirmationID').then((id) => { + cy.log(`Confirmation ID: ${id}`) + }) // Ensure the search field exists - cy.get('#search').should('exist') + cy.get('#search', { timeout: STANDARD_TIMEOUT }).should('exist') // Conditionally widen Submitted Date range if the control exists - cy.get('body').then(($body) => { + cy.get('body', { timeout: STANDARD_TIMEOUT }).then(($body) => { if ($body.find('input#submittedFromDate').length > 0) { - cy.get('input#submittedFromDate') - .should('be.visible') + cy.get('input#submittedFromDate', { timeout: STANDARD_TIMEOUT }) + .should('exist') .clear() .type('2022-01-01') } }) // Clear and focus search - cy.get('#search').clear() - cy.get('#search').click() + cy.get('#search', { timeout: STANDARD_TIMEOUT }).clear() + cy.get('#search', { timeout: STANDARD_TIMEOUT }).click({ force: true }) // Type confirmation ID - cy.getSubmissionDetail('confirmationID') - .then(id => cy.get('#search').type(id)) + cy.getSubmissionDetail('confirmationID').then((id) => { + cy.get('#search', { timeout: STANDARD_TIMEOUT }).type(id) + }) // Select matching row if table rendering exists - cy.getSubmissionDetail('confirmationID') - .then(id => { - cy.get('body').then(($body) => { - if ($body.find(`tr:contains("${id}")`).length > 0) { - cy.contains('tr', id) - .find('.checkbox-select') - .click() - } - }) + cy.getSubmissionDetail('confirmationID').then((id) => { + cy.get('body', { timeout: STANDARD_TIMEOUT }).then(($body) => { + if ($body.find(`tr:contains("${id}")`).length > 0) { + cy.contains('tr', id, { timeout: STANDARD_TIMEOUT }) + .find('.checkbox-select') + .click({ force: true }) + } }) + }) // Open the info panel if available - cy.get('body').then(($body) => { + cy.get('body', { timeout: STANDARD_TIMEOUT }).then(($body) => { if ($body.find('#applicationLink').length > 0) { - cy.get('#applicationLink').click() + cy.get('#applicationLink', { timeout: STANDARD_TIMEOUT }).click({ force: true }) } }) - // 19.) Verify that the info panel populates with mapped data - // Category: AutoUI - cy.get('label[for="Category"]').next('.display-input').should('include.text', 'AutoUI'); - // Organization Name: DOLPHIN ASPHALT - cy.get('label.display-input-label[for="OrganizationName"]').next('div.display-input').should('contain.text', 'DOLPHIN ASPHALT') - // Organization #: - cy.get('label.display-input-label[for="OrganizationNumber"]').next('div.display-input').should('contain.text', 'FM0162628') - // Economic Region: Kootenay - cy.get('label[for="EconomicRegion"]').next('.display-input').should('include.text', 'Kootenay') - // Regional District: East Kootenay - cy.get('label[for="RegionalDistrict"]').next('.display-input').should('include.text', 'East Kootenay') - // Community: East Kootenay B - cy.get('label[for="Community"]').next('.display-input').should('include.text', 'East Kootenay B') - // Requested Amount: $89,000.00 - cy.get('label[for="RequestedAmount"]').next('.display-input').should('include.text', '$89,000.00') - // Total Project Budget: $125,000.00 - cy.get('label[for="ProjectBudget"]').next('.display-input').should('include.text', '$125,000.00') - // Sector: Other services (except public administration) - cy.get('label[for="Sector"]').next('.display-input').should('include.text', 'Other services (except public administration)') - cy.get('#closeSummaryCanvas').click() - // 20.) Verify that the details panel populates with mapped data - cy.get('#externalLink').should('exist').click() //open the application - // Category: AutoUI - cy.get('label[for="Category"]').next('.display-input').should('include.text', 'AutoUI') - // Organization Name: DOLPHIN ASPHALT - cy.get('label[for="OrganizationName"]').next('.display-input').should('include.text', 'DOLPHIN ASPHALT') - // Organization #: - cy.get('label[for="OrganizationNumber"]').next('.display-input').should('include.text', 'FM0162628') - // Economic Region: Kootenay - cy.get('label[for="EconomicRegion"]').next('.display-input').should('include.text', 'Kootenay') - // Regional District: East Kootenay - cy.get('label[for="RegionalDistrict"]').next('.display-input').should('include.text', 'East Kootenay') - // Community: East Kootenay B - cy.get('label[for="Community"]').next('.display-input').should('include.text', 'East Kootenay B') - // Requested Amount: $89,000.00 - cy.get('label[for="RequestedAmount"]').next('.display-input').should('include.text', '$89,000.00') - // Total Project Budget: $125,000.00 - cy.get('label[for="ProjectBudget"]').next('.display-input').should('include.text', '$125,000.00') - // Sector: Other services (except public administration) - cy.get('label[for="Sector"]').next('.display-input').should('include.text', 'Other services (except public administration)') - // 21.) Verify that the Review & Assessment tab populates with mapped data - cy.get('#nav-review-and-assessment-tab').should('exist').click() // open the Review & Assessment tab - // Requested Amount: $89,000.00 - cy.get('#RequestedAmountInputAR').should('have.value', '89,000.00') - // Total Project Budget: $125,000.00 - cy.get('#TotalBudgetInputAR').should('have.value', '125,000.00') - // 22.) Verify that the Project Info tab populates with mapped data - cy.get('#nav-project-info-tab').should('exist').click() // open the Project Info tab - // Project Name - cy.get('#ProjectInfo_ProjectName').should('have.value', 'Hanbury Development Initiative - Phase 2') - // Project Start Date: 2026-01-05 - cy.get('#startDate').should('have.value', '2026-01-05') - // Project End Date: 2027-03-11 - cy.get('#ProjectInfo_ProjectEndDate').should('have.value', '2027-03-11') - // Requested Amount: $89,000.00 - cy.get('#RequestedAmountInputPI').should('have.value', '89,000.00') - // Total Project Budget: $125,000.00 - cy.get('#TotalBudgetInputPI').should('have.value', '125,000.00') - // Acquisition: No - cy.get('#ProjectInfo_Acquisition').should('have.value', 'NO') - // Forestry/Non-Forestry: Forestry - cy.get('#ProjectInfo_Forestry').should('have.value', 'FORESTRY') - // Forestry Focus: Secondary/Value-Added/Not Mass Timber (value="SECONDARY") - cy.get('#ProjectInfo_ForestryFocus').should('have.value', 'SECONDARY') - // Economic Region: Kootenay - cy.get('#economicRegions').should('have.value', 'Kootenay') - // Regional District: East Kootenay - cy.get('#regionalDistricts').should('have.value', 'East Kootenay') - // Community: East Kootenay B - cy.get('#communities').should('have.value', 'East Kootenay B') - // Community Population: 38 - cy.get('#ProjectInfo_CommunityPopulation').should('have.value', '38') - // Electoral District: Kootenay-Rockies - cy.get('#ProjectInfo_ElectoralDistrict').should('have.value', 'Kootenay-Rockies') - // Place: Hanbury - cy.get('#ProjectInfo_Place').should('have.value', 'Hanbury') - - // 23.) open the Applicant Info tab - it('23. Applicant Info tab shows the mapped data', () => { - // 1. open the pane - cy.contains('a.nav-link', 'Applicant Info').click() - - // 2. wait for the Applicant Info fieldset, then work inside it - cy.get('fieldset[name$="Applicant_Summary"]', { timeout: 10_000 }) - .should('be.visible') - .as('app') // alias for scoping - - // 3. simple value assertions - const plainInputs: [string, string][] = [ - ['#ApplicantSummary_OrgName', 'DOLPHIN ASPHALT'], - ['#ApplicantSummary_OrgNumber', 'FM0162628'], - ['#ApplicantSummary_ContactFullName', 'Jeff Gordon'], - ['#ApplicantSummary_ContactTitle', 'Sr. Analyst'], - ['#ApplicantSummary_ContactEmail', 'Jeff.Gordon@Dolphin.ca'], - ['#ApplicantSummary_ContactBusinessPhone', '(250) 621-3217'], - ['#ApplicantSummary_ContactCellPhone', '(887) 362-1459'], - ['#ApplicantSummary_PhysicalAddressStreet', '24th Avenue South'], - ['#ApplicantSummary_PhysicalAddressStreet2', 'Room 409'], - ['#ApplicantSummary_PhysicalAddressUnit', '19'], - ['#ApplicantSummary_PhysicalAddressCity', 'Cranbrook'], - ['#ApplicantSummary_PhysicalAddressProvince', 'British Columbia'], - ['#ApplicantSummary_PhysicalAddressPostalCode', 'V1C 3H8'], - ['#ApplicantInfo_MailingAddressStreet', '2567 Shaughnessy Street'], - ['#ApplicantInfo_MailingAddressStreet2', 'PO Box 905'], - ['#ApplicantInfo_MailingAddressUnit', '22'], - ['#ApplicantInfo_MailingAddressCity', 'Hanbury'], - ['#ApplicantInfo_MailingAddressProvince', 'British Columbia'], - ['#ApplicantInfo_MailingAddressPostalCode', 'V1C 4T6'], - ['#ApplicantInfo_SigningAuthorityFullName', 'Maximillion Cooper'], - ['#ApplicantInfo_SigningAuthorityTitle', 'Consultant'], - ['#ApplicantInfo_SigningAuthorityEmail', 'Maximillion.Cooper@Dolphin.ca'], - ['#ApplicantInfo_SigningAuthorityBusinessPhone', '(250) 841-2511'], - ['#ApplicantInfo_SigningAuthorityCellPhone', '(657) 456-5413'] - ] - - plainInputs.forEach(([selector, expected]) => { - cy.get('@app').find(selector).should('have.value', expected) - }) - - // 4. textarea requires .invoke('val') - cy.get('@app') - .find('#ApplicantSummary_SectorSubSectorIndustryDesc') - .invoke('val') - .should('equal', 'Stone Aggregate Recycling') - }) - - // 24.) Sector and Sub-sector lists - it('24. Sector and Sub-sector dropdowns behave', () => { - // open Applicant Info - cy.contains('a.nav-link', 'Applicant Info').click() - - // locate the Sector and Sub-sector + - + - + disabled="@item.DisableFields" + onchange='checkMaxValueRequest("@item.CorrelationId",this, @item.RemainingAmount)' /> - + - + - + + if (groupOpen && nextGroupKey != itemGroupKey) + { + @: + @: + groupOpen = false; + } + + currentGroupKey = itemGroupKey; }