From 07a05ff0beafc8f79e75f352091a0f7e53519bd7 Mon Sep 17 00:00:00 2001 From: Bangbay Siboliban Date: Fri, 6 Dec 2024 14:06:20 -0800 Subject: [PATCH 1/3] Add MCPAR dashboard Playwright tests (#11959) --- .env.tpl | 4 +- tests/playwright/pages/banner.spec.ts | 6 +- tests/playwright/pages/home.spec.ts | 9 ++- .../playwright/pages/mcpar/dashboard.spec.ts | 72 +++++++++++++++++ tests/playwright/pages/profile.spec.ts | 8 +- tests/playwright/utils/auth.setup.ts | 17 ++-- tests/playwright/utils/consts.ts | 7 +- tests/playwright/utils/fixtures/base.ts | 80 +++++++++++-------- tests/playwright/utils/index.ts | 3 +- .../utils/pageObjects/adminHome.page.ts | 32 ++++++++ tests/playwright/utils/pageObjects/index.ts | 13 +++ .../utils/pageObjects/mcpar/dashboard.page.ts | 67 ++++++++++++++++ .../pageObjects/mcpar/getStarted.page.ts | 17 ++++ .../utils/pageObjects/stateHome.page.ts | 11 +-- 14 files changed, 286 insertions(+), 60 deletions(-) create mode 100644 tests/playwright/pages/mcpar/dashboard.spec.ts create mode 100644 tests/playwright/utils/pageObjects/index.ts create mode 100644 tests/playwright/utils/pageObjects/mcpar/dashboard.page.ts create mode 100644 tests/playwright/utils/pageObjects/mcpar/getStarted.page.ts diff --git a/.env.tpl b/.env.tpl index b86c67798..106769d1d 100644 --- a/.env.tpl +++ b/.env.tpl @@ -46,5 +46,5 @@ TEST_ADMIN_USER_EMAIL=op://mdct_devs/mcr_secrets/CYPRESS_ADMIN_USER_EMAIL TEST_ADMIN_USER_PASSWORD=op://mdct_devs/mcr_secrets/CYPRESS_ADMIN_USER_PASSWORD # pragma: allowlist secret TEST_STATE_USER_EMAIL=op://mdct_devs/mcr_secrets/CYPRESS_STATE_USER_EMAIL TEST_STATE_USER_PASSWORD=op://mdct_devs/mcr_secrets/CYPRESS_STATE_USER_PASSWORD # pragma: allowlist secret -TEST_STATE=MN -TEST_STATE=Minnesota +TEST_STATE=DC +TEST_STATE_NAME="District of Columbia" diff --git a/tests/playwright/pages/banner.spec.ts b/tests/playwright/pages/banner.spec.ts index 92bfff922..0ae8d6368 100644 --- a/tests/playwright/pages/banner.spec.ts +++ b/tests/playwright/pages/banner.spec.ts @@ -1,5 +1,5 @@ -import { test, expect } from "../utils/fixtures/base"; -import BannerPage from "../utils/pageObjects/banner.page"; +import { expect, test } from "../utils/fixtures/base"; +import { BannerPage, stateUserAuth } from "../utils"; test.describe("admin user banner page", () => { test("Should see the correct banner page as an admin user", async ({ @@ -34,7 +34,7 @@ test.describe("admin user banner page", () => { profilePage, }) => { const userContext = await browser.newContext({ - storageState: ".auth/user.json", + storageState: stateUserAuth, }); await profilePage.goto(); const newBannerPage = new BannerPage(await userContext.newPage()); diff --git a/tests/playwright/pages/home.spec.ts b/tests/playwright/pages/home.spec.ts index 435dde011..381ee93cb 100644 --- a/tests/playwright/pages/home.spec.ts +++ b/tests/playwright/pages/home.spec.ts @@ -1,5 +1,5 @@ -import { test, expect } from "../utils/fixtures/base"; -import BasePage from "../utils/pageObjects/base.page"; +import { expect, test } from "../utils/fixtures/base"; +import { BasePage } from "../utils"; test.describe("state user home page", () => { test("Should see the correct home page as a state user", async ({ @@ -7,8 +7,9 @@ test.describe("state user home page", () => { }) => { await stateHomePage.goto(); await stateHomePage.isReady(); - await expect(stateHomePage.wpButton).toBeVisible(); - await expect(stateHomePage.sarButton).toBeVisible(); + await expect(stateHomePage.mcparButton).toBeVisible(); + await expect(stateHomePage.mlrButton).toBeVisible(); + await expect(stateHomePage.naaarButton).toBeVisible(); }); test("Is accessible on all device types for state user", async ({ diff --git a/tests/playwright/pages/mcpar/dashboard.spec.ts b/tests/playwright/pages/mcpar/dashboard.spec.ts new file mode 100644 index 000000000..59448d1d0 --- /dev/null +++ b/tests/playwright/pages/mcpar/dashboard.spec.ts @@ -0,0 +1,72 @@ +import { expect, test } from "../../utils/fixtures/base"; +import { stateName } from "../../utils"; + +let currentDate = ""; +let programName = ""; +let updatedProgramName = ""; + +test.beforeAll(async () => { + currentDate = new Date().toISOString(); + programName = `automated test - ${currentDate}`; + updatedProgramName = `Updated: ${programName}`; +}); + +test.describe( + "MCPAR Dashboard Page - Program Creation/Editing/Archiving", + () => { + test("State users can create and edit reports", async ({ + mcparDashboardPage, + mcparGetStartedPage, + stateHomePage, + }) => { + await stateHomePage.goto(); + await stateHomePage.isReady(); + + await stateHomePage.mcparButton.isVisible(); + await stateHomePage.mcparButton.click(); + await stateHomePage.redirectPage("/mcpar/get-started"); + + await mcparGetStartedPage.mcparButton.isVisible(); + await mcparGetStartedPage.mcparButton.click(); + await mcparGetStartedPage.redirectPage("/mcpar"); + + const originalRow = mcparDashboardPage.table.getByRole("row", { + name: programName, + }); + const updatedRow = mcparDashboardPage.table.getByRole("row", { + name: updatedProgramName, + }); + + // Create MCPAR + await mcparDashboardPage.create(programName); + await expect(originalRow).toBeVisible(); + await expect(updatedRow).toBeHidden(); + + // Update MCPAR + await mcparDashboardPage.update(programName, updatedProgramName); + await expect(originalRow).toBeHidden(); + await expect(updatedRow).toBeVisible(); + }); + + test("Admin users can archive/unarchive reports", async ({ + adminHomePage, + }) => { + await adminHomePage.archiveMCPAR(stateName, updatedProgramName); + await adminHomePage.unarchiveMCPAR(stateName, updatedProgramName); + }); + + test("State users can't see archived programs", async ({ + adminHomePage, + mcparDashboardPage, + }) => { + await adminHomePage.archiveMCPAR(stateName, updatedProgramName); + + await mcparDashboardPage.goto(); + await mcparDashboardPage.isReady(); + await mcparDashboardPage.table.isVisible(); + await expect( + mcparDashboardPage.table.getByRole("row", { name: updatedProgramName }) + ).toBeHidden(); + }); + } +); diff --git a/tests/playwright/pages/profile.spec.ts b/tests/playwright/pages/profile.spec.ts index ca53d2c40..db62155e2 100644 --- a/tests/playwright/pages/profile.spec.ts +++ b/tests/playwright/pages/profile.spec.ts @@ -1,6 +1,6 @@ -import { test, expect } from "../utils/fixtures/base"; +import { expect, test } from "../utils/fixtures/base"; import { BrowserContext, Page } from "@playwright/test"; -import ProfilePage from "../utils/pageObjects/profile.page"; +import { adminUserAuth, ProfilePage, stateUserAuth } from "../utils"; let adminPage: Page; let userPage: Page; @@ -9,12 +9,12 @@ let userContext: BrowserContext; test.beforeAll(async ({ browser }) => { adminContext = await browser.newContext({ - storageState: ".auth/admin.json", + storageState: adminUserAuth, }); adminPage = await adminContext.newPage(); userContext = await browser.newContext({ - storageState: ".auth/user.json", + storageState: stateUserAuth, }); userPage = await userContext.newPage(); }); diff --git a/tests/playwright/utils/auth.setup.ts b/tests/playwright/utils/auth.setup.ts index 8dcf84365..94e389a53 100644 --- a/tests/playwright/utils/auth.setup.ts +++ b/tests/playwright/utils/auth.setup.ts @@ -1,8 +1,13 @@ import { test as setup } from "@playwright/test"; -import { adminPassword, adminUser, statePassword, stateUser } from "./consts"; - -const adminFile = ".auth/admin.json"; +import { + adminPassword, + adminUser, + adminUserAuth, + statePassword, + stateUser, + stateUserAuth, +} from "./consts"; setup("authenticate as admin", async ({ page }) => { await page.goto("/"); @@ -19,11 +24,9 @@ setup("authenticate as admin", async ({ page }) => { }) .isVisible(); await page.waitForTimeout(1000); - await page.context().storageState({ path: adminFile }); + await page.context().storageState({ path: adminUserAuth }); }); -const userFile = ".auth/user.json"; - setup("authenticate as user", async ({ page }) => { await page.goto("/"); const emailInput = page.getByRole("textbox", { name: "email" }); @@ -39,5 +42,5 @@ setup("authenticate as user", async ({ page }) => { }) .isVisible(); await page.waitForTimeout(1000); - await page.context().storageState({ path: userFile }); + await page.context().storageState({ path: stateUserAuth }); }); diff --git a/tests/playwright/utils/consts.ts b/tests/playwright/utils/consts.ts index 367ceeb8c..11cfdfa18 100644 --- a/tests/playwright/utils/consts.ts +++ b/tests/playwright/utils/consts.ts @@ -3,7 +3,10 @@ export const adminPassword = process.env.TEST_ADMIN_USER_PASSWORD!; // pragma: a export const stateUser = process.env.TEST_STATE_USER_EMAIL!; export const statePassword = process.env.TEST_STATE_USER_PASSWORD!; // pragma: allowlist secret -export const stateAbbreviation = process.env.TEST_STATE || "MN"; -export const stateName = process.env.TEST_STATE_NAME || "Minnesota"; +export const stateAbbreviation = process.env.TEST_STATE || "DC"; +export const stateName = process.env.TEST_STATE_NAME || "District of Columbia"; export const currentYear: number = new Date().getFullYear(); + +export const stateUserAuth: string = ".auth/user.json"; +export const adminUserAuth: string = ".auth/admin.json"; diff --git a/tests/playwright/utils/fixtures/base.ts b/tests/playwright/utils/fixtures/base.ts index 88d6a39f7..2bfd969b6 100644 --- a/tests/playwright/utils/fixtures/base.ts +++ b/tests/playwright/utils/fixtures/base.ts @@ -1,48 +1,64 @@ -import { mergeTests, test as base } from "@playwright/test"; -import StateHomePage from "../pageObjects/stateHome.page"; -import AdminHomePage from "../pageObjects/adminHome.page"; -import BannerPage from "../pageObjects/banner.page"; -import ProfilePage from "../pageObjects/profile.page"; +import { Browser, mergeTests, test as base } from "@playwright/test"; +import { + AdminHomePage, + adminUserAuth, + BannerPage, + MCPARDashboardPage, + MCPARGetStartedPage, + ProfilePage, + StateHomePage, + stateUserAuth, +} from "../../utils"; type CustomFixtures = { - stateHomePage: StateHomePage; adminHomePage: AdminHomePage; bannerPage: BannerPage; profilePage: ProfilePage; + mcparGetStartedPage: MCPARGetStartedPage; + mcparDashboardPage: MCPARDashboardPage; + stateHomePage: StateHomePage; }; +async function addPageObject( + PageObject: any, + browser: Browser, + use: any, + storageState: string +) { + const context = await browser.newContext({ storageState }); + const page = new PageObject(await context.newPage()); + // Init page + await page.goto(); + await use(page); + await context.close(); +} + +async function adminPage(PageObject: any, browser: Browser, use: any) { + await addPageObject(PageObject, browser, use, adminUserAuth); +} + +async function statePage(PageObject: any, browser: Browser, use: any) { + await addPageObject(PageObject, browser, use, stateUserAuth); +} + export const baseTest = base.extend({ - stateHomePage: async ({ browser }, use) => { - const context = await browser.newContext({ - storageState: ".auth/user.json", - }); - const stateHomePage = new StateHomePage(await context.newPage()); - await use(stateHomePage); - await context.close(); - }, adminHomePage: async ({ browser }, use) => { - const context = await browser.newContext({ - storageState: ".auth/admin.json", - }); - const adminHomePage = new AdminHomePage(await context.newPage()); - await use(adminHomePage); - await context.close(); + await adminPage(AdminHomePage, browser, use); }, bannerPage: async ({ browser }, use) => { - const context = await browser.newContext({ - storageState: ".auth/admin.json", - }); - const bannerPage = new BannerPage(await context.newPage()); - await use(bannerPage); - await context.close(); + await adminPage(BannerPage, browser, use); + }, + mcparDashboardPage: async ({ browser }, use) => { + await statePage(MCPARDashboardPage, browser, use); + }, + mcparGetStartedPage: async ({ browser }, use) => { + await statePage(MCPARGetStartedPage, browser, use); }, profilePage: async ({ browser }, use) => { - const context = await browser.newContext({ - storageState: ".auth/user.json", - }); - const profilePage = new ProfilePage(await context.newPage()); - await use(profilePage); - await context.close(); + await statePage(ProfilePage, browser, use); + }, + stateHomePage: async ({ browser }, use) => { + await statePage(StateHomePage, browser, use); }, }); diff --git a/tests/playwright/utils/index.ts b/tests/playwright/utils/index.ts index 1b4506130..b42e6c82f 100644 --- a/tests/playwright/utils/index.ts +++ b/tests/playwright/utils/index.ts @@ -1,3 +1,4 @@ +export * from "./a11y"; export * from "./consts"; export * from "./login"; -export * from "./a11y"; +export * from "./pageObjects"; diff --git a/tests/playwright/utils/pageObjects/adminHome.page.ts b/tests/playwright/utils/pageObjects/adminHome.page.ts index 92678722e..da6d9e473 100644 --- a/tests/playwright/utils/pageObjects/adminHome.page.ts +++ b/tests/playwright/utils/pageObjects/adminHome.page.ts @@ -7,6 +7,7 @@ export default class AdminHomePage extends BasePage { readonly page: Page; readonly title: Locator; readonly dropdown: Locator; + readonly table: Locator; constructor(page: Page) { super(page); @@ -17,6 +18,7 @@ export default class AdminHomePage extends BasePage { this.dropdown = page.getByRole("combobox", { name: "List of states, including District of Columbia and Puerto Rico", }); + this.table = page.getByRole("table"); } public async selectMCPAR(state: string) { @@ -55,5 +57,35 @@ export default class AdminHomePage extends BasePage { name: "Go to Report Dashboard", }) .click(); + await this.page.waitForResponse((response) => response.status() == 200); + } + + public async getRowMCPAR(stateName: string, programName: string) { + await this.goto(); + await this.isReady(); + await this.selectMCPAR(stateName); + await this.table.isVisible(); + + return this.table.getByRole("row", { name: programName }); + } + + public async archiveMCPAR(stateName: string, programName: string) { + const row = await this.getRowMCPAR(stateName, programName); + const archiveButton = row.getByRole("button", { name: "Archive" }); + + await archiveButton.click(); + await this.page.waitForResponse((response) => response.status() == 200); + await archiveButton.isHidden(); + await row.getByRole("button", { name: "Unarchive" }).isVisible(); + } + + public async unarchiveMCPAR(stateName: string, programName: string) { + const row = await this.getRowMCPAR(stateName, programName); + const unarchiveButton = row.getByRole("button", { name: "Unarchive" }); + + await unarchiveButton.click(); + await this.page.waitForResponse((response) => response.status() == 200); + await row.getByRole("button", { name: "Archive" }).isVisible(); + await unarchiveButton.isHidden(); } } diff --git a/tests/playwright/utils/pageObjects/index.ts b/tests/playwright/utils/pageObjects/index.ts new file mode 100644 index 000000000..8b06e1a0a --- /dev/null +++ b/tests/playwright/utils/pageObjects/index.ts @@ -0,0 +1,13 @@ +export { default as BasePage } from "./base.page"; +export { default as BannerPage } from "./banner.page"; +export { default as ProfilePage } from "./profile.page"; + +// Admin User +export { default as AdminHomePage } from "./adminHome.page"; + +// State User +export { default as StateHomePage } from "./stateHome.page"; + +// MCPAR +export { default as MCPARDashboardPage } from "./mcpar/dashboard.page"; +export { default as MCPARGetStartedPage } from "./mcpar/getStarted.page"; diff --git a/tests/playwright/utils/pageObjects/mcpar/dashboard.page.ts b/tests/playwright/utils/pageObjects/mcpar/dashboard.page.ts new file mode 100644 index 000000000..df7050732 --- /dev/null +++ b/tests/playwright/utils/pageObjects/mcpar/dashboard.page.ts @@ -0,0 +1,67 @@ +import { Locator, Page } from "@playwright/test"; +import BasePage from "../base.page"; + +export default class MCPARDashboardPage extends BasePage { + public path = "/mcpar"; + + readonly page: Page; + readonly addCopyButton: Locator; + readonly table: Locator; + readonly modal: Locator; + readonly programNameInput: Locator; + readonly saveButton: Locator; + + constructor(page: Page) { + super(page); + this.page = page; + this.addCopyButton = page.getByRole("button", { + name: "Add / copy a MCPAR", + }); + this.table = page.getByRole("table"); + this.modal = page.getByRole("dialog"); + this.programNameInput = this.modal.getByLabel( + "Program name (for new MCPAR)" + ); + this.saveButton = this.modal.getByRole("button", { + name: "Save", + }); + } + + public async create(programName: string) { + await this.addCopyButton.isVisible(); + await this.addCopyButton.click(); + + await this.modal.isVisible(); + await this.modal + .getByRole("heading", { name: "Add / Copy a MCPAR" }) + .isVisible(); + await this.programNameInput.fill(programName); + await this.modal + .getByLabel("A.5a Reporting period (i.e. contract period) start date") + .fill("10/10/2024"); + await this.modal + .getByLabel("A.5b Reporting period (i.e. contract period) end date") + .fill("12/10/2024"); + await this.modal.getByLabel("Exclusion of CHIP from MCPAR").click(); + await this.modal.getByLabel("No").click(); + await this.saveButton.click(); + + await this.modal.isHidden(); + await this.table.isVisible(); + } + + public async update(programName: string, updatedProgramName: string) { + const row = this.page.getByRole("row", { name: programName }); + const editProgramButton = row.getByRole("button").first(); + await editProgramButton.isVisible(); + await editProgramButton.click(); + + await this.modal.isVisible(); + await this.modal.getByRole("heading", { name: "Edit Program" }).isVisible(); + await this.programNameInput.fill(updatedProgramName); + await this.saveButton.click(); + + await this.modal.isHidden(); + await this.table.isVisible(); + } +} diff --git a/tests/playwright/utils/pageObjects/mcpar/getStarted.page.ts b/tests/playwright/utils/pageObjects/mcpar/getStarted.page.ts new file mode 100644 index 000000000..3a73ba2f8 --- /dev/null +++ b/tests/playwright/utils/pageObjects/mcpar/getStarted.page.ts @@ -0,0 +1,17 @@ +import { Locator, Page } from "@playwright/test"; +import BasePage from "../base.page"; + +export default class MCPARGetStartedPage extends BasePage { + public path = "/mcpar/get-started"; + + readonly page: Page; + readonly mcparButton: Locator; + + constructor(page: Page) { + super(page); + this.page = page; + this.mcparButton = page.getByRole("button", { + name: "Enter MCPAR online", + }); + } +} diff --git a/tests/playwright/utils/pageObjects/stateHome.page.ts b/tests/playwright/utils/pageObjects/stateHome.page.ts index f452846d0..d39e404cd 100644 --- a/tests/playwright/utils/pageObjects/stateHome.page.ts +++ b/tests/playwright/utils/pageObjects/stateHome.page.ts @@ -6,8 +6,9 @@ export default class StateHomePage extends BasePage { readonly page: Page; readonly title: Locator; - readonly wpButton: Locator; - readonly sarButton: Locator; + readonly mcparButton: Locator; + readonly mlrButton: Locator; + readonly naaarButton: Locator; constructor(page: Page) { super(page); @@ -15,10 +16,10 @@ export default class StateHomePage extends BasePage { this.title = page.getByRole("heading", { name: "Managed Care Reporting Portal", }); - this.wpButton = page.getByRole("button", { + this.mcparButton = page.getByRole("button", { name: "Enter MCPAR online", }); - this.sarButton = page.getByRole("button", { name: "Enter MLR online" }); - this.sarButton = page.getByRole("button", { name: "Enter NAAAR online" }); + this.mlrButton = page.getByRole("button", { name: "Enter MLR online" }); + this.naaarButton = page.getByRole("button", { name: "Enter NAAAR online" }); } } From 96c91b49deb24e9e1ba8a1afde5e327c712d94d3 Mon Sep 17 00:00:00 2001 From: Bangbay Siboliban Date: Mon, 9 Dec 2024 11:15:28 -0500 Subject: [PATCH 2/3] Use intercept to check for Launch Darkly flag (#11962) --- services/app-api/forms/mcpar.json | 3 +++ tests/cypress/e2e/mcpar/form.cy.js | 31 +++++++++++++++--------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/services/app-api/forms/mcpar.json b/services/app-api/forms/mcpar.json index 579ef516a..ef3be7157 100644 --- a/services/app-api/forms/mcpar.json +++ b/services/app-api/forms/mcpar.json @@ -637,6 +637,7 @@ }, { "name": "XIII: Prior Authorization", + "flag": "novMcparRelease", "path": "/mcpar/state-level-indicators/prior-authorization", "pageType": "standard", "verbiage": { @@ -4267,6 +4268,7 @@ }, { "name": "XIII: Prior Authorization", + "flag": "novMcparRelease", "path": "/mcpar/plan-level-indicators/prior-authorization", "pageType": "drawer", "entityType": "plans", @@ -4488,6 +4490,7 @@ }, { "name": "XIV: Patient Access API Usage", + "flag": "novMcparRelease", "path": "/mcpar/plan-level-indicators/patient-access-api", "pageType": "drawer", "entityType": "plans", diff --git a/tests/cypress/e2e/mcpar/form.cy.js b/tests/cypress/e2e/mcpar/form.cy.js index 4732b0c69..e27987c3c 100644 --- a/tests/cypress/e2e/mcpar/form.cy.js +++ b/tests/cypress/e2e/mcpar/form.cy.js @@ -128,21 +128,7 @@ const traverseRoutes = (routes) => { }); }; -const traverseRoute = (route) => { - /* - * TODO: account for flag status - * - * if ( - * [ - * "/mcpar/plan-level-indicators/patient-access-api", - * "/mcpar/plan-level-indicators/prior-authorization", - * "/mcpar/state-level-indicators/prior-authorization", - * ].includes(route.path) - * ) { - * return; - * } - */ - +const continueTraversing = (route) => { //only perform checks on route if it contains some time of form fill if (route.form || route.modalForm || route.drawerForm) { //validate we are on the URL we expect to be @@ -166,6 +152,21 @@ const traverseRoute = (route) => { if (route.children) traverseRoutes(route.children); }; +const traverseRoute = (route) => { + if (route.flag) { + cy.intercept(/launchdarkly/).as("ld"); + cy.wait("@ld").then(({ request }) => { + const flags = request.body[0].features; + if (!flags[route.flag]) { + return; + } + continueTraversing(route); + }); + } else { + continueTraversing(route); + } +}; + const completeDrawerForm = (drawerForm) => { if (drawerForm) { //enter the drawer, then fill out the form and save it From e484640051fdfebccb99ef57f430e8045c28ad95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karla=20Valc=C3=A1rcel=20Mart=C3=ADnez?= <99458559+karla-vm@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:56:48 -0500 Subject: [PATCH 3/3] MCPAR PDF Feedback (Empty State) (#11960) --- services/app-api/forms/mcpar.json | 2 +- .../export/ExportedReportFieldTable.test.tsx | 37 ++++++++++++++++ .../export/ExportedReportFieldTable.tsx | 43 +++++++++++++------ .../export/ExportedSectionHeading.tsx | 8 ++++ services/ui-src/src/utils/other/export.tsx | 19 +++----- 5 files changed, 80 insertions(+), 29 deletions(-) diff --git a/services/app-api/forms/mcpar.json b/services/app-api/forms/mcpar.json index ef3be7157..5fa07877f 100644 --- a/services/app-api/forms/mcpar.json +++ b/services/app-api/forms/mcpar.json @@ -202,7 +202,7 @@ }, { "type": "html", - "content": "See Glossary in Excel Workbook for the definition of BSS entities." + "content": ". See Glossary in Excel Workbook for the definition of BSS entities." } ] }, diff --git a/services/ui-src/src/components/export/ExportedReportFieldTable.test.tsx b/services/ui-src/src/components/export/ExportedReportFieldTable.test.tsx index 02feb40a5..02a715e57 100644 --- a/services/ui-src/src/components/export/ExportedReportFieldTable.test.tsx +++ b/services/ui-src/src/components/export/ExportedReportFieldTable.test.tsx @@ -9,6 +9,8 @@ import { mockStandardReportPageJson, mockMlrReportStore, mockMcparReportStore, + mockVerbiageIntro, + mockDrawerForm, } from "utils/testing/setupJest"; import { useStore } from "utils"; // components @@ -73,6 +75,18 @@ const mockDrawerPageJson = { ...mockDrawerReportPageJson, drawerForm: { id: "drawer", fields: reportJsonFields }, }; +const mockMissingPlansPageJson = { + name: "mock-route-2a", + path: "/mcpar/plan-level-indicators/ilos", + pageType: "drawer", + entityType: "plans", + verbiage: { + intro: mockVerbiageIntro, + dashboardTitle: "Mock dashboard title", + drawerTitle: "Mock drawer title", + }, + drawerForm: mockDrawerForm, +}; const mockEmptyPageJson = { ...mockStandardReportPageJson, form: { @@ -115,11 +129,19 @@ const hintJson = { const exportedStandardTableComponent = ( ); + const exportedDrawerTableComponent = ( ); + +const exportedMissingEntitiesComponent = ( + +); + const emptyTableComponent = ( ); @@ -141,6 +163,21 @@ describe("ExportedReportFieldRow", () => { expect(row).toBeVisible(); }); + test("handles drawer pages with missing plans", async () => { + const missingEntitiesStore = { + ...mockMcparReportStore, + report: { + fieldData: {}, + }, + }; + mockedUseStore.mockReturnValue({ + ...missingEntitiesStore, + }); + render(exportedMissingEntitiesComponent); + const row = screen.getByTestId("missingEntityMessage"); + expect(row).toBeVisible(); + }); + test("handles a table with no form fields", async () => { render(emptyTableComponent); const row = screen.getByTestId("exportTable"); diff --git a/services/ui-src/src/components/export/ExportedReportFieldTable.tsx b/services/ui-src/src/components/export/ExportedReportFieldTable.tsx index 53fe01093..2269bb0ff 100644 --- a/services/ui-src/src/components/export/ExportedReportFieldTable.tsx +++ b/services/ui-src/src/components/export/ExportedReportFieldTable.tsx @@ -13,6 +13,7 @@ import { FormLayoutElement, isFieldElement, ReportType, + entityTypes, } from "types"; // verbiage import verbiage from "verbiage/pages/mcpar/mcpar-export"; @@ -45,24 +46,39 @@ export const ExportedReportFieldTable = ({ section }: Props) => { const reportType = report?.reportType as ReportType; const hideHintText = reportType === ReportType.MLR; - // handle ILOS rendering logic - const renderIlosVerbiage = () => { - const hasIlos = report?.fieldData["ilos"]?.length; - return section.path === "/mcpar/plan-level-indicators/ilos" && !hasIlos; - }; - const hasPlans = report?.fieldData["plans"]?.length; + const hasIlos = report?.fieldData["ilos"]?.length; + const hasBss = report?.fieldData["bssEntities"]?.length; + + // handle missing plans / ilos rendering logic + const renderMissingEntityVerbiage = () => { + const { path, verbiage: v } = section as DrawerReportPageShape; + + // verbiage for ILOS + if (path === "/mcpar/plan-level-indicators/ilos" && !hasIlos) { + return !hasPlans ? v.missingPlansAndIlosMessage : v.missingIlosMessage; + } - const missingVerbiage = !hasPlans - ? (section as DrawerReportPageShape).verbiage.missingPlansAndIlosMessage - : (section as DrawerReportPageShape).verbiage.missingIlosMessage; + // verbiage for missing plans + return !hasPlans ? v.missingEntityMessage : undefined; + }; + + const missingPlansOrIlos = !(hasIlos || hasPlans); return ( - // if there are no ILOS added, render the appropriate verbiage + // if there are no plans added, render the appropriate verbiage - {renderIlosVerbiage() ? ( - - {parseCustomHtml(missingVerbiage ?? "")} + {entityType === entityTypes[0] && missingPlansOrIlos ? ( + + {parseCustomHtml(renderMissingEntityVerbiage() || "")} + + ) : entityType === entityTypes[1] && !hasBss ? ( + // if there are no BSS entities added, render the appropriate verbiage + + {parseCustomHtml( + (section as DrawerReportPageShape).verbiage.missingEntityMessage || + "" + )} ) : ( {missingEntryVerbiage}; - // need to explicitly make this else if conditional so - } - - if ( - !hasResponse && - isChoiceListField && - formField.id !== "plan_ilosOfferedByPlan" - ) { + if (!hasResponse) { + const isIlos = formField.id === "plan_ilosOfferedByPlan"; return ( - {`${verbiage.missingEntry.noResponse}, optional`} + !isIlos && {missingEntryVerbiage} ); + // need to explicitly make this else if conditional so } - // chandle choice list fields (checkbox, radio) + // handle choice list fields (checkbox, radio) if (isChoiceListField) { return renderChoiceListFieldResponse( formField,