diff --git a/frontend-react/e2e/pages/authenticated/admin/receiver-status.ts b/frontend-react/e2e/pages/authenticated/admin/receiver-status.ts index a937092350c..2f0673d9892 100644 --- a/frontend-react/e2e/pages/authenticated/admin/receiver-status.ts +++ b/frontend-react/e2e/pages/authenticated/admin/receiver-status.ts @@ -1,11 +1,14 @@ import { expect, Locator } from "@playwright/test"; import { endOfDay, format, startOfDay, subDays } from "date-fns"; +import type { RSOrganizationSettings } from "../../../../src/config/endpoints/settings"; import { RSReceiverStatus } from "../../../../src/hooks/api/UseReceiversConnectionStatus/UseReceiversConnectionStatus"; import { createStatusTimePeriodData, SUCCESS_RATE_CLASSNAME_MAP, + SuccessRate, } from "../../../../src/pages/admin/receiver-dashboard/utils"; -import { DatePair, dateShortFormat } from "../../../../src/utils/DateTimeUtils"; +import { DatePair, dateShortFormat, durationFormatShort } from "../../../../src/utils/DateTimeUtils"; +import { formatDate } from "../../../../src/utils/misc"; import { createMockGetReceiverStatus } from "../../../mocks/receiverStatus"; import { BasePage, BasePageTestArgs, type ResponseHandlerEntry, type RouteHandlerFulfillEntry } from "../../BasePage"; @@ -310,8 +313,7 @@ export class AdminReceiverStatusPage extends BasePage { if (!isRequestAwaitedBool) return undefined as void; const req = await p; - const reqUrl = req ? new URL(req.url()) : undefined; - return reqUrl; + return req ? new URL(req.url()) : undefined; } async updateFilterDateRange(start: Date, end: Date, inputMethod: "textbox" | "calendar" = "textbox") { @@ -400,7 +402,7 @@ export class AdminReceiverStatusPage extends BasePage { return a[0] !== b[0] && a[1] !== b[1]; } - async testReceiverStatusDisplay() { + async testReceiverStatusDisplay(isSmoke = false) { const [startDate, endDate] = this.filterFormInputs.dateRange.value; const statusRows = this.receiverStatusRowsLocator; await expect(statusRows).toHaveCount(new Set(this.receiverStatus?.map((r) => r.receiverId)).size); @@ -444,6 +446,192 @@ export class AdminReceiverStatusPage extends BasePage { await expect(sliceEle).toHaveClass(expectedClass); } } + + if (isSmoke && i === 0) { + break; + } + } + + return true; + } + + async testReceiverName() { + const { organizationName, receiverName, successRate } = + this.timePeriodData[1]; + + const receiversStatusRows = this.receiverStatusRowsLocator; + const expectedReceiverStatusRow = receiversStatusRows.nthCustom(0); + const expectedReceiverStatusRowTitle = + this.getExpectedReceiverStatusRowTitle( + organizationName, + receiverName, + successRate, + ); + + await expect(receiversStatusRows).toHaveCount(this.timePeriodData.length); + + await this.updateFilters({ + receiverName, + }); + + await expect(receiversStatusRows).toHaveCount(1); + await expect(expectedReceiverStatusRow).toBeVisible(); + await expect(expectedReceiverStatusRow.title).toHaveText(expectedReceiverStatusRowTitle); + + await this.resetFilters(); + + await expect(receiversStatusRows).toHaveCount(this.timePeriodData.length); + + return true; + } + + async testReceiverMessage() { + // get first entry's result from all-fail receiver's first day -> third time period + const receiverI = 0; + const dayI = 0; + const timePeriodI = 2; + const entryI = 0; + const {days} = this.timePeriodData[receiverI]; + const {connectionCheckResult} = days[dayI].timePeriods[timePeriodI].entries[entryI]; + + const receiversStatusRows = this.receiverStatusRowsLocator; + + await this.updateFilters({ + resultMessage: connectionCheckResult, + }); + + for (const [i, {days}] of this.timePeriodData.entries()) { + const isRowExpected = i === receiverI; + const row = receiversStatusRows.nthCustom(i); + + for (const [i, {timePeriods}] of days.entries()) { + const isDayExpected = isRowExpected && i === dayI; + const rowDay = row.days.nthCustom(i); + + for (const [i] of timePeriods.entries()) { + const isTimePeriodExpected = isDayExpected && i === timePeriodI; + const expectedClass = !isTimePeriodExpected + ? /success-result-hidden/ + : /^((?!success-result-hidden).)*$/; + const rowDayTimePeriod = rowDay.timePeriods.nth(i); + + await expect(rowDayTimePeriod).toBeVisible(); + await expect(rowDayTimePeriod).toHaveClass(expectedClass); + } + } + } + + await this.resetFilters(); + + await this.testReceiverStatusDisplay(); + + return true; + } + + async testReceiverOrgLinks(isSmoke = false) { + const rows = this.receiverStatusRowsLocator; + + for (const [i, { organizationName }] of this.timePeriodData.entries()) { + const row = rows.nthCustom(i); + + const link = row.title.getByRole("link", { name: organizationName, exact: true }).first(); + const expectedUrl = this.getExpectedStatusOrganizationUrl(i); + await expect(link).toBeVisible(); + const p = this.page.route( + `/api/settings/organizations/${organizationName}`, + (route) => + route.fulfill({ + json: { + description: "fake", + filters: [], + name: organizationName, + jurisdiction: "fake", + version: 0, + createdAt: "", + createdBy: "", + } satisfies RSOrganizationSettings, + }), + ); + await link.click(); + await expect(this.page).toHaveURL(expectedUrl); + await p; + await this.page.goBack(); + + if (isSmoke && i === 0) { + break; + } + } + + return true; + } + + async testReceiverTimePeriodModals(isSmoke = false) { + const overlay = this.filterFormInputs.dateRange.modalOverlay; + + for (const [i, { days }] of this.timePeriodData.entries()) { + const { days: daysLoc } = this.receiverStatusRowsLocator.nthCustom(i); + + for (const [dayI, day] of days.entries()) { + for (const [i, { successRateType, entries }] of day.timePeriods.entries()) { + // only first entry in time period is currently displayed + const { + organizationName, + organizationId, + receiverId, + receiverName, + connectionCheckSuccessful, + connectionCheckStartedAt, + connectionCheckCompletedAt, + connectionCheckResult, + } = entries[0] ?? {}; + const sliceEle = daysLoc.nthCustom(dayI).timePeriods.nth(i); + + const isModalExpectedVisible = successRateType !== SuccessRate.UNDEFINED; + + await sliceEle.click({ force: true }); + await expect(overlay).toBeAttached({ + attached: isModalExpectedVisible, + }); + + if (isModalExpectedVisible) { + const expectedResultText = connectionCheckSuccessful ? "success" : "failed"; + const expectedModalText = `Results for connection verification checkOrg:${organizationName} (id: ${organizationId})Receiver:${receiverName} (id: ${receiverId})Result:${expectedResultText}Started At:${formatDate(connectionCheckStartedAt)}${connectionCheckStartedAt.toISOString()}Time to complete:${durationFormatShort(connectionCheckCompletedAt, connectionCheckStartedAt)}Result message:${connectionCheckResult}`; + + await expect(overlay).toBeVisible(); + await expect(overlay).toHaveText(expectedModalText); + + await overlay.press("Escape"); + } + } + } + + if (isSmoke && i === 0) { + break; + } + } + + return true; + } + + async testReceiverLinks(isSmoke = false) { + const rows = this.receiverStatusRowsLocator; + + for (const [i, { receiverName }] of this.timePeriodData.entries()) { + const row = rows.nthCustom(i); + + const link = row.title.getByRole("link", { + name: receiverName, + }); + await expect(link).toBeVisible(); + await link.click(); + await expect(this.page).toHaveURL( + this.getExpectedStatusReceiverUrl(i), + ); + await this.page.goBack(); + + if (isSmoke && i === 0) { + break; + } } return true; diff --git a/frontend-react/e2e/spec/all/authenticated/last-mile-failures-page.spec.ts b/frontend-react/e2e/spec/all/authenticated/admin/last-mile-failures-page.spec.ts similarity index 95% rename from frontend-react/e2e/spec/all/authenticated/last-mile-failures-page.spec.ts rename to frontend-react/e2e/spec/all/authenticated/admin/last-mile-failures-page.spec.ts index df8503376bb..ba01d3812c2 100644 --- a/frontend-react/e2e/spec/all/authenticated/last-mile-failures-page.spec.ts +++ b/frontend-react/e2e/spec/all/authenticated/admin/last-mile-failures-page.spec.ts @@ -1,6 +1,6 @@ -import { tableRows } from "../../../helpers/utils"; -import { LastMileFailuresPage } from "../../../pages/authenticated/last-mile-failures"; -import { test as baseTest, expect } from "../../../test"; +import { tableRows } from "../../../../helpers/utils"; +import { LastMileFailuresPage } from "../../../../pages/authenticated/last-mile-failures"; +import { test as baseTest, expect } from "../../../../test"; export interface LastMileFailuresPageFixtures { lastMileFailuresPage: LastMileFailuresPage; diff --git a/frontend-react/e2e/spec/all/authenticated/message-details-page.spec.ts b/frontend-react/e2e/spec/all/authenticated/admin/message-details-page.spec.ts similarity index 93% rename from frontend-react/e2e/spec/all/authenticated/message-details-page.spec.ts rename to frontend-react/e2e/spec/all/authenticated/admin/message-details-page.spec.ts index 916b14b1096..2a101640d8c 100644 --- a/frontend-react/e2e/spec/all/authenticated/message-details-page.spec.ts +++ b/frontend-react/e2e/spec/all/authenticated/admin/message-details-page.spec.ts @@ -1,12 +1,12 @@ import fs from "node:fs"; -import { parseFileLocation } from "../../../../src/utils/misc"; -import { tableRows } from "../../../helpers/utils"; -import { MOCK_GET_MESSAGE } from "../../../mocks/messages"; -import { MessageDetailsPage } from "../../../pages/authenticated/message-details"; -import { MessageIDSearchPage } from "../../../pages/authenticated/message-id-search"; -import { mockGetHistoryReportResponse } from "../../../pages/authenticated/report-details"; - -import { test as baseTest, expect } from "../../../test"; +import { parseFileLocation } from "../../../../../src/utils/misc"; +import { tableRows } from "../../../../helpers/utils"; +import { MOCK_GET_MESSAGE } from "../../../../mocks/messages"; +import { MessageDetailsPage } from "../../../../pages/authenticated/message-details"; +import { MessageIDSearchPage } from "../../../../pages/authenticated/message-id-search"; +import { mockGetHistoryReportResponse } from "../../../../pages/authenticated/report-details"; + +import { test as baseTest, expect } from "../../../../test"; export interface MessageDetailsPageFixtures { messageDetailsPage: MessageDetailsPage; diff --git a/frontend-react/e2e/spec/all/authenticated/message-id-search-page.spec.ts b/frontend-react/e2e/spec/all/authenticated/admin/message-id-search-page.spec.ts similarity index 94% rename from frontend-react/e2e/spec/all/authenticated/message-id-search-page.spec.ts rename to frontend-react/e2e/spec/all/authenticated/admin/message-id-search-page.spec.ts index 9de8e807739..2ac84973da4 100644 --- a/frontend-react/e2e/spec/all/authenticated/message-id-search-page.spec.ts +++ b/frontend-react/e2e/spec/all/authenticated/admin/message-id-search-page.spec.ts @@ -1,9 +1,9 @@ -import { noData, tableRows } from "../../../helpers/utils"; -import { MOCK_GET_MESSAGES } from "../../../mocks/messages"; -import { MessageIDSearchPage } from "../../../pages/authenticated/message-id-search"; -import { openReportIdDetailPage } from "../../../pages/authenticated/submission-history"; +import { noData, tableRows } from "../../../../helpers/utils"; +import { MOCK_GET_MESSAGES } from "../../../../mocks/messages"; +import { MessageIDSearchPage } from "../../../../pages/authenticated/message-id-search"; +import { openReportIdDetailPage } from "../../../../pages/authenticated/submission-history"; -import { test as baseTest, expect } from "../../../test"; +import { test as baseTest, expect } from "../../../../test"; export interface MessageIDSearchPageFixtures { messageIDSearchPage: MessageIDSearchPage; diff --git a/frontend-react/e2e/spec/all/authenticated/organization-settings-page.spec.ts b/frontend-react/e2e/spec/all/authenticated/admin/organization-settings-page.spec.ts similarity index 81% rename from frontend-react/e2e/spec/all/authenticated/organization-settings-page.spec.ts rename to frontend-react/e2e/spec/all/authenticated/admin/organization-settings-page.spec.ts index 83ce97b9e2a..ee9773e664c 100644 --- a/frontend-react/e2e/spec/all/authenticated/organization-settings-page.spec.ts +++ b/frontend-react/e2e/spec/all/authenticated/admin/organization-settings-page.spec.ts @@ -1,10 +1,10 @@ -import { expect } from "@playwright/test"; -import { readFileSync } from "node:fs"; -import { join } from "node:path"; -import { fileURLToPath } from "node:url"; -import { MOCK_GET_ORGANIZATION_SETTINGS_LIST } from "../../../mocks/organizations"; -import { OrganizationPage } from "../../../pages/authenticated/organization"; -import { test as baseTest } from "../../../test"; +import {expect} from "@playwright/test"; +import {readFileSync} from "node:fs"; +import {join} from "node:path"; +import {fileURLToPath} from "node:url"; +import {MOCK_GET_ORGANIZATION_SETTINGS_LIST} from "../../../../mocks/organizations"; +import {OrganizationPage} from "../../../../pages/authenticated/organization"; +import {test as baseTest} from "../../../../test"; const __dirname = fileURLToPath(import.meta.url); @@ -43,35 +43,35 @@ const test = baseTest.extend({ test.describe("Admin Organization Settings Page", () => { test.describe("not authenticated", () => { - test("redirects to login", async ({ organizationPage }) => { + test("redirects to login", async ({organizationPage}) => { await expect(organizationPage.page).toHaveURL("/login"); }); }); test.describe("authenticated receiver", () => { - test.use({ storageState: "e2e/.auth/receiver.json" }); - test("returns Page Not Found", async ({ organizationPage }) => { + test.use({storageState: "e2e/.auth/receiver.json"}); + test("returns Page Not Found", async ({organizationPage}) => { await expect(organizationPage.page).toHaveTitle(/Page Not Found/); }); }); test.describe("authenticated sender", () => { - test.use({ storageState: "e2e/.auth/sender.json" }); - test("returns Page Not Found", async ({ organizationPage }) => { + test.use({storageState: "e2e/.auth/sender.json"}); + test("returns Page Not Found", async ({organizationPage}) => { await expect(organizationPage.page).toHaveTitle(/Page Not Found/); }); }); test.describe("authenticated admin", () => { - test.use({ storageState: "e2e/.auth/admin.json" }); + test.use({storageState: "e2e/.auth/admin.json"}); test.describe("Header", () => { - test("has correct title + heading", async ({ organizationPage }) => { + test("has correct title + heading", async ({organizationPage}) => { await organizationPage.testHeader(); }); }); - test("If there is an error, the error is shown on the page", async ({ organizationPage }) => { + test("If there is an error, the error is shown on the page", async ({organizationPage}) => { organizationPage.mockError = true; await organizationPage.reload(); await expect(organizationPage.page.getByText("there was an error")).toBeVisible(); @@ -79,8 +79,8 @@ test.describe("Admin Organization Settings Page", () => { test.describe("When there is no error", () => { test("nav contains the 'Admin tools' dropdown with 'Organization Settings' option", async ({ - organizationPage, - }) => { + organizationPage, + }) => { const navItems = organizationPage.page.locator(".usa-nav li"); await expect(navItems).toContainText(["Admin tools"]); @@ -96,12 +96,12 @@ test.describe("Admin Organization Settings Page", () => { await expect(organizationPage.page).toHaveURL("/admin/settings"); }); - test("Has correct title", async ({ organizationPage }) => { + test("Has correct title", async ({organizationPage}) => { await expect(organizationPage.page).toHaveURL(/settings/); await expect(organizationPage.page).toHaveTitle(/Admin-Organizations/); }); - test("Displays data", async ({ organizationPage }) => { + test("Displays data", async ({organizationPage}) => { // Heading with result length await expect( organizationPage.page.getByRole("heading", { @@ -122,12 +122,12 @@ test.describe("Admin Organization Settings Page", () => { const cols = await row.getByRole("cell").allTextContents(); expect(cols).toHaveLength(colHeaders.length); - const { description, jurisdiction, name, stateCode } = + const {description, jurisdiction, name, stateCode} = i === 0 ? MOCK_GET_ORGANIZATION_SETTINGS_LIST[0] : (MOCK_GET_ORGANIZATION_SETTINGS_LIST.find((i) => i.name === cols[0]) ?? { - name: "INVALID", - }); + name: "INVALID", + }); // if first row, we expect column headers. else, the data row matching id (name) // SetEdit is text of buttons in button column const expectedColContents = @@ -141,7 +141,7 @@ test.describe("Admin Organization Settings Page", () => { } }); - test("Create new organization navigation works", async ({ organizationPage }) => { + test("Create new organization navigation works", async ({organizationPage}) => { const link = organizationPage.page.getByRole("link", { name: "Create New Organization", }); @@ -155,7 +155,7 @@ test.describe("Admin Organization Settings Page", () => { expect(organizationPage.page.url()).toContain(expectedUrl); }); - test("Save CSV button downloads a file", async ({ organizationPage }) => { + test("Save CSV button downloads a file", async ({organizationPage}) => { const downloadProm = organizationPage.page.waitForEvent("download"); const saveButton = organizationPage.page.getByRole("button", { name: "Save List to CSV", @@ -165,7 +165,7 @@ test.describe("Admin Organization Settings Page", () => { await saveButton.click(); const download = await downloadProm; - const expectedFile = readFileSync(join(__dirname, "../../../../mocks/prime-orgs.csv"), { + const expectedFile = readFileSync(join(__dirname, "../../../../../mocks/prime-orgs.csv"), { encoding: "utf-8", }); const stream = await download.createReadStream(); @@ -174,9 +174,9 @@ test.describe("Admin Organization Settings Page", () => { expect(download.suggestedFilename()).toBe("prime-orgs.csv"); }); - test("Filtering works", async ({ organizationPage }) => { + test("Filtering works", async ({organizationPage}) => { const table = organizationPage.page.getByRole("table"); - const { description, name, jurisdiction, stateCode } = MOCK_GET_ORGANIZATION_SETTINGS_LIST[2]; + const {description, name, jurisdiction, stateCode} = MOCK_GET_ORGANIZATION_SETTINGS_LIST[2]; const filterBox = organizationPage.page.getByRole("textbox", { name: "Filter:", }); @@ -201,7 +201,7 @@ test.describe("Admin Organization Settings Page", () => { } }); - test('Clicking "Set" updates link label', async ({ organizationPage }) => { + test('Clicking "Set" updates link label', async ({organizationPage}) => { const firstDataRow = organizationPage.page.getByRole("table").getByRole("row").nth(1); const firstDataRowName = (await firstDataRow.getByRole("cell").nth(0).textContent()) ?? "INVALID"; const setButton = firstDataRow.getByRole("button", { @@ -218,7 +218,7 @@ test.describe("Admin Organization Settings Page", () => { await expect(orgLink).toHaveAttribute("href", "/admin/settings"); }); - test("Edit navigation works", async ({ organizationPage }) => { + test("Edit navigation works", async ({organizationPage}) => { const firstDataRow = organizationPage.page.getByRole("table").getByRole("row").nth(1); const firstDataRowName = await firstDataRow.getByRole("cell").nth(0).textContent(); const expectedUrl = `/admin/orgsettings/org/${firstDataRowName}`; @@ -237,7 +237,7 @@ test.describe("Admin Organization Settings Page", () => { }); test.describe("Footer", () => { - test("has footer and explicit scroll to footer and scroll to top", async ({ organizationPage }) => { + test("has footer and explicit scroll to footer and scroll to top", async ({organizationPage}) => { await organizationPage.testFooter(); }); }); diff --git a/frontend-react/e2e/spec/all/authenticated/admin/receiver-status-page.spec.ts b/frontend-react/e2e/spec/all/authenticated/admin/receiver-status-page.spec.ts index f8f92d2615b..edba29a56c0 100644 --- a/frontend-react/e2e/spec/all/authenticated/admin/receiver-status-page.spec.ts +++ b/frontend-react/e2e/spec/all/authenticated/admin/receiver-status-page.spec.ts @@ -1,10 +1,6 @@ -import { addDays, endOfDay, startOfDay, subDays } from "date-fns"; -import type { RSOrganizationSettings } from "../../../../../src/config/endpoints/settings"; -import { SuccessRate } from "../../../../../src/pages/admin/receiver-dashboard/utils"; -import { durationFormatShort } from "../../../../../src/utils/DateTimeUtils"; -import { formatDate } from "../../../../../src/utils/misc"; -import { AdminReceiverStatusPage } from "../../../../pages/authenticated/admin/receiver-status"; -import { test as baseTest, expect, logins } from "../../../../test"; +import {addDays, endOfDay, startOfDay, subDays} from "date-fns"; +import {AdminReceiverStatusPage} from "../../../../pages/authenticated/admin/receiver-status"; +import {test as baseTest, expect, logins} from "../../../../test"; export interface AdminReceiverStatusPageFixtures { adminReceiverStatusPage: AdminReceiverStatusPage; @@ -42,29 +38,29 @@ const test = baseTest.extend({ test.describe("Admin Receiver Status Page", () => { test.describe("not authenticated", () => { - test("redirects to login", async ({ adminReceiverStatusPage }) => { + test("redirects to login", async ({adminReceiverStatusPage}) => { await expect(adminReceiverStatusPage.page).toHaveURL("/login"); }); }); test.describe("authenticated receiver", () => { - test.use({ storageState: logins.receiver.path }); - test("returns Page Not Found", async ({ adminReceiverStatusPage }) => { + test.use({storageState: logins.receiver.path}); + test("returns Page Not Found", async ({adminReceiverStatusPage}) => { await expect(adminReceiverStatusPage.page).toHaveTitle(/Page Not Found/); }); }); test.describe("authenticated sender", () => { - test.use({ storageState: logins.sender.path }); - test("returns Page Not Found", async ({ adminReceiverStatusPage }) => { + test.use({storageState: logins.sender.path}); + test("returns Page Not Found", async ({adminReceiverStatusPage}) => { await expect(adminReceiverStatusPage.page).toHaveTitle(/Page Not Found/); }); }); test.describe("authenticated admin", () => { - test.use({ storageState: logins.admin.path }); + test.use({storageState: logins.admin.path}); - test("If there is an error, the error is shown on the page", async ({ adminReceiverStatusPage }) => { + test("If there is an error, the error is shown on the page", async ({adminReceiverStatusPage}) => { adminReceiverStatusPage.mockError = true; await adminReceiverStatusPage.reload(); @@ -74,10 +70,7 @@ test.describe("Admin Receiver Status Page", () => { test.describe("Header", () => { test( "has correct title + heading", - { - tag: "@smoke", - }, - async ({ adminReceiverStatusPage }) => { + async ({adminReceiverStatusPage}) => { await adminReceiverStatusPage.testHeader(); }, ); @@ -87,12 +80,9 @@ test.describe("Admin Receiver Status Page", () => { test.describe("Displays correctly", () => { test.describe( "filters", - { - tag: "@smoke", - }, () => { - test("date range", async ({ adminReceiverStatusPage }) => { - const { button, label, modalOverlay, valueDisplay } = + test("date range", async ({adminReceiverStatusPage}) => { + const {button, label, modalOverlay, valueDisplay} = adminReceiverStatusPage.filterFormInputs.dateRange; await expect(label).toBeVisible(); await expect(button).toBeVisible(); @@ -100,8 +90,8 @@ test.describe("Admin Receiver Status Page", () => { await expect(modalOverlay).toBeHidden(); }); - test("receiver name", async ({ adminReceiverStatusPage }) => { - const { input, expectedTooltipText, label, tooltip, expectedDefaultValue } = + test("receiver name", async ({adminReceiverStatusPage}) => { + const {input, expectedTooltipText, label, tooltip, expectedDefaultValue} = adminReceiverStatusPage.filterFormInputs.receiverName; await expect(label).toBeVisible(); await expect(input).toBeVisible(); @@ -113,8 +103,8 @@ test.describe("Admin Receiver Status Page", () => { await expect(tooltip).toHaveText(expectedTooltipText); }); - test("results message", async ({ adminReceiverStatusPage }) => { - const { input, expectedTooltipText, label, tooltip, expectedDefaultValue } = + test("results message", async ({adminReceiverStatusPage}) => { + const {input, expectedTooltipText, label, tooltip, expectedDefaultValue} = adminReceiverStatusPage.filterFormInputs.resultMessage; await expect(label).toBeVisible(); await expect(input).toBeVisible(); @@ -126,8 +116,8 @@ test.describe("Admin Receiver Status Page", () => { await expect(tooltip).toHaveText(expectedTooltipText); }); - test("success type", async ({ adminReceiverStatusPage }) => { - const { input, expectedTooltipText, label, tooltip, expectedDefaultValue } = + test("success type", async ({adminReceiverStatusPage}) => { + const {input, expectedTooltipText, label, tooltip, expectedDefaultValue} = adminReceiverStatusPage.filterFormInputs.successType; await expect(label).toBeVisible(); await expect(input).toBeVisible(); @@ -143,7 +133,7 @@ test.describe("Admin Receiver Status Page", () => { // Failures here indicate potential misalignment of playwright/browser timezone test.describe("receiver statuses", () => { - test("time periods", async ({ adminReceiverStatusPage }) => { + test("time periods", async ({adminReceiverStatusPage}) => { const result = await adminReceiverStatusPage.testReceiverStatusDisplay(); expect(result).toBe(true); }); @@ -152,8 +142,8 @@ test.describe("Admin Receiver Status Page", () => { test.describe("Footer", () => { test("has footer and explicit scroll to footer and scroll to top", async ({ - adminReceiverStatusPage, - }) => { + adminReceiverStatusPage, + }) => { await adminReceiverStatusPage.testFooter(); }); }); @@ -162,12 +152,9 @@ test.describe("Admin Receiver Status Page", () => { test.describe("filters", () => { test.describe( "date range", - { - tag: "@smoke", - }, () => { - test("works through calendar", async ({ adminReceiverStatusPage }) => { - const { valueDisplay } = adminReceiverStatusPage.filterFormInputs.dateRange; + test("works through calendar", async ({adminReceiverStatusPage}) => { + const {valueDisplay} = adminReceiverStatusPage.filterFormInputs.dateRange; const now = new Date(); const targetFrom = startOfDay(subDays(now, 3)); const targetTo = addDays(endOfDay(now), 1); @@ -188,8 +175,8 @@ test.describe("Admin Receiver Status Page", () => { }); }); - test("works through textboxes", async ({ adminReceiverStatusPage }) => { - const { valueDisplay } = adminReceiverStatusPage.filterFormInputs.dateRange; + test("works through textboxes", async ({adminReceiverStatusPage}) => { + const {valueDisplay} = adminReceiverStatusPage.filterFormInputs.dateRange; await expect(adminReceiverStatusPage.receiverStatusRowsLocator).not.toHaveCount(0); const now = new Date(); const targetFrom = startOfDay(subDays(now, 3)); @@ -242,48 +229,12 @@ test.describe("Admin Receiver Status Page", () => { await expect(receiversStatusRows).toHaveCount(adminReceiverStatusPage.timePeriodData.length); }); - test("result message", async ({ adminReceiverStatusPage }) => { - // get first entry's result from all-fail receiver's first day -> third time period - const receiverI = 0; - const dayI = 0; - const timePeriodI = 2; - const entryI = 0; - const { days } = adminReceiverStatusPage.timePeriodData[receiverI]; - const { connectionCheckResult } = days[dayI].timePeriods[timePeriodI].entries[entryI]; - - const receiversStatusRows = adminReceiverStatusPage.receiverStatusRowsLocator; - - await adminReceiverStatusPage.updateFilters({ - resultMessage: connectionCheckResult, - }); - - for (const [i, { days }] of adminReceiverStatusPage.timePeriodData.entries()) { - const isRowExpected = i === receiverI; - const row = receiversStatusRows.nthCustom(i); - - for (const [i, { timePeriods }] of days.entries()) { - const isDayExpected = isRowExpected && i === dayI; - const rowDay = row.days.nthCustom(i); - - for (const [i] of timePeriods.entries()) { - const isTimePeriodExpected = isDayExpected && i === timePeriodI; - const expectedClass = !isTimePeriodExpected - ? /success-result-hidden/ - : /^((?!success-result-hidden).)*$/; - const rowDayTimePeriod = rowDay.timePeriods.nth(i); - - await expect(rowDayTimePeriod).toBeVisible(); - await expect(rowDayTimePeriod).toHaveClass(expectedClass); - } - } - } - - await adminReceiverStatusPage.resetFilters(); - - await adminReceiverStatusPage.testReceiverStatusDisplay(); + test("result message", async ({adminReceiverStatusPage}) => { + const result = await adminReceiverStatusPage.testReceiverMessage(); + expect(result).toBe(true); }); - test("success type", async ({ adminReceiverStatusPage }) => { + test("success type", async ({adminReceiverStatusPage}) => { const [failRow, , mixedRow] = adminReceiverStatusPage.timePeriodData; const failRowTitle = adminReceiverStatusPage.getExpectedReceiverStatusRowTitle( failRow.organizationName, @@ -321,7 +272,7 @@ test.describe("Admin Receiver Status Page", () => { test.describe("receiver statuses", () => { test.describe("date range length changes", () => { - test("increases", async ({ adminReceiverStatusPage }) => { + test("increases", async ({adminReceiverStatusPage}) => { const rows = adminReceiverStatusPage.receiverStatusRowsLocator; const days = rows.nthCustom(0).days; await expect(rows).not.toHaveCount(0); @@ -336,7 +287,7 @@ test.describe("Admin Receiver Status Page", () => { await expect(days).toHaveCount(4); }); - test("decreases", async ({ adminReceiverStatusPage }) => { + test("decreases", async ({adminReceiverStatusPage}) => { const rows = adminReceiverStatusPage.receiverStatusRowsLocator; const days = rows.nthCustom(0).days; await expect(rows).not.toHaveCount(0); @@ -352,96 +303,19 @@ test.describe("Admin Receiver Status Page", () => { }); }); - test("time period modals", async ({ adminReceiverStatusPage }) => { - const overlay = adminReceiverStatusPage.filterFormInputs.dateRange.modalOverlay; - for (const [i, { days }] of adminReceiverStatusPage.timePeriodData.entries()) { - const { days: daysLoc } = adminReceiverStatusPage.receiverStatusRowsLocator.nthCustom(i); - - for (const [dayI, day] of days.entries()) { - for (const [i, { successRateType, entries }] of day.timePeriods.entries()) { - // only first entry in time period is currently displayed - const { - organizationName, - organizationId, - receiverId, - receiverName, - connectionCheckSuccessful, - connectionCheckStartedAt, - connectionCheckCompletedAt, - connectionCheckResult, - } = entries[0] ?? {}; - const sliceEle = daysLoc.nthCustom(dayI).timePeriods.nth(i); - - const isModalExpectedVisible = successRateType !== SuccessRate.UNDEFINED; - - await sliceEle.click({ force: true }); - await expect(overlay).toBeAttached({ - attached: isModalExpectedVisible, - }); - - if (isModalExpectedVisible) { - const expectedResultText = connectionCheckSuccessful ? "success" : "failed"; - const expectedModalText = `Results for connection verification checkOrg:${organizationName} (id: ${organizationId})Receiver:${receiverName} (id: ${receiverId})Result:${expectedResultText}Started At:${formatDate(connectionCheckStartedAt)}${connectionCheckStartedAt.toISOString()}Time to complete:${durationFormatShort(connectionCheckCompletedAt, connectionCheckStartedAt)}Result message:${connectionCheckResult}`; - - await expect(overlay).toBeVisible(); - await expect(overlay).toHaveText(expectedModalText); - - await overlay.press("Escape"); - } - } - } - } + test("time period modals", async ({adminReceiverStatusPage}) => { + const result = await adminReceiverStatusPage.testReceiverTimePeriodModals(); + expect(result).toBe(true); }); - test("receiver org links", async ({ adminReceiverStatusPage }) => { - const rows = adminReceiverStatusPage.receiverStatusRowsLocator; - - for (const [i, { organizationName }] of adminReceiverStatusPage.timePeriodData.entries()) { - const row = rows.nthCustom(i); - - const link = row.title.getByRole("link", { - name: organizationName, - }); - const expectedUrl = adminReceiverStatusPage.getExpectedStatusOrganizationUrl(i); - await expect(link).toBeVisible(); - const p = adminReceiverStatusPage.page.route( - `api/settings/organizations/${organizationName}`, - (route) => - route.fulfill({ - json: { - description: "fake", - filters: [], - name: organizationName, - jurisdiction: "fake", - version: 0, - createdAt: "", - createdBy: "", - } satisfies RSOrganizationSettings, - }), - ); - await link.click(); - await expect(adminReceiverStatusPage.page).toHaveURL(expectedUrl); - await p; - await adminReceiverStatusPage.page.goBack(); - } + test("receiver org links", async ({adminReceiverStatusPage}) => { + const result = await adminReceiverStatusPage.testReceiverOrgLinks(); + expect(result).toBe(true); }); - test("receiver links", async ({ adminReceiverStatusPage }) => { - const rows = adminReceiverStatusPage.receiverStatusRowsLocator; - - for (const [i, { receiverName }] of adminReceiverStatusPage.timePeriodData.entries()) { - const row = rows.nthCustom(i); - - const link = row.title.getByRole("link", { - name: receiverName, - }); - await expect(link).toBeVisible(); - await link.click(); - await expect(adminReceiverStatusPage.page).toHaveURL( - adminReceiverStatusPage.getExpectedStatusReceiverUrl(i), - ); - await adminReceiverStatusPage.page.goBack(); - } + test("receiver links", async ({adminReceiverStatusPage}) => { + const result = await adminReceiverStatusPage.testReceiverLinks(); + expect(result).toBe(true); }); }); }); diff --git a/frontend-react/e2e/spec/chromium-only/authenticated/receiver-status-page-user-flow.spec.ts b/frontend-react/e2e/spec/chromium-only/authenticated/receiver-status-page-user-flow.spec.ts new file mode 100644 index 00000000000..5c4e456b43d --- /dev/null +++ b/frontend-react/e2e/spec/chromium-only/authenticated/receiver-status-page-user-flow.spec.ts @@ -0,0 +1,337 @@ +import { addDays, endOfDay, startOfDay, subDays } from "date-fns"; +import { AdminReceiverStatusPage } from "../../../pages/authenticated/admin/receiver-status"; +import { test as baseTest, expect, logins } from "../../../test"; + +export interface AdminReceiverStatusPageFixtures { + adminReceiverStatusPage: AdminReceiverStatusPage; +} + +const test = baseTest.extend({ + adminReceiverStatusPage: async ( + { + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + frontendWarningsLogPath, + isFrontendWarningsLog, + }, + use, + ) => { + const page = new AdminReceiverStatusPage({ + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + isFrontendWarningsLog, + frontendWarningsLogPath, + isTestOrg: true, + }); + await page.goto(); + await use(page); + }, +}); + +test.describe("Admin Receiver Status Page", + { + tag: "@smoke", + }, () => { + test.use({ storageState: logins.admin.path }); + test.describe("displays correctly", () => { + test.describe("header", () => { + test( + "has correct title + heading", + async ({ adminReceiverStatusPage }) => { + await adminReceiverStatusPage.testHeader(); + }, + ); + }); + + test.describe( + "filters", + () => { + test("date range", async ({ adminReceiverStatusPage }) => { + const { button, label, modalOverlay, valueDisplay } = + adminReceiverStatusPage.filterFormInputs.dateRange; + await expect(label).toBeVisible(); + await expect(button).toBeVisible(); + await expect(valueDisplay).toHaveText(adminReceiverStatusPage.expectedDateRangeLabelText); + await expect(modalOverlay).toBeHidden(); + }); + + test("receiver name", async ({ adminReceiverStatusPage }) => { + const { input, expectedTooltipText, label, tooltip, expectedDefaultValue } = + adminReceiverStatusPage.filterFormInputs.receiverName; + await expect(label).toBeVisible(); + await expect(input).toBeVisible(); + await expect(input).toHaveValue(expectedDefaultValue); + + await expect(tooltip).toBeHidden(); + await input.hover(); + await expect(tooltip).toBeVisible(); + await expect(tooltip).toHaveText(expectedTooltipText); + }); + + test("results message", async ({ adminReceiverStatusPage }) => { + const { input, expectedTooltipText, label, tooltip, expectedDefaultValue } = + adminReceiverStatusPage.filterFormInputs.resultMessage; + await expect(label).toBeVisible(); + await expect(input).toBeVisible(); + await expect(input).toHaveValue(expectedDefaultValue); + + await expect(tooltip).toBeHidden(); + await input.hover(); + await expect(tooltip).toBeVisible(); + await expect(tooltip).toHaveText(expectedTooltipText); + }); + + test("success type", async ({ adminReceiverStatusPage }) => { + const { input, expectedTooltipText, label, tooltip, expectedDefaultValue } = + adminReceiverStatusPage.filterFormInputs.successType; + await expect(label).toBeVisible(); + await expect(input).toBeVisible(); + await expect(input).toHaveValue(expectedDefaultValue); + + await expect(tooltip).toBeHidden(); + await input.hover(); + await expect(tooltip).toBeVisible(); + await expect(tooltip).toHaveText(expectedTooltipText); + }); + }, + ); + + // Failures here indicate potential misalignment of playwright/browser timezone + test.describe("receiver statuses", () => { + test("time periods", async ({ adminReceiverStatusPage }) => { + const result = await adminReceiverStatusPage.testReceiverStatusDisplay(true); + expect(result).toBe(true); + }); + }); + + test.describe("has footer", () => { + test("has footer and explicit scroll to footer and scroll to top", + async ({ + adminReceiverStatusPage, + }) => { + await adminReceiverStatusPage.testFooter(); + }); + }); + }); + + test.describe("functions correctly", () => { + test.describe("filters", () => { + test.describe( + "date range", + () => { + test("works through calendar", async ({ adminReceiverStatusPage }) => { + const { valueDisplay } = adminReceiverStatusPage.filterFormInputs.dateRange; + const now = new Date(); + const targetFrom = startOfDay(subDays(now, 3)); + const targetTo = addDays(endOfDay(now), 1); + + const reqUrl = await adminReceiverStatusPage.updateFilters({ + dateRange: { + value: [targetFrom, targetTo], + }, + }); + expect(reqUrl).toBeDefined(); + + await expect(valueDisplay).toHaveText( + adminReceiverStatusPage.expectedDateRangeLabelText, + ); + expect(Object.fromEntries(reqUrl!.searchParams.entries())).toMatchObject({ + start_date: targetFrom.toISOString(), + end_date: targetTo.toISOString(), + }); + }); + + test("works through textboxes", async ({ adminReceiverStatusPage }) => { + const { valueDisplay } = adminReceiverStatusPage.filterFormInputs.dateRange; + await expect(adminReceiverStatusPage.receiverStatusRowsLocator).not.toHaveCount(0); + const now = new Date(); + const targetFrom = startOfDay(subDays(now, 3)); + const targetTo = addDays(endOfDay(now), 1); + + const reqUrl = await adminReceiverStatusPage.updateFilters({ + dateRange: { + value: [targetFrom, targetTo], + }, + }); + + expect(reqUrl).toBeDefined(); + + await expect(valueDisplay).toHaveText( + adminReceiverStatusPage.expectedDateRangeLabelText, + ); + expect(Object.fromEntries(reqUrl!.searchParams.entries())).toMatchObject({ + start_date: targetFrom.toISOString(), + end_date: targetTo.toISOString(), + }); + }); + }, + ); + + test("receiver name", async ({adminReceiverStatusPage, isMockDisabled}) => { + test.skip(!isMockDisabled, "Mocks are ENABLED, skipping 'receiver name' test"); + const {organizationName, receiverName, successRate} = + adminReceiverStatusPage.timePeriodData[1]; + + const receiversStatusRows = adminReceiverStatusPage.receiverStatusRowsLocator; + const defaultReceiversStatusRowsCount = await receiversStatusRows.count(); + const expectedReceiverStatusRow = receiversStatusRows.nthCustom(0); + const expectedReceiverStatusRowTitle = + adminReceiverStatusPage.getExpectedReceiverStatusRowTitle( + organizationName, + receiverName, + successRate, + ); + + expect(defaultReceiversStatusRowsCount).toBe(adminReceiverStatusPage.timePeriodData.length); + + await adminReceiverStatusPage.updateFilters({ + receiverName, + }); + + const receiversStatusRowsCount = await receiversStatusRows.count(); + expect(receiversStatusRowsCount).toBeGreaterThanOrEqual(1); + await expect(expectedReceiverStatusRow).toBeVisible(); + await expect(expectedReceiverStatusRow.title).toHaveText(expectedReceiverStatusRowTitle); + + await adminReceiverStatusPage.resetFilters(); + + expect(defaultReceiversStatusRowsCount).toBe(adminReceiverStatusPage.timePeriodData.length); + }); + + test("result message", async ({adminReceiverStatusPage, isMockDisabled}) => { + test.skip(!isMockDisabled, "Mocks are ENABLED, skipping 'result message' test"); + // get first entry's result from all-fail receiver's first day -> third time period + const receiverI = 0; + const dayI = 0; + const timePeriodI = 2; + const entryI = 0; + const {days} = adminReceiverStatusPage.timePeriodData[receiverI]; + const {connectionCheckResult} = days[dayI].timePeriods[timePeriodI].entries[entryI]; + + const receiversStatusRows = adminReceiverStatusPage.receiverStatusRowsLocator; + + await adminReceiverStatusPage.updateFilters({ + resultMessage: connectionCheckResult, + }); + + for (const [i, {days}] of adminReceiverStatusPage.timePeriodData.entries()) { + const row = receiversStatusRows.nthCustom(i); + + for (const [i, {timePeriods}] of days.entries()) { + const rowDay = row.days.nthCustom(i); + + for (const [i] of timePeriods.entries()) { + const rowDayTimePeriod = rowDay.timePeriods.nth(i); + + await expect(rowDayTimePeriod).toHaveClass(/success-result-hidden/); + } + } + } + + await adminReceiverStatusPage.resetFilters(); + + // TODO: revisit after filters have been fixed per ticket #15737 + // await adminReceiverStatusPage.testReceiverStatusDisplay(); + }); + + test("success type", async ({ adminReceiverStatusPage, isMockDisabled }) => { + test.skip(!isMockDisabled, "Mocks are ENABLED, skipping 'success type' test"); + const [failRow, ,] = adminReceiverStatusPage.timePeriodData; + const failRowTitle = adminReceiverStatusPage.getExpectedReceiverStatusRowTitle( + failRow.organizationName, + failRow.receiverName, + failRow.successRate, + ); + // const mixedRowTitle = adminReceiverStatusPage.getExpectedReceiverStatusRowTitle( + // mixedRow.organizationName, + // mixedRow.receiverName, + // mixedRow.successRate, + // ); + + const receiversStatusRows = adminReceiverStatusPage.receiverStatusRowsLocator; + const defaultReceiversStatusRowsCount = await receiversStatusRows.count(); + const expectedRow = receiversStatusRows.nthCustom(0); + + expect(defaultReceiversStatusRowsCount).toBe(adminReceiverStatusPage.timePeriodData.length); + + await adminReceiverStatusPage.updateFilters({ + successType: "ALL_FAILURE", + }); + let receiversStatusRowsCount = await receiversStatusRows.count(); + + expect(receiversStatusRowsCount).toBeGreaterThanOrEqual(1); + await expect(expectedRow.title).toHaveText(failRowTitle); + + await adminReceiverStatusPage.updateFilters({ + successType: "MIXED_SUCCESS", + }); + receiversStatusRowsCount = await receiversStatusRows.count(); + expect(receiversStatusRowsCount).toBeGreaterThanOrEqual(1); + // TODO: revisit after filters have been fixed per ticket #15737 + // await expect(expectedRow.title).toHaveText(mixedRowTitle); + + // await adminReceiverStatusPage.resetFilters(); + // receiversStatusRowsCount = await receiversStatusRows.count(); + // + // expect(receiversStatusRowsCount).toBe(defaultReceiversStatusRowsCount); + }); + }); + + test.describe("receiver statuses", () => { + test.describe("date range length changes", () => { + test("increases", async ({ adminReceiverStatusPage }) => { + const rows = adminReceiverStatusPage.receiverStatusRowsLocator; + const days = rows.nthCustom(0).days; + await expect(rows).not.toHaveCount(0); + const now = new Date(); + const targetFrom = startOfDay(subDays(now, 3)); + const targetTo = endOfDay(now); + await adminReceiverStatusPage.updateFilters({ + dateRange: { + value: [targetFrom, targetTo], + }, + }); + await expect(days).toHaveCount(4); + }); + + test("decreases", async ({ adminReceiverStatusPage }) => { + const rows = adminReceiverStatusPage.receiverStatusRowsLocator; + const days = rows.nthCustom(0).days; + await expect(rows).not.toHaveCount(0); + const now = new Date(); + const targetFrom = startOfDay(subDays(now, 1)); + const targetTo = endOfDay(now); + await adminReceiverStatusPage.updateFilters({ + dateRange: { + value: [targetFrom, targetTo], + }, + }); + await expect(days).toHaveCount(2); + }); + }); + + test("time period modals", async ({ adminReceiverStatusPage }) => { + const result = await adminReceiverStatusPage.testReceiverTimePeriodModals(true); + expect(result).toBe(true); + }); + + test("receiver org links", async ({ adminReceiverStatusPage }) => { + const result = await adminReceiverStatusPage.testReceiverOrgLinks(true); + expect(result).toBe(true); + }); + + test("receiver links", async ({ adminReceiverStatusPage }) => { + const result = await adminReceiverStatusPage.testReceiverLinks(true); + expect(result).toBe(true); + }); + }); + }); + });