diff --git a/.github/actions/build-backend/action.yml b/.github/actions/build-backend/action.yml index c453c028cb1..aa3e5d95152 100644 --- a/.github/actions/build-backend/action.yml +++ b/.github/actions/build-backend/action.yml @@ -39,7 +39,7 @@ runs: distribution: "temurin" cache: "gradle" - - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 + - uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 - name: Lint if: inputs.run-integration-tests == 'true' diff --git a/.github/actions/build-submissions/action.yml b/.github/actions/build-submissions/action.yml new file mode 100644 index 00000000000..630c1e46f85 --- /dev/null +++ b/.github/actions/build-submissions/action.yml @@ -0,0 +1,70 @@ +# action.yml +name: "Build Submissions" +description: "Build submissions microservice" +inputs: + version: + description: "Version tag" + required: true + upload-build: + default: true + run-integration-tests: + default: false + run-qc: + default: false + github-token: + default: false + sp-creds: + description: "Azure Service Principal creds" + +runs: + using: "composite" + steps: + # These are for CI and not credentials of any system + - name: Set Environment Variables + working-directory: prime-router + shell: bash + run: | + echo >> $GITHUB_ENV DB_USER='prime' + echo >> $GITHUB_ENV DB_PASSWORD='changeIT!' + + - name: Remove unnecessary software + shell: bash + run: | + sudo rm -rf /usr/local/lib/android + + - name: Set up JDK 17 + uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 + with: + java-version: "17" + distribution: "temurin" + cache: "gradle" + + - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 + + - name: Lint + if: inputs.run-integration-tests == 'true' + run: ./gradlew :submissions:ktlintCheck + shell: bash + + - name: Spin up build containers + working-directory: prime-router + shell: bash + run: docker compose -f docker-compose.postgres.yml up -d + + - name: Build Submissions Package + uses: ./.github/actions/retry + with: + timeout_minutes: 10 + max_attempts: 2 + retry_wait_seconds: 30 + command: | + ./gradlew :submissions:build -x test + shell: bash + + - name: Cleanup Gradle Cache + if: inputs.run-integration-tests == 'true' + working-directory: prime-router + run: | + rm -f .gradle/caches/modules-2/modules-2.lock + rm -f .gradle/caches/modules-2/gc.properties + shell: bash diff --git a/.github/actions/sonarcloud/action.yml b/.github/actions/sonarcloud/action.yml index b88aa91b77a..a9fb695c0d9 100644 --- a/.github/actions/sonarcloud/action.yml +++ b/.github/actions/sonarcloud/action.yml @@ -22,11 +22,11 @@ runs: args: > -Dsonar.coverage.exclusions=prime-router/src/test/**,prime-router/src/testIntegration/**,prime-router/src/main/kotlin/cli/tests/**,frontend-react/**/__mocks__/**,frontend-react/**/mocks/**,frontend-react/**/*.test.* -Dsonar.cpd.exclusions=frontend-react/**/*.test.*,prime-router/src/test/**,prime-router/src/testIntegration/**,prime-router/src/main/kotlin/cli/tests/** - -Dsonar.sources=frontend-react/src,prime-router/src + -Dsonar.sources=frontend-react/src,prime-router/src,submissions/src,shared/src -Dsonar.projectKey=CDCgov_prime-data-hub -Dsonar.organization=cdcgov - -Dsonar.java.binaries=prime-router/build/classes/java/main,prime-router/build/classes/kotlin/main - -Dsonar.java.libraries=prime-router/build/libs/*.jar,prime-router/build/**/*.jar + -Dsonar.java.binaries=prime-router/build/classes/java/main,prime-router/build/classes/kotlin/main,submissions/build/classes/kotlin/main,shared/build/classes/kotlin/main + -Dsonar.java.libraries=prime-router/build/libs/*.jar,prime-router/build/**/*.jar,submissions/build/**/*.jar,shared/build/**/*.jar -Dsonar.coverage.jacoco.xmlReportPaths=prime-router/build/reports/jacoco/test/jacocoTestReport.xml -Dsonar.javascript.lcov.reportPaths=frontend-react/coverage/lcov.info diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml index 5f0551d1034..555553c1bcd 100644 --- a/.github/workflows/snyk.yml +++ b/.github/workflows/snyk.yml @@ -40,7 +40,7 @@ jobs: java-version: "17" distribution: "temurin" cache: "gradle" - - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 + - uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 - name: Snyk Monitor working-directory: ${{ matrix.folder }} run: snyk monitor --org=prime-reportstream diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index eb7ab3820af..9a16052d958 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -70,7 +70,7 @@ jobs: - name: Gradle setup if: steps.changed-files-yaml.outputs.backend_any_changed == 'true' || steps.branch-name.outputs.is_default == 'true' - uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 + uses: gradle/actions/setup-gradle@16bf8bc8fe830fa669c3c9f914d3eb147c629707 - name: Spin up build containers if: steps.changed-files-yaml.outputs.backend_any_changed == 'true' || steps.branch-name.outputs.is_default == 'true' @@ -93,6 +93,10 @@ jobs: command: ./gradlew -Dorg.gradle.jvmargs="-Xmx6g" :prime-router:package -x fatjar shell: bash + - name: Build Submissions Package + if: steps.changed-files-yaml.outputs.backend_any_changed == 'true' || steps.branch-name.outputs.is_default == 'true' + uses: ./.github/actions/build-submissions + - name: Perform Java CodeQL Analysis if: steps.changed-files-yaml.outputs.backend_any_changed == 'true' || steps.branch-name.outputs.is_default == 'true' uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/validate_terraform.yml b/.github/workflows/validate_terraform.yml index 46b5748de6e..14bdbf2dd74 100644 --- a/.github/workflows/validate_terraform.yml +++ b/.github/workflows/validate_terraform.yml @@ -48,7 +48,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 - name: Run Checkov action - uses: bridgecrewio/checkov-action@b57df8031953b36872c225e6627691100b03bcde + uses: bridgecrewio/checkov-action@1b813e8f72afe2b6263a6ea10c873707e21ebe44 with: directory: operations/app/terraform skip_check: CKV_AZURE_139,CKV_AZURE_137,CKV_AZURE_103,CKV_AZURE_104,CKV_AZURE_102,CKV_AZURE_130,CKV_AZURE_121,CKV_AZURE_67,CKV_AZURE_56,CKV_AZURE_17,CKV_AZURE_63,CKV_AZURE_18,CKV_AZURE_88,CKV_AZURE_65,CKV_AZURE_13,CKV_AZURE_66,CKV_AZURE_33,CKV_AZURE_35,CKV_AZURE_36,CKV_AZURE_98,CKV2_AZURE_1,CKV2_AZURE_15,CKV2_AZURE_21,CKV_AZURE_213,CKV_AZURE_59,CKV2_AZURE_33,CKV2_AZURE_32,CKV2_AZURE_28,CKV_AZURE_206,CKV_AZURE_42,CKV_AZURE_110,CKV_AZURE_109,CKV_AZURE_166,CKV2_AZURE_38,CKV2_AZURE_40,CKV2_AZURE_41,CKV_AZURE_235 diff --git a/frontend-react/.eslintrc.cjs b/frontend-react/.eslintrc.cjs index 4a4debd4a41..ab82b5316c1 100644 --- a/frontend-react/.eslintrc.cjs +++ b/frontend-react/.eslintrc.cjs @@ -82,6 +82,7 @@ module.exports = { // TODO: investigate these for reconsideration or per-module ignoring "playwright/no-conditional-in-test": ["off"], "playwright/no-force-option": ["off"], + "playwright/expect-expect": ["off"], }, }, ], diff --git a/frontend-react/e2e/helpers/internal-links.ts b/frontend-react/e2e/helpers/internal-links.ts index f3dc9e4af23..3ad68bb073a 100644 --- a/frontend-react/e2e/helpers/internal-links.ts +++ b/frontend-react/e2e/helpers/internal-links.ts @@ -1,17 +1,58 @@ import { Page } from "@playwright/test"; -export const ELC = - "https://www.cdc.gov/epidemiology-laboratory-capacity/php/about/"; +export const ELC = "https://www.cdc.gov/epidemiology-laboratory-capacity/php/about/"; -export async function clickOnInternalLink( - locator: string, - dataTestId: string, - linkName: string, - page: Page, -) { - await page - .locator(locator) - .getByTestId(dataTestId) - .getByRole("link", { name: linkName }) - .click(); +export async function clickOnInternalLink(locator: string, dataTestId: string, linkName: string, page: Page) { + await page.locator(locator).getByTestId(dataTestId).getByRole("link", { name: linkName }).click(); } + +export interface SideNavItem { + name: string; + path: string; +} + +export const aboutSideNav = [ + { + name: "About", + path: "/about", + }, + { + name: "Our network", + path: "/about/our-network", + }, + { + name: "Product roadmap", + path: "/about/roadmap", + }, + { + name: "News", + path: "/about/news", + }, + { + name: "Case studies", + path: "/about/case-studies", + }, + { + name: "Security", + path: "/about/security", + }, + { + name: "Release notes", + path: "/about/release-notes", + }, +]; + +export const gettingStartedSideNav = [ + { + name: "Getting started", + path: "/getting-started", + }, + { + name: "Sending data", + path: "/getting-started/sending-data", + }, + { + name: "Receiving data", + path: "/getting-started/receiving-data", + }, +]; diff --git a/frontend-react/e2e/pages/BasePage.ts b/frontend-react/e2e/pages/BasePage.ts index 69a5441c8e6..ea831731a04 100644 --- a/frontend-react/e2e/pages/BasePage.ts +++ b/frontend-react/e2e/pages/BasePage.ts @@ -1,6 +1,7 @@ +import { SideNavItem } from "../helpers/internal-links"; import { selectTestOrg } from "../helpers/utils"; import appInsightsConfig from "../mocks/appInsightsConfig.json" assert { type: "json" }; -import { Locator, Page, Request, Response, Route, TestArgs } from "../test"; +import { expect, Locator, Page, Request, Response, Route, TestArgs } from "../test"; export type RouteHandlers = Record[1]>; export type MockRouteCache = Record; @@ -12,25 +13,12 @@ export interface BasePageProps { heading?: Locator; } -export type RouteFulfillOptions = Exclude< - Parameters[0], - undefined -> & { isMock?: boolean }; -export type RouteFulfillOptionsFn = ( - request: Request, -) => Promise | RouteFulfillOptions; +export type RouteFulfillOptions = Exclude[0], undefined> & { isMock?: boolean }; +export type RouteFulfillOptionsFn = (request: Request) => Promise | RouteFulfillOptions; export type RouteHandlerFn = (route: Route, request: Request) => Promise; -export type RouteHandlerFulfillOptions = - | RouteFulfillOptions - | RouteFulfillOptionsFn; -export type RouteHandlerFulfillEntry = [ - url: string, - fulfillOptions: RouteHandlerFulfillOptions, -]; -export type ResponseHandlerEntry = [ - url: string, - handler: (response: Response) => Promise | void, -]; +export type RouteHandlerFulfillOptions = RouteFulfillOptions | RouteFulfillOptionsFn; +export type RouteHandlerFulfillEntry = [url: string, fulfillOptions: RouteHandlerFulfillOptions]; +export type ResponseHandlerEntry = [url: string, handler: (response: Response) => Promise | void]; export type RouteHandlerEntry = [url: string, handler: RouteHandlerFn]; export interface GotoRouteHandlerOptions { @@ -70,10 +58,7 @@ export abstract class BasePage { readonly heading: Locator; readonly footer: Locator; - constructor( - { url, title, heading }: BasePageProps, - testArgs: BasePageTestArgs, - ) { + constructor({ url, title, heading }: BasePageProps, testArgs: BasePageTestArgs) { this.page = testArgs.page; this.url = url; this.title = title; @@ -94,9 +79,7 @@ export abstract class BasePage { return this._mockError; } - set mockError( - err: boolean | number | RouteHandlerFulfillOptions | undefined, - ) { + set mockError(err: boolean | number | RouteHandlerFulfillOptions | undefined) { if (err == null || err === false) { this._mockError = undefined; return; @@ -151,6 +134,39 @@ export abstract class BasePage { ); } + async testHeader() { + await expect(this.page).toHaveTitle(this.title); + await expect(this.heading).toBeVisible(); + } + + async testCard(card: { name: string }) { + const cardHeader = this.page.locator(".usa-card__header", { + hasText: card.name, + }); + + await expect(cardHeader).toBeVisible(); + } + + async testSidenav(navItems: SideNavItem[]) { + const sideNav = this.page.getByTestId("sidenav"); + + for (const navItem of navItems) { + const link = sideNav.locator(`a`, { hasText: navItem.name }); + + await expect(link).toBeVisible(); + await expect(link).toHaveAttribute("href", navItem.path); + } + } + + async testFooter() { + await expect(this.page.locator("footer")).toBeAttached(); + await this.page.locator("footer").scrollIntoViewIfNeeded(); + await expect(this.page.locator("footer")).toBeInViewport(); + await expect(this.page.getByTestId("govBanner")).not.toBeInViewport(); + await this.page.evaluate(() => window.scrollTo(0, 0)); + await expect(this.page.getByTestId("govBanner")).toBeInViewport(); + } + /** * Used to select the test org if logged-in user is Admin and the isTestOrg prop is set to true. * This is needed for smoke tests since they use live data. @@ -210,19 +226,11 @@ export abstract class BasePage { const wrapped = items.map(([url, _fulfillOptions]) => { const fn = async (request: Request) => { const fulfillOptions = - typeof _fulfillOptions === "function" - ? await _fulfillOptions(request) - : _fulfillOptions; + typeof _fulfillOptions === "function" ? await _fulfillOptions(request) : _fulfillOptions; const mockErrorFulfillOptions = - typeof this.mockError === "function" - ? await this.mockError(request) - : this.mockError; - const mockCacheFulfillOptions = this.getMockCacheFulfillOptions( - url, - fulfillOptions, - ); - const mockOverrideFulfillOptions = - mockErrorFulfillOptions ?? mockCacheFulfillOptions; + typeof this.mockError === "function" ? await this.mockError(request) : this.mockError; + const mockCacheFulfillOptions = this.getMockCacheFulfillOptions(url, fulfillOptions); + const mockOverrideFulfillOptions = mockErrorFulfillOptions ?? mockCacheFulfillOptions; return { isMock: true, @@ -233,9 +241,7 @@ export abstract class BasePage { }); wrapped.forEach(([url, fn]) => - this.mockRouteHandlers.set(url, async (route, req) => - route.fulfill(await fn(req)), - ), + this.mockRouteHandlers.set(url, async (route, req) => route.fulfill(await fn(req))), ); return wrapped; @@ -244,15 +250,10 @@ export abstract class BasePage { /** * Helper function to convert RouteHandlerFulfillEntries to RouteHandlerEntries. */ - createRouteHandlers( - items: RouteHandlerFulfillEntry[], - ): RouteHandlerEntry[] { + createRouteHandlers(items: RouteHandlerFulfillEntry[]): RouteHandlerEntry[] { return items.map(([url, _fulfill]) => { const handler = async (route: Route, request: Request) => { - const fulfill = - typeof _fulfill === "function" - ? await _fulfill(request) - : _fulfill; + const fulfill = typeof _fulfill === "function" ? await _fulfill(request) : _fulfill; return route.fulfill(fulfill); }; @@ -302,10 +303,7 @@ export abstract class BasePage { * Get or warm the cache for a particular mock URL's fulfillOptions. This * allows for dynamic options to persist across page reloads for consistency. */ - getMockCacheFulfillOptions( - url: string, - fulfillOptions: RouteFulfillOptions, - ) { + getMockCacheFulfillOptions(url: string, fulfillOptions: RouteFulfillOptions) { const cache = this._mockRouteCache[url]; if (!cache) { this._mockRouteCache[url] = fulfillOptions; diff --git a/frontend-react/e2e/pages/about-side-navigation.ts b/frontend-react/e2e/pages/about-side-navigation.ts deleted file mode 100644 index cd685dbad0e..00000000000 --- a/frontend-react/e2e/pages/about-side-navigation.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Page } from "@playwright/test"; - -export async function clickNetwork(page: Page) { - await page - .getByTestId("sidenav") - .getByRole("link", { name: /Our network/ }) - .click(); -} - -export async function clickRoadmap(page: Page) { - await page - .getByTestId("sidenav") - .getByRole("link", { name: /Product roadmap/ }) - .click(); -} - -export async function clickNews(page: Page) { - await page.getByTestId("sidenav").getByRole("link", { name: /News/ }).click(); -} - -export async function clickCaseStudies(page: Page) { - await page - .getByTestId("sidenav") - .getByRole("link", { name: /Case studies/ }) - .click(); -} - -export async function clickSecurity(page: Page) { - await page - .getByTestId("sidenav") - .getByRole("link", { name: /Security/ }) - .click(); -} - -export async function clickReleaseNotes(page: Page) { - await page - .getByTestId("sidenav") - .getByRole("link", { name: /Release notes/ }) - .click(); -} diff --git a/frontend-react/e2e/pages/admin/receiver-status.ts b/frontend-react/e2e/pages/authenticated/admin/receiver-status.ts similarity index 75% rename from frontend-react/e2e/pages/admin/receiver-status.ts rename to frontend-react/e2e/pages/authenticated/admin/receiver-status.ts index 0f69f4bb691..a937092350c 100644 --- a/frontend-react/e2e/pages/admin/receiver-status.ts +++ b/frontend-react/e2e/pages/authenticated/admin/receiver-status.ts @@ -1,18 +1,13 @@ import { expect, Locator } from "@playwright/test"; import { endOfDay, format, startOfDay, subDays } from "date-fns"; -import { RSReceiverStatus } from "../../../src/hooks/api/UseReceiversConnectionStatus/UseReceiversConnectionStatus"; +import { RSReceiverStatus } from "../../../../src/hooks/api/UseReceiversConnectionStatus/UseReceiversConnectionStatus"; import { createStatusTimePeriodData, SUCCESS_RATE_CLASSNAME_MAP, -} from "../../../src/pages/admin/receiver-dashboard/utils"; -import { DatePair, dateShortFormat } from "../../../src/utils/DateTimeUtils"; -import { createMockGetReceiverStatus } from "../../mocks/receiverStatus"; -import { - BasePage, - BasePageTestArgs, - type ResponseHandlerEntry, - type RouteHandlerFulfillEntry, -} from "../BasePage"; +} from "../../../../src/pages/admin/receiver-dashboard/utils"; +import { DatePair, dateShortFormat } from "../../../../src/utils/DateTimeUtils"; +import { createMockGetReceiverStatus } from "../../../mocks/receiverStatus"; +import { BasePage, BasePageTestArgs, type ResponseHandlerEntry, type RouteHandlerFulfillEntry } from "../../BasePage"; export interface AdminReceiverStatusPageUpdateFiltersProps { dateRange?: { @@ -90,9 +85,7 @@ export class AdminReceiverStatusPage extends BasePage { ); this.addMockRouteHandlers([this.createMockReceiverStatusHandler()]); - this.addResponseHandlers([ - this.createMockReceiverStatusResponseHandler(), - ]); + this.addResponseHandlers([this.createMockReceiverStatusResponseHandler()]); const now = new Date(); @@ -100,13 +93,8 @@ export class AdminReceiverStatusPage extends BasePage { this._timePeriodData = []; this.filterForm = this.page.getByRole("form", { name: "filter" }); - const dateRangeOverlay = this.page - .getByRole("dialog") - .locator(".usa-modal-overlay"); - const dateRangeDefaultValue = [ - startOfDay(subDays(now, 2)), - endOfDay(now), - ] as DatePair; + const dateRangeOverlay = this.page.getByRole("dialog").locator(".usa-modal-overlay"); + const dateRangeDefaultValue = [startOfDay(subDays(now, 2)), endOfDay(now)] as DatePair; this.filterFormInputs = { dateRange: { label: this.page.locator("label", { @@ -116,8 +104,7 @@ export class AdminReceiverStatusPage extends BasePage { name: "Change...", }), modalOverlay: dateRangeOverlay, - expectedModalOverlayText: - "Select date range to show. (Max 10 days span)FromToUpdate", + expectedModalOverlayText: "Select date range to show. (Max 10 days span)FromToUpdate", modalOverlayCalendar: dateRangeOverlay.getByRole("application"), modalPrimaryButton: dateRangeOverlay.getByRole("button", { name: "Update", @@ -144,22 +131,15 @@ export class AdminReceiverStatusPage extends BasePage { name: "Receiver Name:", }), expectedDefaultValue: "", - tooltip: this.page - .getByTestId("tooltipWrapper") - .nth(0) - .getByRole("tooltip"), + tooltip: this.page.getByTestId("tooltipWrapper").nth(0).getByRole("tooltip"), value: "", }, resultMessage: { label: this.page.locator("label", { hasText: "Results Message:", }), - expectedTooltipText: - "Filter rows on the Result Message details. This value is found in the details.", - tooltip: this.page - .getByTestId("tooltipWrapper") - .nth(1) - .getByRole("tooltip"), + expectedTooltipText: "Filter rows on the Result Message details. This value is found in the details.", + tooltip: this.page.getByTestId("tooltipWrapper").nth(1).getByRole("tooltip"), input: this.page.getByRole("textbox", { name: "Results Message:", }), @@ -171,10 +151,7 @@ export class AdminReceiverStatusPage extends BasePage { hasText: "Success Type:", }), expectedTooltipText: "Show only rows in one of these states.", - tooltip: this.page - .getByTestId("tooltipWrapper") - .nth(2) - .getByRole("tooltip"), + tooltip: this.page.getByTestId("tooltipWrapper").nth(2).getByRole("tooltip"), input: this.page.getByRole("combobox", { name: "Success Type:", }), @@ -192,10 +169,7 @@ export class AdminReceiverStatusPage extends BasePage { * Error expected additionally if user context isn't admin */ get isPageLoadExpected() { - return ( - super.isPageLoadExpected && - this.testArgs.storageState === this.testArgs.adminLogin.path - ); + return super.isPageLoadExpected && this.testArgs.storageState === this.testArgs.adminLogin.path; } get receiverStatus() { @@ -213,10 +187,7 @@ export class AdminReceiverStatusPage extends BasePage { const url = new URL(request.url()); const startDate = url.searchParams.get("start_date"); const endDate = url.searchParams.get("end_date"); - const range = - startDate && endDate - ? ([new Date(startDate), new Date(endDate)] as DatePair) - : undefined; + const range = startDate && endDate ? ([new Date(startDate), new Date(endDate)] as DatePair) : undefined; return { json: this.createMockReceiverStatuses(range), @@ -233,27 +204,18 @@ export class AdminReceiverStatusPage extends BasePage { const url = new URL(apiRes.url()); const startDate = url.searchParams.get("start_date"); const endDate = url.searchParams.get("end_date"); - const range = - startDate && endDate - ? ([new Date(startDate), new Date(endDate)] as DatePair) - : undefined; + const range = startDate && endDate ? ([new Date(startDate), new Date(endDate)] as DatePair) : undefined; this._receiverStatus = data; - this._timePeriodData = range - ? this.createTimePeriodData({ data, range }) - : []; + this._timePeriodData = range ? this.createTimePeriodData({ data, range }) : []; }, ]; } - createMockReceiverStatuses( - ...args: Parameters - ) { + createMockReceiverStatuses(...args: Parameters) { return createMockGetReceiverStatus(...args); } - createTimePeriodData( - ...args: Parameters - ) { + createTimePeriodData(...args: Parameters) { return createStatusTimePeriodData(...args); } @@ -281,11 +243,7 @@ export class AdminReceiverStatusPage extends BasePage { }); } - getExpectedReceiverStatusRowTitle( - organizationName: string, - receiverName: string, - successRate: number | string, - ) { + getExpectedReceiverStatusRowTitle(organizationName: string, receiverName: string, successRate: number | string) { return [organizationName, receiverName, successRate, "%"].join(""); } @@ -303,16 +261,14 @@ export class AdminReceiverStatusPage extends BasePage { allCustom: async () => { return (await days.all()).map((d) => Object.assign(d, { - timePeriods: - this.getReceiverStatusRowDisplayDayTimePeriods(d), + timePeriods: this.getReceiverStatusRowDisplayDayTimePeriods(d), }), ); }, nthCustom: (nth: number) => { const day = days.nth(nth); return Object.assign(day, { - timePeriods: - this.getReceiverStatusRowDisplayDayTimePeriods(day), + timePeriods: this.getReceiverStatusRowDisplayDayTimePeriods(day), }); }, }); @@ -334,39 +290,21 @@ export class AdminReceiverStatusPage extends BasePage { }: AdminReceiverStatusPageUpdateFiltersProps) { // API request will only fire if date ranges are different const isDateRangeDifferent = - dateRange == null || - this.getIsDateRangesDifferent( - this.filterFormInputs.dateRange.value, - dateRange.value, - ); - const isRequestAwaitedBool = - dateRange != null && - isDateRangeDifferent && - dateRange.isRequestAwaited !== false; + dateRange == null || this.getIsDateRangesDifferent(this.filterFormInputs.dateRange.value, dateRange.value); + const isRequestAwaitedBool = dateRange != null && isDateRangeDifferent && dateRange.isRequestAwaited !== false; const p = isRequestAwaitedBool - ? this.page.waitForRequest( - AdminReceiverStatusPage.API_RECEIVER_STATUS, - ) + ? this.page.waitForRequest(AdminReceiverStatusPage.API_RECEIVER_STATUS) : Promise.resolve(); if (dateRange && isDateRangeDifferent) { const { value, inputMethod } = dateRange; await this.updateFilterDateRange(...value, inputMethod); } - if ( - receiverName != null && - receiverName !== this.filterFormInputs.receiverName.value - ) + if (receiverName != null && receiverName !== this.filterFormInputs.receiverName.value) await this.updateFilterReceiverName(receiverName); - if ( - resultMessage != null && - resultMessage !== this.filterFormInputs.resultMessage.value - ) + if (resultMessage != null && resultMessage !== this.filterFormInputs.resultMessage.value) await this.updateFilterResultMessage(resultMessage); - if ( - successType != null && - successType !== this.filterFormInputs.successType.value - ) + if (successType != null && successType !== this.filterFormInputs.successType.value) await this.updateFilterSuccessType(successType); if (!isRequestAwaitedBool) return undefined as void; @@ -376,11 +314,7 @@ export class AdminReceiverStatusPage extends BasePage { return reqUrl; } - async updateFilterDateRange( - start: Date, - end: Date, - inputMethod: "textbox" | "calendar" = "textbox", - ) { + async updateFilterDateRange(start: Date, end: Date, inputMethod: "textbox" | "calendar" = "textbox") { const { button, modalStartInput, @@ -438,10 +372,8 @@ export class AdminReceiverStatusPage extends BasePage { dateRange: { value: this.filterFormInputs.dateRange.expectedDefaultValue, }, - receiverName: - this.filterFormInputs.receiverName.expectedDefaultValue, - resultMessage: - this.filterFormInputs.resultMessage.expectedDefaultValue, + receiverName: this.filterFormInputs.receiverName.expectedDefaultValue, + resultMessage: this.filterFormInputs.resultMessage.expectedDefaultValue, successType: this.filterFormInputs.successType.expectedDefaultValue, }; return await this.updateFilters(resetValues); @@ -471,9 +403,7 @@ export class AdminReceiverStatusPage extends BasePage { async testReceiverStatusDisplay() { const [startDate, endDate] = this.filterFormInputs.dateRange.value; const statusRows = this.receiverStatusRowsLocator; - await expect(statusRows).toHaveCount( - new Set(this.receiverStatus?.map((r) => r.receiverId)).size, - ); + await expect(statusRows).toHaveCount(new Set(this.receiverStatus?.map((r) => r.receiverId)).size); const expectedDaysText = [ dateShortFormat(startDate), @@ -482,13 +412,7 @@ export class AdminReceiverStatusPage extends BasePage { ].join(" "); for (const [ i, - { - days, - successRate, - organizationName, - receiverName, - successRateType, - }, + { days, successRate, organizationName, receiverName, successRateType }, ] of this.timePeriodData.entries()) { const { title, display, days: daysLoc } = statusRows.nthCustom(i); @@ -497,9 +421,7 @@ export class AdminReceiverStatusPage extends BasePage { receiverName, successRate, ); - const expectedClass = new RegExp( - SUCCESS_RATE_CLASSNAME_MAP[successRateType], - ); + const expectedClass = new RegExp(SUCCESS_RATE_CLASSNAME_MAP[successRateType]); await expect(title).toBeVisible(); await expect(title).toHaveText(expectedTitleText); @@ -516,9 +438,7 @@ export class AdminReceiverStatusPage extends BasePage { for (const [i, { successRateType }] of timePeriods.entries()) { const sliceEle = daySlices.nth(i); - const expectedClass = new RegExp( - SUCCESS_RATE_CLASSNAME_MAP[successRateType], - ); + const expectedClass = new RegExp(SUCCESS_RATE_CLASSNAME_MAP[successRateType]); await expect(sliceEle).toBeVisible(); await expect(sliceEle).toHaveClass(expectedClass); diff --git a/frontend-react/e2e/pages/daily-data-details.ts b/frontend-react/e2e/pages/authenticated/daily-data-details.ts similarity index 90% rename from frontend-react/e2e/pages/daily-data-details.ts rename to frontend-react/e2e/pages/authenticated/daily-data-details.ts index 06d382632b9..ed98347e902 100644 --- a/frontend-react/e2e/pages/daily-data-details.ts +++ b/frontend-react/e2e/pages/authenticated/daily-data-details.ts @@ -1,8 +1,8 @@ -import { BasePage, BasePageTestArgs, type RouteHandlerFulfillEntry } from "./BasePage"; import { API_WATERS_REPORT, URL_REPORT_DETAILS } from "./report-details"; -import { RSDelivery, RSFacility } from "../../src/config/endpoints/deliveries"; -import { MOCK_GET_DELIVERY } from "../mocks/delivery"; -import { MOCK_GET_FACILITIES } from "../mocks/facilities"; +import { RSDelivery, RSFacility } from "../../../src/config/endpoints/deliveries"; +import { MOCK_GET_DELIVERY } from "../../mocks/delivery"; +import { MOCK_GET_FACILITIES } from "../../mocks/facilities"; +import { BasePage, BasePageTestArgs, type RouteHandlerFulfillEntry } from "../BasePage"; const id = "73e3cbc8-9920-4ab7-871f-843a1db4c074"; diff --git a/frontend-react/e2e/pages/daily-data.ts b/frontend-react/e2e/pages/authenticated/daily-data.ts similarity index 96% rename from frontend-react/e2e/pages/daily-data.ts rename to frontend-react/e2e/pages/authenticated/daily-data.ts index 647a20ede97..540b2eb6396 100644 --- a/frontend-react/e2e/pages/daily-data.ts +++ b/frontend-react/e2e/pages/authenticated/daily-data.ts @@ -1,10 +1,9 @@ import { expect, Page } from "@playwright/test"; import { format } from "date-fns"; -import { BasePage, BasePageTestArgs, type RouteHandlerFulfillEntry } from "./BasePage"; import { DailyDataDetailsPage } from "./daily-data-details"; import { API_WATERS_ORG } from "./report-details"; -import { RSReceiver } from "../../src/config/endpoints/settings"; -import { TEST_ORG_AK, TEST_ORG_AK_RECEIVER, TEST_ORG_IGNORE, TEST_ORG_UP_RECEIVER_UP } from "../helpers/utils"; +import { RSReceiver } from "../../../src/config/endpoints/settings"; +import { TEST_ORG_AK, TEST_ORG_AK_RECEIVER, TEST_ORG_IGNORE, TEST_ORG_UP_RECEIVER_UP } from "../../helpers/utils"; import { MOCK_GET_DELIVERIES_AK, MOCK_GET_DELIVERIES_AK_FILENAME, @@ -14,9 +13,10 @@ import { MOCK_GET_DELIVERIES_IGNORE_FILENAME, MOCK_GET_DELIVERIES_IGNORE_FULL_ELR, MOCK_GET_DELIVERIES_IGNORE_REPORT_ID, -} from "../mocks/deliveries"; -import { MOCK_GET_DELIVERY } from "../mocks/delivery"; -import { MOCK_GET_FACILITIES } from "../mocks/facilities"; +} from "../../mocks/deliveries"; +import { MOCK_GET_DELIVERY } from "../../mocks/delivery"; +import { MOCK_GET_FACILITIES } from "../../mocks/facilities"; +import { BasePage, BasePageTestArgs, type RouteHandlerFulfillEntry } from "../BasePage"; export class DailyDataPage extends BasePage { static readonly URL_DAILY_DATA = "/daily-data"; diff --git a/frontend-react/e2e/pages/authenticated/last-mile-failures.ts b/frontend-react/e2e/pages/authenticated/last-mile-failures.ts new file mode 100644 index 00000000000..ab8e38e5f92 --- /dev/null +++ b/frontend-react/e2e/pages/authenticated/last-mile-failures.ts @@ -0,0 +1,45 @@ +import { MOCK_GET_RESEND, MOCK_GET_SEND_FAILURES } from "../../mocks/lastMilefailures"; +import { BasePage, BasePageTestArgs, RouteHandlerFulfillEntry } from "../BasePage"; + +export class LastMileFailuresPage extends BasePage { + static readonly URL_LAST_MILE = "/admin/lastmile"; + static readonly API_GET_SEND_FAILURES = "/api/adm/getsendfailures?days_to_show=15"; + static readonly API_GET_RESEND = "/api/adm/getresend?days_to_show=15"; + + constructor(testArgs: BasePageTestArgs) { + super( + { + url: LastMileFailuresPage.URL_LAST_MILE, + title: "Last Mile Failures", + heading: testArgs.page.getByRole("heading", { + name: "Last Mile Failures", + }), + }, + testArgs, + ); + + this.addMockRouteHandlers([this.createMockGetSendFailuresHandler(), this.createMockGetResendHandler()]); + } + + createMockGetSendFailuresHandler(): RouteHandlerFulfillEntry { + return [ + LastMileFailuresPage.API_GET_SEND_FAILURES, + () => { + return { + json: MOCK_GET_SEND_FAILURES, + }; + }, + ]; + } + + createMockGetResendHandler(): RouteHandlerFulfillEntry { + return [ + LastMileFailuresPage.API_GET_RESEND, + () => { + return { + json: MOCK_GET_RESEND, + }; + }, + ]; + } +} diff --git a/frontend-react/e2e/pages/authenticated/message-details.ts b/frontend-react/e2e/pages/authenticated/message-details.ts new file mode 100644 index 00000000000..124831a1829 --- /dev/null +++ b/frontend-react/e2e/pages/authenticated/message-details.ts @@ -0,0 +1,34 @@ +import { MessageIDSearchPage } from "./message-id-search"; +import { MOCK_GET_MESSAGE } from "../../mocks/messages"; + +import { BasePage, BasePageTestArgs, RouteHandlerFulfillEntry } from "../BasePage"; + +export class MessageDetailsPage extends BasePage { + static readonly URL_MESSAGE_DETAILS = `/message-details/${MessageIDSearchPage.MESSAGE_ID}`; + + constructor(testArgs: BasePageTestArgs) { + super( + { + url: MessageDetailsPage.URL_MESSAGE_DETAILS, + title: "ReportStream - CDC's free, interoperable data transfer platform", + heading: testArgs.page.getByRole("heading", { + name: "Message ID Search", + }), + }, + testArgs, + ); + + this.addMockRouteHandlers([this.createMessageIDSearchAPIHandler()]); + } + + createMessageIDSearchAPIHandler(): RouteHandlerFulfillEntry { + return [ + MessageIDSearchPage.API_MESSAGE, + () => { + return { + json: MOCK_GET_MESSAGE, + }; + }, + ]; + } +} diff --git a/frontend-react/e2e/pages/authenticated/message-id-search.ts b/frontend-react/e2e/pages/authenticated/message-id-search.ts new file mode 100644 index 00000000000..d708f3f10c3 --- /dev/null +++ b/frontend-react/e2e/pages/authenticated/message-id-search.ts @@ -0,0 +1,46 @@ +import { MOCK_GET_MESSAGE, MOCK_GET_MESSAGES } from "../../mocks/messages"; +import { BasePage, BasePageTestArgs, RouteHandlerFulfillEntry } from "../BasePage"; + +export class MessageIDSearchPage extends BasePage { + static readonly URL_MESSAGE_ID_SEARCH = "/admin/message-tracker"; + static readonly API_MESSAGES = "**/api/messages?messageId=*"; + static readonly API_MESSAGE = "**/api/message/*"; + static readonly MESSAGE_ID = "582098"; + + constructor(testArgs: BasePageTestArgs) { + super( + { + url: MessageIDSearchPage.URL_MESSAGE_ID_SEARCH, + title: "Message ID search - Admin", + heading: testArgs.page.getByRole("heading", { + name: "Message ID Search", + }), + }, + testArgs, + ); + + this.addMockRouteHandlers([this.createMessageIDSearchAPIHandler(), this.createMessagesIDSearchAPIHandler()]); + } + + createMessageIDSearchAPIHandler(): RouteHandlerFulfillEntry { + return [ + MessageIDSearchPage.API_MESSAGE, + () => { + return { + json: MOCK_GET_MESSAGE, + }; + }, + ]; + } + + createMessagesIDSearchAPIHandler(): RouteHandlerFulfillEntry { + return [ + MessageIDSearchPage.API_MESSAGES, + () => { + return { + json: MOCK_GET_MESSAGES, + }; + }, + ]; + } +} diff --git a/frontend-react/e2e/pages/organization.ts b/frontend-react/e2e/pages/authenticated/organization.ts similarity index 61% rename from frontend-react/e2e/pages/organization.ts rename to frontend-react/e2e/pages/authenticated/organization.ts index bbac970dd64..6d24305989d 100644 --- a/frontend-react/e2e/pages/organization.ts +++ b/frontend-react/e2e/pages/authenticated/organization.ts @@ -1,10 +1,6 @@ -import { - BasePage, - BasePageTestArgs, - type RouteHandlerFulfillEntry, -} from "./BasePage"; -import { RSOrganizationSettings } from "../../src/config/endpoints/settings"; -import { MOCK_GET_ORGANIZATION_SETTINGS_LIST } from "../mocks/organizations"; +import { RSOrganizationSettings } from "../../../src/config/endpoints/settings"; +import { MOCK_GET_ORGANIZATION_SETTINGS_LIST } from "../../mocks/organizations"; +import { BasePage, BasePageTestArgs, type RouteHandlerFulfillEntry } from "../BasePage"; export class OrganizationPage extends BasePage { static readonly API_ORGANIZATIONS = "/api/settings/organizations"; @@ -13,7 +9,7 @@ export class OrganizationPage extends BasePage { super( { url: "/admin/settings", - title: "Organizations", + title: "Admin-Organizations", heading: testArgs.page.getByRole("heading", { name: "Organizations", }), @@ -23,19 +19,13 @@ export class OrganizationPage extends BasePage { this._organizationSettings = []; this.addResponseHandlers([ - [ - OrganizationPage.API_ORGANIZATIONS, - async (res) => (this._organizationSettings = await res.json()), - ], + [OrganizationPage.API_ORGANIZATIONS, async (res) => (this._organizationSettings = await res.json())], ]); this.addMockRouteHandlers([this.createMockOrganizationHandler()]); } get isPageLoadExpected() { - return ( - super.isPageLoadExpected && - this.testArgs.storageState === this.testArgs.adminLogin.path - ); + return super.isPageLoadExpected && this.testArgs.storageState === this.testArgs.adminLogin.path; } createMockOrganizationHandler(): RouteHandlerFulfillEntry { diff --git a/frontend-react/e2e/pages/report-details.ts b/frontend-react/e2e/pages/authenticated/report-details.ts similarity index 90% rename from frontend-react/e2e/pages/report-details.ts rename to frontend-react/e2e/pages/authenticated/report-details.ts index 01f342b9383..757335b0c7d 100644 --- a/frontend-react/e2e/pages/report-details.ts +++ b/frontend-react/e2e/pages/authenticated/report-details.ts @@ -1,8 +1,8 @@ import { expect, Page } from "@playwright/test"; import fs from "node:fs"; -import { MOCK_GET_DELIVERY } from "../mocks/delivery"; -import { MOCK_GET_HISTORY_REPORT } from "../mocks/historyReport"; -import { MOCK_GET_SUBMISSION_HISTORY } from "../mocks/submissionHistory"; +import { MOCK_GET_DELIVERY } from "../../mocks/delivery"; +import { MOCK_GET_HISTORY_REPORT } from "../../mocks/historyReport"; +import { MOCK_GET_SUBMISSION_HISTORY } from "../../mocks/submissionHistory"; export const URL_REPORT_DETAILS = "/report-details"; export const API_WATERS_REPORT = "**/api/waters/report"; diff --git a/frontend-react/e2e/pages/submission-history.ts b/frontend-react/e2e/pages/authenticated/submission-history.ts similarity index 71% rename from frontend-react/e2e/pages/submission-history.ts rename to frontend-react/e2e/pages/authenticated/submission-history.ts index e65b4792397..f09dad1bf5a 100644 --- a/frontend-react/e2e/pages/submission-history.ts +++ b/frontend-react/e2e/pages/authenticated/submission-history.ts @@ -1,6 +1,6 @@ import { expect, Page } from "@playwright/test"; -import { MOCK_GET_SUBMISSION_HISTORY } from "../mocks/submissionHistory"; -import { MOCK_GET_SUBMISSIONS } from "../mocks/submissions"; +import { MOCK_GET_SUBMISSION_HISTORY } from "../../mocks/submissionHistory"; +import { MOCK_GET_SUBMISSIONS } from "../../mocks/submissions"; export const URL_SUBMISSION_HISTORY = "/submissions"; export const API_GET_REPORT_HISTORY = `**/api/waters/report/**`; @@ -21,11 +21,7 @@ export function getOrgSubmissionsAPI(org: string) { return `**/api/waters/org/${org}/submissions?*`; } -export async function mockGetSubmissionsResponse( - page: Page, - org: string, - responseStatus = 200, -) { +export async function mockGetSubmissionsResponse(page: Page, org: string, responseStatus = 200) { const submissionsApi = getOrgSubmissionsAPI(org); await page.route(submissionsApi, async (route) => { const json = MOCK_GET_SUBMISSIONS; @@ -33,10 +29,7 @@ export async function mockGetSubmissionsResponse( }); } -export async function mockGetReportHistoryResponse( - page: Page, - responseStatus = 200, -) { +export async function mockGetReportHistoryResponse(page: Page, responseStatus = 200) { await page.route(API_GET_REPORT_HISTORY, async (route) => { const json = MOCK_GET_SUBMISSION_HISTORY; await route.fulfill({ json, status: responseStatus }); @@ -49,33 +42,21 @@ export async function openReportIdDetailPage(page: Page, id: string) { } export async function title(page: Page) { - await expect(page).toHaveTitle( - /ReportStream - CDC's free, interoperable data transfer platform/, - ); + await expect(page).toHaveTitle(/ReportStream - CDC's free, interoperable data transfer platform/); } export async function tableHeaders(page: Page) { await expect(page.locator(".usa-table th").nth(0)).toHaveText(/Report ID/); - await expect(page.locator(".usa-table th").nth(1)).toHaveText( - "Date/time submitted", - ); + await expect(page.locator(".usa-table th").nth(1)).toHaveText("Date/time submitted"); await expect(page.locator(".usa-table th").nth(2)).toHaveText(/File/); await expect(page.locator(".usa-table th").nth(3)).toHaveText(/Records/); await expect(page.locator(".usa-table th").nth(4)).toHaveText(/Status/); } -export async function breadcrumbLink( - page: Page, - index: number, - linkName: string, - expectedUrl: string, -) { +export async function breadcrumbLink(page: Page, index: number, linkName: string, expectedUrl: string) { const breadcrumbLinks = page.locator(".usa-breadcrumb ol li"); await expect(breadcrumbLinks.nth(index)).toHaveText(linkName); - await breadcrumbLinks - .nth(index) - .getByRole("link", { name: linkName }) - .click(); + await breadcrumbLinks.nth(index).getByRole("link", { name: linkName }).click(); await expect(page.locator("h1")).toBeAttached(); await expect(page).toHaveURL(expectedUrl); } diff --git a/frontend-react/e2e/pages/homepage.ts b/frontend-react/e2e/pages/homepage.ts deleted file mode 100644 index 5a22616b8ba..00000000000 --- a/frontend-react/e2e/pages/homepage.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Page } from "@playwright/test"; - -export async function goto(page: Page) { - await page.goto("/", { - waitUntil: "domcontentloaded", - }); -} diff --git a/frontend-react/e2e/pages/last-mile-failures.ts b/frontend-react/e2e/pages/last-mile-failures.ts deleted file mode 100644 index 86baae6ac4f..00000000000 --- a/frontend-react/e2e/pages/last-mile-failures.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Page } from "@playwright/test"; -import { - MOCK_GET_RESEND, - MOCK_GET_SEND_FAILURES, -} from "../mocks/lastMilefailures"; - -const URL_LAST_MILE = "/admin/lastmile"; -const API_GET_RESEND = "/api/adm/getresend?days_to_show=15"; -export const API_GET_SEND_FAILURES = "/api/adm/getsendfailures?days_to_show=15"; - -export async function goto(page: Page) { - await page.goto(URL_LAST_MILE, { - waitUntil: "domcontentloaded", - }); -} - -export async function mockGetSendFailuresResponse( - page: Page, - responseStatus = 200, -) { - await page.route(API_GET_SEND_FAILURES, async (route) => { - const json = MOCK_GET_SEND_FAILURES; - await route.fulfill({ json, status: responseStatus }); - }); -} - -export async function mockGetResendResponse(page: Page) { - await page.route(API_GET_RESEND, async (route) => { - const json = MOCK_GET_RESEND; - await route.fulfill({ json }); - }); -} diff --git a/frontend-react/e2e/pages/message-details.ts b/frontend-react/e2e/pages/message-details.ts deleted file mode 100644 index f989d00d1f7..00000000000 --- a/frontend-react/e2e/pages/message-details.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Page } from "@playwright/test"; - -import { MESSAGE_ID } from "../pages/message-id-search"; - -export const URL_MESSAGE_DETAILS = `/message-details/${MESSAGE_ID}`; - -export async function goto(page: Page) { - await page.goto(URL_MESSAGE_DETAILS, { - waitUntil: "domcontentloaded", - }); -} diff --git a/frontend-react/e2e/pages/message-id-search.ts b/frontend-react/e2e/pages/message-id-search.ts deleted file mode 100644 index a71a877b395..00000000000 --- a/frontend-react/e2e/pages/message-id-search.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Page } from "@playwright/test"; - -export const URL_MESSAGE_ID_SEARCH = "/admin/message-tracker"; -export const API_MESSAGES = "**/api/messages?messageId=*"; -export const API_MESSAGE = "**/api/message/*"; - -export const MESSAGE_ID = "582098"; - -export async function goto(page: Page) { - await page.goto(URL_MESSAGE_ID_SEARCH, { - waitUntil: "domcontentloaded", - }); -} diff --git a/frontend-react/e2e/pages/our-network.ts b/frontend-react/e2e/pages/our-network.ts deleted file mode 100644 index 7a8f3d7dbc7..00000000000 --- a/frontend-react/e2e/pages/our-network.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { expect, Page } from "@playwright/test"; - -const URL_OUR_NETWORK = "/about/our-network"; -export async function goto(page: Page) { - await page.goto(URL_OUR_NETWORK, { - waitUntil: "domcontentloaded", - }); -} -export async function onLoad(page: Page) { - await expect(page).toHaveURL(/our-network/); - await expect(page).toHaveTitle(/Our network/); -} - -export async function clickOnLiveMap(page: Page) { - await page.getByTestId("map").click(); - await expect(page).toHaveURL(URL_OUR_NETWORK); -} diff --git a/frontend-react/e2e/pages/public-pages-link-check.ts b/frontend-react/e2e/pages/public-pages-link-check.ts deleted file mode 100644 index 64224aad02d..00000000000 --- a/frontend-react/e2e/pages/public-pages-link-check.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Page } from "@playwright/test"; - -export async function publicPageGoto(page: Page, path: string) { - await page.goto(path, { - waitUntil: "networkidle", - }); -} diff --git a/frontend-react/e2e/pages/about.ts b/frontend-react/e2e/pages/public/about/about.ts similarity index 85% rename from frontend-react/e2e/pages/about.ts rename to frontend-react/e2e/pages/public/about/about.ts index 2189c703f39..c1c0d3b6082 100644 --- a/frontend-react/e2e/pages/about.ts +++ b/frontend-react/e2e/pages/public/about/about.ts @@ -1,4 +1,4 @@ -import { BasePage, BasePageTestArgs } from "./BasePage"; +import { BasePage, BasePageTestArgs } from "../../BasePage"; export class AboutPage extends BasePage { constructor(testArgs: BasePageTestArgs) { diff --git a/frontend-react/e2e/pages/public/about/our-network.ts b/frontend-react/e2e/pages/public/about/our-network.ts new file mode 100644 index 00000000000..c36c21b8b87 --- /dev/null +++ b/frontend-react/e2e/pages/public/about/our-network.ts @@ -0,0 +1,16 @@ +import { BasePage, BasePageTestArgs } from "../../BasePage"; + +export class OurNetworkPage extends BasePage { + constructor(testArgs: BasePageTestArgs) { + super( + { + url: "/about/our-network", + title: "Our network - ReportStream", + heading: testArgs.page.getByRole("heading", { + name: "Our network", + }), + }, + testArgs, + ); + } +} diff --git a/frontend-react/e2e/pages/public/about/roadmap.ts b/frontend-react/e2e/pages/public/about/roadmap.ts new file mode 100644 index 00000000000..80c6227940f --- /dev/null +++ b/frontend-react/e2e/pages/public/about/roadmap.ts @@ -0,0 +1,16 @@ +import { BasePage, BasePageTestArgs } from "../../BasePage"; + +export class RoadmapPage extends BasePage { + constructor(testArgs: BasePageTestArgs) { + super( + { + url: "/about/roadmap", + title: "Product roadmap", + heading: testArgs.page.getByRole("heading", { + name: "Product roadmap", + }), + }, + testArgs, + ); + } +} diff --git a/frontend-react/e2e/pages/security.ts b/frontend-react/e2e/pages/public/about/security.ts similarity index 91% rename from frontend-react/e2e/pages/security.ts rename to frontend-react/e2e/pages/public/about/security.ts index 8294a43283e..5d494e97795 100644 --- a/frontend-react/e2e/pages/security.ts +++ b/frontend-react/e2e/pages/public/about/security.ts @@ -1,5 +1,5 @@ import { expect, Page } from "@playwright/test"; -import { BasePage, BasePageTestArgs } from "./BasePage"; +import { BasePage, BasePageTestArgs } from "../../BasePage"; export class SecurityPage extends BasePage { constructor(testArgs: BasePageTestArgs) { diff --git a/frontend-react/e2e/pages/getting-started/receiving-data.ts b/frontend-react/e2e/pages/public/getting-started/receiving-data.ts similarity index 88% rename from frontend-react/e2e/pages/getting-started/receiving-data.ts rename to frontend-react/e2e/pages/public/getting-started/receiving-data.ts index d87fb8acdb7..3ccd227e24f 100644 --- a/frontend-react/e2e/pages/getting-started/receiving-data.ts +++ b/frontend-react/e2e/pages/public/getting-started/receiving-data.ts @@ -1,4 +1,4 @@ -import { BasePage, BasePageTestArgs } from "../BasePage"; +import { BasePage, BasePageTestArgs } from "../../BasePage"; export class ReceivingDataPage extends BasePage { constructor(testArgs: BasePageTestArgs) { diff --git a/frontend-react/e2e/pages/getting-started/sending-data.ts b/frontend-react/e2e/pages/public/getting-started/sending-data.ts similarity index 88% rename from frontend-react/e2e/pages/getting-started/sending-data.ts rename to frontend-react/e2e/pages/public/getting-started/sending-data.ts index 9bb57acf9a5..47aa28823d5 100644 --- a/frontend-react/e2e/pages/getting-started/sending-data.ts +++ b/frontend-react/e2e/pages/public/getting-started/sending-data.ts @@ -1,4 +1,4 @@ -import { BasePage, BasePageTestArgs } from "../BasePage"; +import { BasePage, BasePageTestArgs } from "../../BasePage"; export class SendingDataPage extends BasePage { constructor(testArgs: BasePageTestArgs) { diff --git a/frontend-react/e2e/pages/header.ts b/frontend-react/e2e/pages/public/header.ts similarity index 63% rename from frontend-react/e2e/pages/header.ts rename to frontend-react/e2e/pages/public/header.ts index b4929379008..1ed78a50c6e 100644 --- a/frontend-react/e2e/pages/header.ts +++ b/frontend-react/e2e/pages/public/header.ts @@ -6,11 +6,7 @@ export async function clickOnHome(page: Page) { } export async function clickOnAbout(page: Page) { - await page - .getByTestId("header") - .getByTestId("navDropDownButton") - .getByText("About") - .click(); + await page.getByTestId("header").getByTestId("navDropDownButton").getByText("About").click(); expect(page.getByText("About ReportStream")).toBeTruthy(); expect(page.getByText("Our network")).toBeTruthy(); @@ -21,34 +17,22 @@ export async function clickOnAbout(page: Page) { } export async function clickOnGettingStarted(page: Page) { - await page - .getByTestId("header") - .getByRole("link", { name: "Getting started" }) - .click(); + await page.getByTestId("header").getByRole("link", { name: "Getting started" }).click(); await expect(page).toHaveURL(/getting-started/); } export async function clickOnDevelopers(page: Page) { - await page - .getByTestId("header") - .getByRole("link", { name: "Developers" }) - .click(); + await page.getByTestId("header").getByRole("link", { name: "Developers" }).click(); await expect(page).toHaveURL(/.*developer-resources/); } export async function clickOnYourConnection(page: Page) { - await page - .getByTestId("header") - .getByRole("link", { name: "Your Connection" }) - .click(); + await page.getByTestId("header").getByRole("link", { name: "Your Connection" }).click(); await expect(page).toHaveURL(/.*managing-your-connection/); } export async function clickOnSupport(page: Page) { - await page - .getByTestId("header") - .getByRole("link", { name: "Support" }) - .click(); + await page.getByTestId("header").getByRole("link", { name: "Support" }).click(); await expect(page).toHaveURL(/.*support/); } diff --git a/frontend-react/e2e/pages/public/homepage.ts b/frontend-react/e2e/pages/public/homepage.ts new file mode 100644 index 00000000000..994f14f5116 --- /dev/null +++ b/frontend-react/e2e/pages/public/homepage.ts @@ -0,0 +1,17 @@ +import { BasePage, BasePageTestArgs } from "../BasePage"; + +export class HomePage extends BasePage { + constructor(testArgs: BasePageTestArgs) { + super( + { + url: "/", + title: "ReportStream - CDC's free, interoperable data transfer platform", + heading: testArgs.page.getByRole("heading", { + name: "CDC’s free, single connection to streamline your data transfer and improve public health", + exact: true, + }), + }, + testArgs, + ); + } +} diff --git a/frontend-react/e2e/pages/managing-your-connection.ts b/frontend-react/e2e/pages/public/managing-your-connection/managing-your-connection.ts similarity index 91% rename from frontend-react/e2e/pages/managing-your-connection.ts rename to frontend-react/e2e/pages/public/managing-your-connection/managing-your-connection.ts index 6098bec9e63..31427cb506b 100644 --- a/frontend-react/e2e/pages/managing-your-connection.ts +++ b/frontend-react/e2e/pages/public/managing-your-connection/managing-your-connection.ts @@ -1,5 +1,5 @@ import { expect, Page } from "@playwright/test"; -import { BasePage, BasePageTestArgs } from "./BasePage"; +import { BasePage, BasePageTestArgs } from "../../BasePage"; export async function onLoad(page: Page) { await expect(page).toHaveURL(/managing-your-connection/); diff --git a/frontend-react/e2e/pages/refer-healthcare.ts b/frontend-react/e2e/pages/public/managing-your-connection/refer-healthcare.ts similarity index 88% rename from frontend-react/e2e/pages/refer-healthcare.ts rename to frontend-react/e2e/pages/public/managing-your-connection/refer-healthcare.ts index b86152b914f..a5805d5a9c0 100644 --- a/frontend-react/e2e/pages/refer-healthcare.ts +++ b/frontend-react/e2e/pages/public/managing-your-connection/refer-healthcare.ts @@ -1,4 +1,4 @@ -import { BasePage, BasePageTestArgs } from "./BasePage"; +import { BasePage, BasePageTestArgs } from "../../BasePage"; export class ReferHealthcarePage extends BasePage { constructor(testArgs: BasePageTestArgs) { diff --git a/frontend-react/e2e/pages/public/resources.ts b/frontend-react/e2e/pages/public/resources.ts new file mode 100644 index 00000000000..5accb844500 --- /dev/null +++ b/frontend-react/e2e/pages/public/resources.ts @@ -0,0 +1,17 @@ +import { BasePage, BasePageTestArgs } from "../BasePage"; + +export class DeveloperResourcesPage extends BasePage { + constructor(testArgs: BasePageTestArgs) { + super( + { + url: "/developer-resources", + title: "ReportStream developer resources", + heading: testArgs.page.getByRole("heading", { + name: "Developer resources", + exact: true, + }), + }, + testArgs, + ); + } +} diff --git a/frontend-react/e2e/pages/support.ts b/frontend-react/e2e/pages/public/support.ts similarity index 87% rename from frontend-react/e2e/pages/support.ts rename to frontend-react/e2e/pages/public/support.ts index ae5bc223708..119815da6b2 100644 --- a/frontend-react/e2e/pages/support.ts +++ b/frontend-react/e2e/pages/public/support.ts @@ -1,4 +1,4 @@ -import { BasePage, BasePageTestArgs } from "./BasePage"; +import { BasePage, BasePageTestArgs } from "../BasePage"; export class SupportPage extends BasePage { constructor(testArgs: BasePageTestArgs) { diff --git a/frontend-react/e2e/pages/resources.ts b/frontend-react/e2e/pages/resources.ts deleted file mode 100644 index c96baa6d17f..00000000000 --- a/frontend-react/e2e/pages/resources.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Page } from "@playwright/test"; - -export async function goto(page: Page) { - await page.goto("/developer-resources", { - waitUntil: "domcontentloaded", - }); -} diff --git a/frontend-react/e2e/pages/roadmap.ts b/frontend-react/e2e/pages/roadmap.ts deleted file mode 100644 index af49e5aefa5..00000000000 --- a/frontend-react/e2e/pages/roadmap.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Page } from "@playwright/test"; - -export const URL_ROADMAP = "/about/roadmap"; -export async function goto(page: Page) { - await page.goto(URL_ROADMAP, { - waitUntil: "domcontentloaded", - }); -} diff --git a/frontend-react/e2e/spec/all/admin/receiver-status-page.spec.ts b/frontend-react/e2e/spec/all/authenticated/admin/receiver-status-page.spec.ts similarity index 95% rename from frontend-react/e2e/spec/all/admin/receiver-status-page.spec.ts rename to frontend-react/e2e/spec/all/authenticated/admin/receiver-status-page.spec.ts index 33fcf977608..f8f92d2615b 100644 --- a/frontend-react/e2e/spec/all/admin/receiver-status-page.spec.ts +++ b/frontend-react/e2e/spec/all/authenticated/admin/receiver-status-page.spec.ts @@ -1,10 +1,10 @@ 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/admin/receiver-status"; -import { test as baseTest, expect, logins } from "../../../test"; +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"; export interface AdminReceiverStatusPageFixtures { adminReceiverStatusPage: AdminReceiverStatusPage; @@ -71,23 +71,20 @@ test.describe("Admin Receiver Status Page", () => { await expect(adminReceiverStatusPage.page.getByText("there was an error")).toBeVisible(); }); - test( - "Has correct title", - { - tag: "@smoke", - }, - async ({ adminReceiverStatusPage }) => { - await expect(adminReceiverStatusPage.page).toHaveURL(adminReceiverStatusPage.url); - await expect(adminReceiverStatusPage.page).toHaveTitle(adminReceiverStatusPage.title); - }, - ); + test.describe("Header", () => { + test( + "has correct title + heading", + { + tag: "@smoke", + }, + async ({ adminReceiverStatusPage }) => { + await adminReceiverStatusPage.testHeader(); + }, + ); + }); test.describe("When there is no error", () => { test.describe("Displays correctly", () => { - test("header", async ({ adminReceiverStatusPage }) => { - await expect(adminReceiverStatusPage.heading).toBeVisible(); - }); - test.describe( "filters", { @@ -153,6 +150,14 @@ test.describe("Admin Receiver Status Page", () => { }); }); + test.describe("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( diff --git a/frontend-react/e2e/spec/all/daily-data-details-page.spec.ts b/frontend-react/e2e/spec/all/authenticated/daily-data-details-page.spec.ts similarity index 96% rename from frontend-react/e2e/spec/all/daily-data-details-page.spec.ts rename to frontend-react/e2e/spec/all/authenticated/daily-data-details-page.spec.ts index 0d8930c39a7..bf2dc36b129 100644 --- a/frontend-react/e2e/spec/all/daily-data-details-page.spec.ts +++ b/frontend-react/e2e/spec/all/authenticated/daily-data-details-page.spec.ts @@ -1,9 +1,9 @@ import { expect } from "@playwright/test"; -import { tableDataCellValue } from "../../helpers/utils"; -import { detailsTableHeaders } from "../../pages/daily-data"; -import { DailyDataDetailsPage } from "../../pages/daily-data-details"; -import * as reportDetails from "../../pages/report-details"; -import { test as baseTest } from "../../test"; +import { tableDataCellValue } from "../../../helpers/utils"; +import { detailsTableHeaders } from "../../../pages/authenticated/daily-data"; +import { DailyDataDetailsPage } from "../../../pages/authenticated/daily-data-details"; +import * as reportDetails from "../../../pages/authenticated/report-details"; +import { test as baseTest } from "../../../test"; export interface DailyDataDetailsPageFixtures { dailyDataDetailsPage: DailyDataDetailsPage; diff --git a/frontend-react/e2e/spec/all/daily-data-page.spec.ts b/frontend-react/e2e/spec/all/authenticated/daily-data-page.spec.ts similarity index 99% rename from frontend-react/e2e/spec/all/daily-data-page.spec.ts rename to frontend-react/e2e/spec/all/authenticated/daily-data-page.spec.ts index 71a59e3f0d0..f6b4e05803d 100644 --- a/frontend-react/e2e/spec/all/daily-data-page.spec.ts +++ b/frontend-react/e2e/spec/all/authenticated/daily-data-page.spec.ts @@ -8,8 +8,8 @@ import { TEST_ORG_AK_RECEIVER, TEST_ORG_IGNORE, TEST_ORG_UP_RECEIVER_UP, -} from "../../helpers/utils"; -import * as dailyData from "../../pages/daily-data"; +} from "../../../helpers/utils"; +import * as dailyData from "../../../pages/authenticated/daily-data.js"; import { applyButton, DailyDataPage, @@ -28,8 +28,8 @@ import { startTime, startTimeClear, tableHeaders, -} from "../../pages/daily-data"; -import { test as baseTest } from "../../test"; +} from "../../../pages/authenticated/daily-data.js"; +import { test as baseTest } from "../../../test"; const defaultStartTime = "9:00am"; const defaultEndTime = "11:00pm"; diff --git a/frontend-react/e2e/spec/all/authenticated/last-mile-failures-page.spec.ts b/frontend-react/e2e/spec/all/authenticated/last-mile-failures-page.spec.ts new file mode 100644 index 00000000000..df8503376bb --- /dev/null +++ b/frontend-react/e2e/spec/all/authenticated/last-mile-failures-page.spec.ts @@ -0,0 +1,115 @@ +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; +} + +const test = baseTest.extend({ + lastMileFailuresPage: async ( + { + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + frontendWarningsLogPath, + isFrontendWarningsLog, + }, + use, + ) => { + const page = new LastMileFailuresPage({ + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + frontendWarningsLogPath, + isFrontendWarningsLog, + }); + await page.goto(); + await use(page); + }, +}); + +test.describe("Last Mile Failure page", () => { + test.describe("admin user - happy path", () => { + test.use({ storageState: "e2e/.auth/admin.json" }); + + test.describe("Header", () => { + test("has correct title + heading", async ({ lastMileFailuresPage }) => { + await lastMileFailuresPage.testHeader(); + }); + }); + + test("table has correct headers", async ({ lastMileFailuresPage }) => { + await expect(lastMileFailuresPage.page.locator(".column-header-text").nth(0)).toHaveText(/Failed At/); + await expect(lastMileFailuresPage.page.locator(".column-header-text").nth(1)).toHaveText(/ReportId/); + await expect(lastMileFailuresPage.page.locator(".column-header-text").nth(2)).toHaveText(/Receiver/); + }); + + test("table column 'Failed At' has expected data", async ({ lastMileFailuresPage }) => { + await expect(tableRows(lastMileFailuresPage.page).nth(0).locator("td").nth(0)).toHaveText( + "Tue, 2/20/2024, 9:35 PM", + ); + }); + + test("table column 'ReportId' will open a modal with report details", async ({ lastMileFailuresPage }) => { + const reportId = tableRows(lastMileFailuresPage.page).nth(0).locator("td").nth(1); + await expect(reportId).toContainText(/e5ce49c0-b230-4364-8230-964273249fa1/); + await reportId.click(); + + const modal = lastMileFailuresPage.page.getByTestId("modalWindow").nth(0); + await expect(modal).toContainText(/Report ID:e5ce49c0-b230-4364-8230-964273249fa1/); + }); + + test("table column 'Receiver' will open receiver edit page", async ({ lastMileFailuresPage }) => { + const receiver = tableRows(lastMileFailuresPage.page).nth(0).locator("td").nth(2); + await expect(receiver).toContainText(/flexion.etor-service-receiver-results/); + await receiver.click(); + + await expect(lastMileFailuresPage.page).toHaveURL( + "/admin/orgreceiversettings/org/flexion/receiver/etor-service-receiver-results/action/edit", + ); + }); + }); + + test.describe("admin user - server error", () => { + test.use({ storageState: "e2e/.auth/admin.json" }); + + test("has alert", async ({ lastMileFailuresPage }) => { + lastMileFailuresPage.mockError = true; + await lastMileFailuresPage.reload(); + + await expect(lastMileFailuresPage.page.getByTestId("alert")).toBeAttached(); + await expect( + lastMileFailuresPage.page.getByText(/Our apologies, there was an error loading this content./), + ).toBeAttached(); + }); + }); + + test.describe("receiver user", () => { + test.use({ storageState: "e2e/.auth/receiver.json" }); + + test("returns Page Not Found", async ({ lastMileFailuresPage }) => { + await expect(lastMileFailuresPage.page).toHaveTitle(/Page Not Found/); + }); + }); + + test.describe("sender user", () => { + test.use({ storageState: "e2e/.auth/sender.json" }); + + test("returns Page Not Found", async ({ lastMileFailuresPage }) => { + await expect(lastMileFailuresPage.page).toHaveTitle(/Page Not Found/); + }); + }); + + test.describe("Footer", () => { + test("has footer and explicit scroll to footer and scroll to top", async ({ lastMileFailuresPage }) => { + await lastMileFailuresPage.testFooter(); + }); + }); +}); diff --git a/frontend-react/e2e/spec/all/authenticated/message-details-page.spec.ts b/frontend-react/e2e/spec/all/authenticated/message-details-page.spec.ts new file mode 100644 index 00000000000..916b14b1096 --- /dev/null +++ b/frontend-react/e2e/spec/all/authenticated/message-details-page.spec.ts @@ -0,0 +1,187 @@ +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"; + +export interface MessageDetailsPageFixtures { + messageDetailsPage: MessageDetailsPage; +} + +const test = baseTest.extend({ + messageDetailsPage: async ( + { + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + frontendWarningsLogPath, + isFrontendWarningsLog, + }, + use, + ) => { + const page = new MessageDetailsPage({ + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + frontendWarningsLogPath, + isFrontendWarningsLog, + }); + await page.goto(); + await use(page); + }, +}); + +test.describe("Message Details Page", () => { + test.describe("not authenticated", () => { + test("redirects to login", async ({ messageDetailsPage }) => { + await expect(messageDetailsPage.page).toHaveURL("/login"); + }); + }); + + test.describe("authenticated admin", () => { + test.use({ storageState: "e2e/.auth/admin.json" }); + + test.describe("Header", () => { + test("has correct title + heading", async ({ messageDetailsPage }) => { + await messageDetailsPage.testHeader(); + }); + }); + + test("has message id section", async ({ messageDetailsPage }) => { + await expect(messageDetailsPage.page.getByText("Message ID", { exact: true })).toBeVisible(); + await expect(messageDetailsPage.page.getByText(MessageIDSearchPage.MESSAGE_ID)).toBeVisible(); + }); + + test("has sender section", async ({ messageDetailsPage }) => { + const { sender, reportId, submittedDate } = MOCK_GET_MESSAGE; + + await expect(messageDetailsPage.page.getByText("Sender:")).toBeVisible(); + await expect(messageDetailsPage.page.getByText(sender)).toBeVisible(); + await expect(messageDetailsPage.page.getByText("Incoming Report ID")).toBeVisible(); + await expect(messageDetailsPage.page.getByText(reportId, { exact: true })).toBeVisible(); + await expect(messageDetailsPage.page.getByText("Date/Time Submitted")).toBeVisible(); + await expect(messageDetailsPage.page.getByText(new Date(submittedDate).toLocaleString())).toBeVisible(); + await expect(messageDetailsPage.page.getByText("File Location")).toBeVisible(); + await expect(messageDetailsPage.page.getByText("RECEIVE", { exact: true })).toBeVisible(); + await expect(messageDetailsPage.page.getByText("ignore.ignore-simple-report")).toBeVisible(); + await expect(messageDetailsPage.page.getByText("Incoming File Name")).toBeVisible(); + await expect( + messageDetailsPage.page.getByText( + "pdi-covid-19-d9a57df0-2702-4e28-9d80-ff8c9ec51816-20240514142655.csv", + ), + ).toBeVisible(); + }); + + test.describe("authenticated admin", () => { + test("displays expected table headers and data", async ({ messageDetailsPage }) => { + // include header row + const rowCount = MOCK_GET_MESSAGE.receiverData.length + 1; + const table = messageDetailsPage.page.getByRole("table"); + await expect(table).toBeVisible(); + const rows = await table.getByRole("row").all(); + expect(rows).toHaveLength(rowCount); + + const colHeaders = [ + "Name", + "Service", + "Date", + "Report Id", + "Main", + "Sub", + "File Name", + "Transport Results", + ]; + for (const [i, row] of rows.entries()) { + const cols = await row.getByRole("cell").allTextContents(); + expect(cols).toHaveLength(colHeaders.length); + + const { receivingOrg, receivingOrgSvc, createdAt, reportId, fileUrl, transportResult } = + i === 0 + ? MOCK_GET_MESSAGE.receiverData[0] + : (MOCK_GET_MESSAGE.receiverData.find((i) => i.reportId === cols[3]) ?? { + reportId: "INVALID", + }); + + // if first row, we expect column headers. else, the data row matching the report id + const expectedColContents = + i === 0 + ? colHeaders + : [ + receivingOrg ?? "", + receivingOrgSvc ?? "", + createdAt ? new Date(createdAt).toLocaleString() : "", + reportId, + parseFileLocation(fileUrl ?? "N/A").folderLocation.toLocaleUpperCase(), + parseFileLocation(fileUrl ?? "N/A").sendingOrg, + parseFileLocation(fileUrl ?? "N/A").fileName, + transportResult ?? "", + ]; + + for (const [i, col] of cols.entries()) { + expect(col).toBe(expectedColContents[i]); + } + } + }); + + test("table column 'FileName' will download file", async ({ messageDetailsPage }) => { + const downloadProm = messageDetailsPage.page.waitForEvent("download"); + await mockGetHistoryReportResponse(messageDetailsPage.page, "*"); + + await tableRows(messageDetailsPage.page).nth(0).locator("td").nth(6).getByRole("button").click(); + + const download = await downloadProm; + + // assert filename + expect(download.suggestedFilename()).toBe( + "hhsprotect-covid-19-73e3cbc8-9920-4ab7-871f-843a1db4c074.csv", + ); + // get and assert stats + expect((await fs.promises.stat(await download.path())).size).toBeGreaterThan(200); + }); + }); + + test.describe("Footer", () => { + test("has footer and explicit scroll to footer and scroll to top", async ({ messageDetailsPage }) => { + await messageDetailsPage.testFooter(); + }); + }); + }); + + test.describe("receiver user", () => { + test.use({ storageState: "e2e/.auth/receiver.json" }); + + test("has alert", async ({ messageDetailsPage }) => { + messageDetailsPage.mockError = true; + await messageDetailsPage.reload(); + + await expect(messageDetailsPage.page.getByTestId("alert")).toBeAttached(); + await expect( + messageDetailsPage.page.getByText(/Our apologies, there was an error loading this content./), + ).toBeAttached(); + }); + }); + + test.describe("sender user", () => { + test.use({ storageState: "e2e/.auth/sender.json" }); + + test("has alert", async ({ messageDetailsPage }) => { + messageDetailsPage.mockError = true; + await messageDetailsPage.reload(); + + await expect(messageDetailsPage.page.getByTestId("alert")).toBeAttached(); + await expect( + messageDetailsPage.page.getByText(/Our apologies, there was an error loading this content./), + ).toBeAttached(); + }); + }); +}); diff --git a/frontend-react/e2e/spec/all/authenticated/message-id-search-page.spec.ts b/frontend-react/e2e/spec/all/authenticated/message-id-search-page.spec.ts new file mode 100644 index 00000000000..9de8e807739 --- /dev/null +++ b/frontend-react/e2e/spec/all/authenticated/message-id-search-page.spec.ts @@ -0,0 +1,177 @@ +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"; + +export interface MessageIDSearchPageFixtures { + messageIDSearchPage: MessageIDSearchPage; +} + +const test = baseTest.extend({ + messageIDSearchPage: async ( + { + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + frontendWarningsLogPath, + isFrontendWarningsLog, + }, + use, + ) => { + const page = new MessageIDSearchPage({ + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + frontendWarningsLogPath, + isFrontendWarningsLog, + }); + await page.goto(); + await use(page); + }, +}); + +test.describe("Message ID Search Page", () => { + test.describe("not authenticated", () => { + test("redirects to login", async ({ messageIDSearchPage }) => { + await expect(messageIDSearchPage.page).toHaveURL("/login"); + }); + }); + + test.describe("authenticated admin", () => { + test.use({ storageState: "e2e/.auth/admin.json" }); + test.beforeEach(async ({ messageIDSearchPage }) => { + await messageIDSearchPage.page.locator("#search-field").fill(MessageIDSearchPage.MESSAGE_ID); + await messageIDSearchPage.page + .getByRole("button", { + name: "Search", + }) + .click(); + }); + + test.describe("on search with results", () => { + test.describe("Header", () => { + test("has correct title + heading", async ({ messageIDSearchPage }) => { + await messageIDSearchPage.testHeader(); + }); + }); + + test("displays expected table headers and data", async ({ messageIDSearchPage }) => { + // include header row + const rowCount = MOCK_GET_MESSAGES.length + 1; + const table = messageIDSearchPage.page.getByRole("table"); + await expect(table).toBeVisible(); + const rows = await table.getByRole("row").all(); + expect(rows).toHaveLength(rowCount); + + const colHeaders = ["Message ID", "Sender", "Date/time submitted", "Incoming Report Id"]; + for (const [i, row] of rows.entries()) { + const cols = await row.getByRole("cell").allTextContents(); + expect(cols).toHaveLength(colHeaders.length); + + const { messageId, sender, submittedDate, reportId } = + i === 0 + ? MOCK_GET_MESSAGES[0] + : (MOCK_GET_MESSAGES.find((i) => i.reportId === cols[3]) ?? { reportId: "INVALID" }); + // if first row, we expect column headers. else, the data row matching the report id + const expectedColContents = + i === 0 + ? colHeaders + : [ + messageId, + sender ?? "", + submittedDate ? new Date(submittedDate).toLocaleString() : "", + reportId ?? "", + ]; + + for (const [i, col] of cols.entries()) { + expect(col).toBe(expectedColContents[i]); + } + } + }); + + test("table column 'Message ID' will open message id details", async ({ messageIDSearchPage }) => { + const messageIdCell = tableRows(messageIDSearchPage.page) + .nth(0) + .locator("td") + .nth(0) + .getByRole("link", { name: MessageIDSearchPage.MESSAGE_ID }); + await messageIdCell.click(); + await expect(messageIDSearchPage.page).toHaveURL("/message-details/0"); + expect(messageIDSearchPage.page.locator("h1").getByText(MessageIDSearchPage.MESSAGE_ID)).toBeTruthy(); + }); + + test("table column 'Incoming Report Id' will open report id details", async ({ messageIDSearchPage }) => { + const reportId = "73e3cbc8-9920-4ab7-871f-843a1db4c074"; + const reportIdCell = tableRows(messageIDSearchPage.page).nth(0).locator("td").nth(3).getByRole("link", { + name: reportId, + }); + await reportIdCell.click(); + await openReportIdDetailPage(messageIDSearchPage.page, reportId); + }); + }); + + test.describe("on search without results", () => { + test.beforeEach(async ({ messageIDSearchPage }) => { + await messageIDSearchPage.page.route(MessageIDSearchPage.API_MESSAGES, (route) => + route.fulfill({ + status: 200, + json: [], + }), + ); + await messageIDSearchPage.page.goto(MessageIDSearchPage.URL_MESSAGE_ID_SEARCH); + + await messageIDSearchPage.page.locator("#search-field").fill(MessageIDSearchPage.MESSAGE_ID); + await messageIDSearchPage.page + .getByRole("button", { + name: "Search", + }) + .click(); + }); + + test("has correct title", async ({ page }) => { + await expect(page).toHaveURL(MessageIDSearchPage.URL_MESSAGE_ID_SEARCH); + await expect(page).toHaveTitle(/Message ID search - Admin/); + }); + + test("shows no data", async ({ page }) => { + await expect(noData(page)).toBeAttached(); + }); + }); + + test.describe("Footer", () => { + test("has footer and explicit scroll to footer and scroll to top", async ({ messageIDSearchPage }) => { + await messageIDSearchPage.testFooter(); + }); + }); + }); + + test.describe("receiver user", () => { + test.use({ storageState: "e2e/.auth/receiver.json" }); + + test("has alert", async ({ messageIDSearchPage }) => { + messageIDSearchPage.mockError = true; + await messageIDSearchPage.reload(); + + await expect(messageIDSearchPage.page).toHaveTitle(/Page Not Found/); + }); + }); + + test.describe("sender user", () => { + test.use({ storageState: "e2e/.auth/sender.json" }); + + test("has alert", async ({ messageIDSearchPage }) => { + messageIDSearchPage.mockError = true; + await messageIDSearchPage.reload(); + + await expect(messageIDSearchPage.page).toHaveTitle(/Page Not Found/); + }); + }); +}); diff --git a/frontend-react/e2e/spec/all/organization-settings-page.spec.ts b/frontend-react/e2e/spec/all/authenticated/organization-settings-page.spec.ts similarity index 93% rename from frontend-react/e2e/spec/all/organization-settings-page.spec.ts rename to frontend-react/e2e/spec/all/authenticated/organization-settings-page.spec.ts index e8906c66fe2..83ce97b9e2a 100644 --- a/frontend-react/e2e/spec/all/organization-settings-page.spec.ts +++ b/frontend-react/e2e/spec/all/authenticated/organization-settings-page.spec.ts @@ -2,9 +2,9 @@ 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/organization"; -import { test as baseTest } from "../../test"; +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); @@ -65,6 +65,12 @@ test.describe("Admin Organization Settings Page", () => { test.describe("authenticated admin", () => { test.use({ storageState: "e2e/.auth/admin.json" }); + test.describe("Header", () => { + 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 }) => { organizationPage.mockError = true; await organizationPage.reload(); @@ -159,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(); @@ -229,4 +235,10 @@ test.describe("Admin Organization Settings Page", () => { }); }); }); + + test.describe("Footer", () => { + 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/submission-history-page.spec.ts b/frontend-react/e2e/spec/all/authenticated/submission-history-page.spec.ts similarity index 74% rename from frontend-react/e2e/spec/all/submission-history-page.spec.ts rename to frontend-react/e2e/spec/all/authenticated/submission-history-page.spec.ts index 25b3043a6d1..98f6a4f684c 100644 --- a/frontend-react/e2e/spec/all/submission-history-page.spec.ts +++ b/frontend-react/e2e/spec/all/authenticated/submission-history-page.spec.ts @@ -1,14 +1,8 @@ import { expect, test } from "@playwright/test"; -import { - noData, - selectTestOrg, - tableDataCellValue, - tableRows, - TEST_ORG_IGNORE, -} from "../../helpers/utils"; -import * as submissionHistory from "../../pages/submission-history"; -import { openReportIdDetailPage } from "../../pages/submission-history"; +import { noData, selectTestOrg, tableDataCellValue, tableRows, TEST_ORG_IGNORE } from "../../../helpers/utils"; +import * as submissionHistory from "../../../pages/authenticated/submission-history"; +import { openReportIdDetailPage } from "../../../pages/authenticated/submission-history"; const id = "73e3cbc8-9920-4ab7-871f-843a1db4c074"; test.describe("Submission history page", () => { @@ -28,9 +22,7 @@ test.describe("Submission history page", () => { }); test("will not load page", async ({ page }) => { - await expect( - page.getByText("Cannot fetch Organization data as admin"), - ).toBeVisible(); + await expect(page.getByText("Cannot fetch Organization data as admin")).toBeVisible(); }); test("has footer", async ({ page }) => { @@ -41,19 +33,14 @@ test.describe("Submission history page", () => { test.describe("with org selected", () => { test.beforeEach(async ({ page }) => { await selectTestOrg(page); - await submissionHistory.mockGetSubmissionsResponse( - page, - TEST_ORG_IGNORE, - ); + await submissionHistory.mockGetSubmissionsResponse(page, TEST_ORG_IGNORE); await submissionHistory.mockGetReportHistoryResponse(page); // abort all app insight calls await page.route("**/v2/track", (route) => route.abort()); await submissionHistory.goto(page); }); - test("nav contains the 'Submission History' option", async ({ - page, - }) => { + test("nav contains the 'Submission History' option", async ({ page }) => { const navItems = page.locator(".usa-nav li"); await expect(navItems).toContainText(["Submission History"]); }); @@ -63,9 +50,7 @@ test.describe("Submission history page", () => { }); test("has filter", async ({ page }) => { - await expect( - page.getByTestId("filter-container"), - ).toBeAttached(); + await expect(page.getByTestId("filter-container")).toBeAttached(); }); test.describe("table", () => { @@ -73,56 +58,35 @@ test.describe("Submission history page", () => { await submissionHistory.tableHeaders(page); }); - test("table column 'ReportId' will open the report details", async ({ - page, - }) => { - const reportId = tableRows(page) - .nth(0) - .locator("td") - .nth(0); + test("table column 'ReportId' will open the report details", async ({ page }) => { + const reportId = tableRows(page).nth(0).locator("td").nth(0); await expect(reportId).toContainText(id); await reportId.getByRole("link", { name: id }).click(); await openReportIdDetailPage(page, id); }); - test("table column 'Date/time submitted' has expected data", async ({ - page, - }) => { - expect(await tableDataCellValue(page, 0, 1)).toEqual( - "3/7/2024, 6:00:22 PM", - ); + test("table column 'Date/time submitted' has expected data", async ({ page }) => { + expect(await tableDataCellValue(page, 0, 1)).toEqual("3/7/2024, 6:00:22 PM"); }); - test("table column 'File' has expected data", async ({ - page, - }) => { - expect(await tableDataCellValue(page, 0, 2)).toEqual( - "myfile.hl7", - ); + test("table column 'File' has expected data", async ({ page }) => { + expect(await tableDataCellValue(page, 0, 2)).toEqual("myfile.hl7"); expect(await tableDataCellValue(page, 1, 2)).toEqual( "None-03c3b7ab-7c65-4174-bea7-9195cbb7ed01-20240314174050.hl7", ); }); - test("table column 'Records' has expected data", async ({ - page, - }) => { + test("table column 'Records' has expected data", async ({ page }) => { expect(await tableDataCellValue(page, 0, 3)).toEqual("1"); }); - test("table column 'Status' has expected data", async ({ - page, - }) => { - expect(await tableDataCellValue(page, 0, 4)).toEqual( - "Success", - ); + test("table column 'Status' has expected data", async ({ page }) => { + expect(await tableDataCellValue(page, 0, 4)).toEqual("Success"); }); test("table has pagination", async ({ page }) => { - await expect( - page.getByTestId("Submissions pagination"), - ).toBeAttached(); + await expect(page.getByTestId("Submissions pagination")).toBeAttached(); }); }); @@ -139,9 +103,7 @@ test.describe("Submission history page", () => { await submissionHistory.goto(page); }); - test("nav does not contain the Submissions option", async ({ - page, - }) => { + test("nav does not contain the Submissions option", async ({ page }) => { const navItems = page.locator(".usa-nav li"); await expect(navItems).not.toContainText(["Submissions"]); }); @@ -163,10 +125,7 @@ test.describe("Submission history page", () => { test.use({ storageState: "e2e/.auth/sender.json" }); test.beforeEach(async ({ page }) => { - await submissionHistory.mockGetSubmissionsResponse( - page, - TEST_ORG_IGNORE, - ); + await submissionHistory.mockGetSubmissionsResponse(page, TEST_ORG_IGNORE); await submissionHistory.mockGetReportHistoryResponse(page); await submissionHistory.goto(page); }); @@ -189,9 +148,7 @@ test.describe("Submission history page", () => { await submissionHistory.tableHeaders(page); }); - test("table column 'ReportId' will open the report details", async ({ - page, - }) => { + test("table column 'ReportId' will open the report details", async ({ page }) => { const reportId = tableRows(page).nth(0).locator("td").nth(0); await expect(reportId).toContainText(id); await reportId.getByRole("link", { name: id }).click(); @@ -199,30 +156,20 @@ test.describe("Submission history page", () => { await openReportIdDetailPage(page, id); }); - test("table column 'Date/time submitted' has expected data", async ({ - page, - }) => { - expect(await tableDataCellValue(page, 0, 1)).toEqual( - "3/7/2024, 6:00:22 PM", - ); + test("table column 'Date/time submitted' has expected data", async ({ page }) => { + expect(await tableDataCellValue(page, 0, 1)).toEqual("3/7/2024, 6:00:22 PM"); }); - test("table column 'Records' has expected data", async ({ - page, - }) => { + test("table column 'Records' has expected data", async ({ page }) => { expect(await tableDataCellValue(page, 0, 3)).toEqual("1"); }); - test("table column 'Status' has expected data", async ({ - page, - }) => { + test("table column 'Status' has expected data", async ({ page }) => { expect(await tableDataCellValue(page, 0, 4)).toEqual("Success"); }); test("table has pagination", async ({ page }) => { - await expect( - page.getByTestId("Submissions pagination"), - ).toBeAttached(); + await expect(page.getByTestId("Submissions pagination")).toBeAttached(); }); }); diff --git a/frontend-react/e2e/spec/all/submissions-details-page.spec.ts b/frontend-react/e2e/spec/all/authenticated/submissions-details-page.spec.ts similarity index 74% rename from frontend-react/e2e/spec/all/submissions-details-page.spec.ts rename to frontend-react/e2e/spec/all/authenticated/submissions-details-page.spec.ts index 7f4d76b3464..a25780ae0b8 100644 --- a/frontend-react/e2e/spec/all/submissions-details-page.spec.ts +++ b/frontend-react/e2e/spec/all/authenticated/submissions-details-page.spec.ts @@ -1,8 +1,8 @@ import { expect, test } from "@playwright/test"; -import { selectTestOrg } from "../../helpers/utils"; -import * as reportDetails from "../../pages/report-details"; -import * as submissionDetails from "../../pages/submission-history"; -import { URL_SUBMISSION_HISTORY } from "../../pages/submission-history"; +import { selectTestOrg } from "../../../helpers/utils"; +import * as reportDetails from "../../../pages/authenticated/report-details"; +import * as submissionDetails from "../../../pages/authenticated/submission-history"; +import { URL_SUBMISSION_HISTORY } from "../../../pages/authenticated/submission-history"; const id = "73e3cbc8-9920-4ab7-871f-843a1db4c074"; test.describe("Submissions Details page", () => { @@ -27,9 +27,7 @@ test.describe("Submissions Details page", () => { }); test("has reportId in breadcrumb", async ({ page }) => { - await expect( - page.locator(".usa-breadcrumb ol li").nth(1), - ).toHaveText(`Details: ${id}`); + await expect(page.locator(".usa-breadcrumb ol li").nth(1)).toHaveText(`Details: ${id}`); }); test("has footer", async ({ page }) => { @@ -48,15 +46,8 @@ test.describe("Submissions Details page", () => { await submissionDetails.title(page); }); - test("breadcrumb navigates to Submission History page", async ({ - page, - }) => { - await submissionDetails.breadcrumbLink( - page, - 0, - "Submissions", - URL_SUBMISSION_HISTORY, - ); + test("breadcrumb navigates to Submission History page", async ({ page }) => { + await submissionDetails.breadcrumbLink(page, 0, "Submissions", URL_SUBMISSION_HISTORY); }); test("has footer", async ({ page }) => { @@ -74,9 +65,7 @@ test.describe("Submissions Details page", () => { }); test("has error message", async ({ page }) => { - await expect( - page.getByText(/An error has occurred./), - ).toBeAttached(); + await expect(page.getByText(/An error has occurred./)).toBeAttached(); }); test("has footer", async ({ page }) => { @@ -97,20 +86,11 @@ test.describe("Submissions Details page", () => { }); test("has reportId in breadcrumb", async ({ page }) => { - await expect( - page.locator(".usa-breadcrumb ol li").nth(1), - ).toHaveText(`Details: ${id}`); + await expect(page.locator(".usa-breadcrumb ol li").nth(1)).toHaveText(`Details: ${id}`); }); - test("breadcrumb navigates to Submission History page", async ({ - page, - }) => { - await submissionDetails.breadcrumbLink( - page, - 0, - "Submissions", - URL_SUBMISSION_HISTORY, - ); + test("breadcrumb navigates to Submission History page", async ({ page }) => { + await submissionDetails.breadcrumbLink(page, 0, "Submissions", URL_SUBMISSION_HISTORY); }); test("has footer", async ({ page }) => { @@ -127,9 +107,7 @@ test.describe("Submissions Details page", () => { }); test("has error message", async ({ page }) => { - await expect( - page.getByText(/An error has occurred./), - ).toBeAttached(); + await expect(page.getByText(/An error has occurred./)).toBeAttached(); }); test("has footer", async ({ page }) => { @@ -145,9 +123,7 @@ test.describe("Submissions Details page", () => { }); test("has error message", async ({ page }) => { - await expect( - page.getByText(/An error has occurred./), - ).toBeAttached(); + await expect(page.getByText(/An error has occurred./)).toBeAttached(); }); test("has footer", async ({ page }) => { diff --git a/frontend-react/e2e/spec/all/daily-data-page-user-flow.spec.ts b/frontend-react/e2e/spec/all/daily-data-page-user-flow.spec.ts index 03156ebc07f..71aaa857b75 100644 --- a/frontend-react/e2e/spec/all/daily-data-page-user-flow.spec.ts +++ b/frontend-react/e2e/spec/all/daily-data-page-user-flow.spec.ts @@ -28,8 +28,8 @@ import { setTime, startDate, startTime, -} from "../../pages/daily-data"; -import { URL_REPORT_DETAILS } from "../../pages/report-details"; +} from "../../pages/authenticated/daily-data.js"; +import { URL_REPORT_DETAILS } from "../../pages/authenticated/report-details.js"; import { test as baseTest } from "../../test"; const defaultStartTime = "9:00am"; diff --git a/frontend-react/e2e/spec/all/homepage.spec.ts b/frontend-react/e2e/spec/all/homepage.spec.ts deleted file mode 100644 index 6d1b4bb9d08..00000000000 --- a/frontend-react/e2e/spec/all/homepage.spec.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { expect, test } from "@playwright/test"; - -import { scrollToFooter, scrollToTop } from "../../helpers/utils"; -import * as header from "../../pages/header"; -import * as homepage from "../../pages/homepage"; -import * as managingYourConnection from "../../pages/managing-your-connection"; -import * as ourNetwork from "../../pages/our-network"; -import * as security from "../../pages/security"; - -test.describe( - "Homepage", - { - tag: "@smoke", - }, - () => { - test.beforeEach(async ({ page }) => { - await homepage.goto(page); - }); - - test("has correct title", async ({ page }) => { - await expect(page).toHaveTitle( - /ReportStream - CDC's free, interoperable data transfer platform/, - ); - }); - - test("opens the Security page on 'security of your data' click", async ({ - page, - }) => { - await page - .getByRole("link", { name: "security of your data" }) - .click(); - await security.onLoad(page); - // Go back to the homepage - await header.clickOnHome(page); - - expect(true).toBe(true); - }); - - test("opens the managing-your-connection page on 'our tools' click", async ({ - page, - }) => { - await page.getByRole("link", { name: "our tools" }).click(); - await managingYourConnection.onLoad(page); - // Go back to the homepage - await header.clickOnHome(page); - - expect(true).toBe(true); - }); - - test("opens Our Network page on 'See our full network' click", async ({ - page, - }) => { - await page - .getByRole("link", { name: "See our full network" }) - .click(); - await ourNetwork.onLoad(page); - // Go back to the homepage - await header.clickOnHome(page); - - expect(true).toBe(true); - }); - - test("is clickable Where were live map", async ({ page }) => { - // Trigger map click and go to our network page - await ourNetwork.clickOnLiveMap(page); - // Go back to the homepage - await header.clickOnHome(page); - - expect(true).toBe(true); - }); - - test("explicit scroll to footer and then scroll to top", async ({ - page, - }) => { - await expect(page.locator("footer")).not.toBeInViewport(); - await scrollToFooter(page); - await expect(page.locator("footer")).toBeInViewport(); - await expect(page.getByTestId("govBanner")).not.toBeInViewport(); - await scrollToTop(page); - await expect(page.getByTestId("govBanner")).toBeInViewport(); - }); - }, -); diff --git a/frontend-react/e2e/spec/all/idletimeout.spec.ts b/frontend-react/e2e/spec/all/idletimeout.spec.ts index 3d2ae35eb96..0b35f0fd34d 100644 --- a/frontend-react/e2e/spec/all/idletimeout.spec.ts +++ b/frontend-react/e2e/spec/all/idletimeout.spec.ts @@ -1,7 +1,7 @@ import { expect } from "@playwright/test"; import process from "node:process"; -import { OrganizationPage } from "../../pages/organization"; +import { OrganizationPage } from "../../pages/authenticated/organization"; import { test as baseTest } from "../../test"; const timeout = parseInt(process.env.VITE_IDLE_TIMEOUT ?? "20000"); @@ -35,7 +35,7 @@ const test = baseTest.extend({ receiverLogin, storageState, frontendWarningsLogPath, - isFrontendWarningsLog + isFrontendWarningsLog, }); await page.goto(); await use(page); @@ -45,19 +45,14 @@ const test = baseTest.extend({ test.use({ storageState: "e2e/.auth/admin.json" }); test.skip("Does not trigger early", async ({ organizationPage }) => { - await expect( - organizationPage.page.getByRole("banner").first(), - ).toBeVisible(); + await expect(organizationPage.page.getByRole("banner").first()).toBeVisible(); await organizationPage.page.keyboard.down("Tab"); const start = new Date(); - await organizationPage.page.waitForRequest( - /\/oauth2\/default\/v1\/revoke/, - { - timeout: timeoutHigh, - }, - ); + await organizationPage.page.waitForRequest(/\/oauth2\/default\/v1\/revoke/, { + timeout: timeoutHigh, + }); const end = new Date(); diff --git a/frontend-react/e2e/spec/all/last-mile-failures-page.spec.ts b/frontend-react/e2e/spec/all/last-mile-failures-page.spec.ts deleted file mode 100644 index 8af5e5b64af..00000000000 --- a/frontend-react/e2e/spec/all/last-mile-failures-page.spec.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { expect, test } from "@playwright/test"; - -import { tableRows } from "../../helpers/utils"; -import * as lastMileFailures from "../../pages/last-mile-failures"; - -test.describe("Last Mile Failure page", () => { - test.describe("not authenticated", () => { - test("redirects to login", async ({ page }) => { - await lastMileFailures.goto(page); - await expect(page).toHaveURL("/login"); - }); - }); - - test.describe("admin user - happy path", () => { - test.use({ storageState: "e2e/.auth/admin.json" }); - - test.beforeEach(async ({ page }) => { - // Mock the api call before navigating - await lastMileFailures.mockGetSendFailuresResponse(page); - await lastMileFailures.mockGetResendResponse(page); - await lastMileFailures.goto(page); - }); - - test("has correct title", async ({ page }) => { - await expect(page).toHaveTitle(/Last Mile Failures/); - }); - - test("has footer", async ({ page }) => { - await expect(page.locator("footer")).toBeAttached(); - }); - - test("table has correct headers", async ({ page }) => { - await expect(page.locator(".column-header-text").nth(0)).toHaveText(/Failed At/); - await expect(page.locator(".column-header-text").nth(1)).toHaveText(/ReportId/); - await expect(page.locator(".column-header-text").nth(2)).toHaveText(/Receiver/); - }); - - test("table column 'Failed At' has expected data", async ({ page }) => { - await expect(tableRows(page).nth(0).locator("td").nth(0)).toHaveText("Tue, 2/20/2024, 9:35 PM"); - }); - - test("table column 'ReportId' will open a modal with report details", async ({ page }) => { - const reportId = tableRows(page).nth(0).locator("td").nth(1); - await expect(reportId).toContainText(/e5ce49c0-b230-4364-8230-964273249fa1/); - await reportId.click(); - - const modal = page.getByTestId("modalWindow").nth(0); - await expect(modal).toContainText(/Report ID:e5ce49c0-b230-4364-8230-964273249fa1/); - }); - - test("table column 'Receiver' will open receiver edit page", async ({ page }) => { - const receiver = tableRows(page).nth(0).locator("td").nth(2); - await expect(receiver).toContainText(/flexion.etor-service-receiver-results/); - await receiver.click(); - - await expect(page).toHaveURL( - "/admin/orgreceiversettings/org/flexion/receiver/etor-service-receiver-results/action/edit", - ); - }); - }); - - test.describe("admin user - server error", () => { - test.use({ storageState: "e2e/.auth/admin.json" }); - - test.beforeEach(async ({ page }) => { - await lastMileFailures.mockGetSendFailuresResponse(page, 500); - await lastMileFailures.goto(page); - }); - - test("has correct title", async ({ page }) => { - await expect(page).toHaveTitle(/Last Mile Failures/); - }); - - test("has alert", async ({ page }) => { - await expect(page.getByTestId("alert")).toBeAttached(); - await expect(page.getByText(/Our apologies, there was an error loading this content./)).toBeAttached(); - }); - - test("has footer", async ({ page }) => { - await expect(page.locator("footer")).toBeAttached(); - }); - }); - - test.describe("receiver user", () => { - test.use({ storageState: "e2e/.auth/receiver.json" }); - - test.beforeEach(async ({ page }) => { - await lastMileFailures.goto(page); - }); - - test("returns Page Not Found", async ({ page }) => { - await expect(page).toHaveTitle(/Page Not Found/); - }); - - test("has footer", async ({ page }) => { - await expect(page.locator("footer")).toBeAttached(); - }); - }); - - test.describe("sender user", () => { - test.use({ storageState: "e2e/.auth/sender.json" }); - - test.beforeEach(async ({ page }) => { - await lastMileFailures.goto(page); - }); - - test("returns Page Not Found", async ({ page }) => { - await expect(page).toHaveTitle(/Page Not Found/); - }); - - test("has footer", async ({ page }) => { - await expect(page.locator("footer")).toBeAttached(); - }); - }); -}); diff --git a/frontend-react/e2e/spec/all/managing-your-connection-page.spec.ts b/frontend-react/e2e/spec/all/managing-your-connection-page.spec.ts deleted file mode 100644 index 9e3d7ceabc0..00000000000 --- a/frontend-react/e2e/spec/all/managing-your-connection-page.spec.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { scrollToFooter, scrollToTop } from "../../helpers/utils"; -import { ManagingYourConnectionPage } from "../../pages/managing-your-connection"; -import { test as baseTest, expect } from "../../test"; - -const cards = [ - { - name: "For healthcare organizations", - links: [ - "Manage your public key", - "View your submission history", - "Login", - "contact us", - ], - }, - { - name: "For public health agencies", - links: [ - "Refer healthcare organizations", - "View your dashboard", - "Login", - "contact us", - ], - }, -]; - -export interface ManagingYourConnectionPageFixtures { - managingYourConnectionPage: ManagingYourConnectionPage; -} - -const test = baseTest.extend({ - managingYourConnectionPage: async ( - { - page: _page, - isMockDisabled, - adminLogin, - senderLogin, - receiverLogin, - storageState, - isFrontendWarningsLog, - frontendWarningsLogPath, - }, - use, - ) => { - const page = new ManagingYourConnectionPage({ - page: _page, - isMockDisabled, - adminLogin, - senderLogin, - receiverLogin, - storageState, - isFrontendWarningsLog, - frontendWarningsLogPath, - }); - await page.goto(); - await use(page); - }, -}); - -test.describe( - "Managing Your Connection page", - { - tag: "@smoke", - }, - () => { - test("has correct title", async ({ managingYourConnectionPage }) => { - await expect(managingYourConnectionPage.page).toHaveTitle( - managingYourConnectionPage.title, - ); - await expect(managingYourConnectionPage.heading).toBeVisible(); - }); - - test.describe("Quick links", () => { - for (const card of cards) { - test(`should have ${card.name} links`, async ({ - managingYourConnectionPage, - }) => { - const cardHeader = managingYourConnectionPage.page.locator( - ".usa-card__header", - { - hasText: card.name, - }, - ); - - await expect(cardHeader).toBeVisible(); - - const cardContainer = cardHeader.locator(".."); - - for (const link of card.links) { - await expect( - cardContainer.getByRole("link", { - name: `${link}`, - }), - ).toBeVisible(); - } - }); - } - }); - - test.describe("Footer", () => { - test("has footer", async ({ managingYourConnectionPage }) => { - await expect(managingYourConnectionPage.footer).toBeAttached(); - }); - - test("explicit scroll to footer and then scroll to top", async ({ - managingYourConnectionPage, - }) => { - await expect( - managingYourConnectionPage.footer, - ).not.toBeInViewport(); - await scrollToFooter(managingYourConnectionPage.page); - await expect( - managingYourConnectionPage.footer, - ).toBeInViewport(); - await expect( - managingYourConnectionPage.page.getByTestId("govBanner"), - ).not.toBeInViewport(); - await scrollToTop(managingYourConnectionPage.page); - await expect( - managingYourConnectionPage.page.getByTestId("govBanner"), - ).toBeInViewport(); - }); - }); - }, -); diff --git a/frontend-react/e2e/spec/all/message-details-page.spec.ts b/frontend-react/e2e/spec/all/message-details-page.spec.ts deleted file mode 100644 index 77291601b05..00000000000 --- a/frontend-react/e2e/spec/all/message-details-page.spec.ts +++ /dev/null @@ -1,215 +0,0 @@ -import { expect, test } from "@playwright/test"; -import fs from "node:fs"; -import { parseFileLocation } from "../../../src/utils/misc"; -import { tableRows } from "../../helpers/utils"; -import { MOCK_GET_MESSAGE } from "../../mocks/messages"; -import * as messageDetails from "../../pages/message-details"; -import { URL_MESSAGE_DETAILS } from "../../pages/message-details"; -import * as messageIdSearch from "../../pages/message-id-search"; -import { MESSAGE_ID } from "../../pages/message-id-search"; -import { mockGetHistoryReportResponse } from "../../pages/report-details"; -test.describe("Message Details Page", () => { - test.describe("not authenticated", () => { - test("redirects to login", async ({ page }) => { - await messageDetails.goto(page); - await expect(page).toHaveURL("/login"); - }); - }); - - test.describe("authenticated admin", () => { - test.use({ storageState: "e2e/.auth/admin.json" }); - - test.beforeEach(async ({ page }) => { - await page.route(messageIdSearch.API_MESSAGE, (route) => - route.fulfill({ - status: 200, - json: MOCK_GET_MESSAGE, - }), - ); - await messageDetails.goto(page); - }); - - test("has correct title", async ({ page }) => { - await expect(page).toHaveURL(URL_MESSAGE_DETAILS); - await expect(page).toHaveTitle( - /ReportStream - CDC's free, interoperable data transfer platform/, - ); - }); - - test("has message id section", async ({ page }) => { - await expect( - page.getByText("Message ID", { exact: true }), - ).toBeVisible(); - await expect(page.getByText(MESSAGE_ID)).toBeVisible(); - }); - - test("has sender section", async ({ page }) => { - const { sender, reportId, submittedDate } = MOCK_GET_MESSAGE; - - await expect(page.getByText("Sender:")).toBeVisible(); - await expect(page.getByText(sender)).toBeVisible(); - await expect(page.getByText("Incoming Report ID")).toBeVisible(); - await expect( - page.getByText(reportId, { exact: true }), - ).toBeVisible(); - await expect(page.getByText("Date/Time Submitted")).toBeVisible(); - await expect( - page.getByText(new Date(submittedDate).toLocaleString()), - ).toBeVisible(); - await expect(page.getByText("File Location")).toBeVisible(); - await expect( - page.getByText("RECEIVE", { exact: true }), - ).toBeVisible(); - await expect( - page.getByText("ignore.ignore-simple-report"), - ).toBeVisible(); - await expect(page.getByText("Incoming File Name")).toBeVisible(); - await expect( - page.getByText( - "pdi-covid-19-d9a57df0-2702-4e28-9d80-ff8c9ec51816-20240514142655.csv", - ), - ).toBeVisible(); - }); - - test.describe("authenticated admin", () => { - test("has receiver title", async ({ page }) => { - await expect(page.getByText("Receivers:")).toBeVisible(); - }); - - test("displays expected table headers and data", async ({ - page, - }) => { - // include header row - const rowCount = MOCK_GET_MESSAGE.receiverData.length + 1; - const table = page.getByRole("table"); - await expect(table).toBeVisible(); - const rows = await table.getByRole("row").all(); - expect(rows).toHaveLength(rowCount); - - const colHeaders = [ - "Name", - "Service", - "Date", - "Report Id", - "Main", - "Sub", - "File Name", - "Transport Results", - ]; - for (const [i, row] of rows.entries()) { - const cols = await row.getByRole("cell").allTextContents(); - expect(cols).toHaveLength(colHeaders.length); - - const { - receivingOrg, - receivingOrgSvc, - createdAt, - reportId, - fileUrl, - transportResult, - } = - i === 0 - ? MOCK_GET_MESSAGE.receiverData[0] - : MOCK_GET_MESSAGE.receiverData.find( - (i) => i.reportId === cols[3], - ) ?? { reportId: "INVALID" }; - - // if first row, we expect column headers. else, the data row matching the report id - const expectedColContents = - i === 0 - ? colHeaders - : [ - receivingOrg ?? "", - receivingOrgSvc ?? "", - createdAt - ? new Date(createdAt).toLocaleString() - : "", - reportId, - parseFileLocation( - fileUrl ?? "N/A", - ).folderLocation.toLocaleUpperCase(), - parseFileLocation(fileUrl ?? "N/A") - .sendingOrg, - parseFileLocation(fileUrl ?? "N/A").fileName, - transportResult ?? "", - ]; - - for (const [i, col] of cols.entries()) { - expect(col).toBe(expectedColContents[i]); - } - } - }); - - test("table column 'FileName' will download file", async ({ - page, - }) => { - const downloadProm = page.waitForEvent("download"); - await mockGetHistoryReportResponse(page, "*"); - - await tableRows(page) - .nth(0) - .locator("td") - .nth(6) - .getByRole("button") - .click(); - - const download = await downloadProm; - - // assert filename - expect(download.suggestedFilename()).toBe( - "hhsprotect-covid-19-73e3cbc8-9920-4ab7-871f-843a1db4c074.csv", - ); - // get and assert stats - expect( - (await fs.promises.stat(await download.path())).size, - ).toBeGreaterThan(200); - }); - }); - - test("has footer", async ({ page }) => { - await expect(page.locator("footer")).toBeAttached(); - }); - }); - - test.describe("receiver user", () => { - test.use({ storageState: "e2e/.auth/receiver.json" }); - - test.beforeEach(async ({ page }) => { - await messageDetails.goto(page); - }); - - test("has alert", async ({ page }) => { - await expect(page.getByTestId("alert")).toBeAttached(); - await expect( - page.getByText( - /Our apologies, there was an error loading this content./, - ), - ).toBeAttached(); - }); - - test("has footer", async ({ page }) => { - await expect(page.locator("footer")).toBeAttached(); - }); - }); - - test.describe("sender user", () => { - test.use({ storageState: "e2e/.auth/sender.json" }); - - test.beforeEach(async ({ page }) => { - await messageDetails.goto(page); - }); - - test("has alert", async ({ page }) => { - await expect(page.getByTestId("alert")).toBeAttached(); - await expect( - page.getByText( - /Our apologies, there was an error loading this content./, - ), - ).toBeAttached(); - }); - - test("has footer", async ({ page }) => { - await expect(page.locator("footer")).toBeAttached(); - }); - }); -}); diff --git a/frontend-react/e2e/spec/all/message-id-search-page.spec.ts b/frontend-react/e2e/spec/all/message-id-search-page.spec.ts deleted file mode 100644 index c9057d80d2a..00000000000 --- a/frontend-react/e2e/spec/all/message-id-search-page.spec.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { expect, test } from "@playwright/test"; -import { noData, tableRows } from "../../helpers/utils"; -import { MOCK_GET_MESSAGE, MOCK_GET_MESSAGES } from "../../mocks/messages"; -import * as messageIdSearch from "../../pages/message-id-search"; -import { - MESSAGE_ID, - URL_MESSAGE_ID_SEARCH, -} from "../../pages/message-id-search"; -import { openReportIdDetailPage } from "../../pages/submission-history"; -import * as submissionHistory from "../../pages/submission-history"; - -test.describe("Message ID Search Page", () => { - test.describe("not authenticated", () => { - test("redirects to login", async ({ page }) => { - await messageIdSearch.goto(page); - await expect(page).toHaveURL("/login"); - }); - }); - - test.describe("authenticated admin", () => { - test.use({ storageState: "e2e/.auth/admin.json" }); - - test.describe("on search with results", () => { - test.beforeEach(async ({ page }) => { - await page.route(messageIdSearch.API_MESSAGES, (route) => - route.fulfill({ - status: 200, - json: MOCK_GET_MESSAGES, - }), - ); - await page.route(messageIdSearch.API_MESSAGE, (route) => - route.fulfill({ - status: 200, - json: MOCK_GET_MESSAGE, - }), - ); - await submissionHistory.mockGetReportHistoryResponse(page); - await messageIdSearch.goto(page); - - await page.locator("#search-field").fill(MESSAGE_ID); - await page - .getByRole("button", { - name: "Search", - }) - .click(); - }); - - test("has correct title", async ({ page }) => { - await expect(page).toHaveURL(URL_MESSAGE_ID_SEARCH); - await expect(page).toHaveTitle(/Message ID search - Admin/); - }); - - test("has footer", async ({ page }) => { - await expect(page.locator("footer")).toBeAttached(); - }); - - test("displays expected table headers and data", async ({ - page, - }) => { - // include header row - const rowCount = MOCK_GET_MESSAGES.length + 1; - const table = page.getByRole("table"); - await expect(table).toBeVisible(); - const rows = await table.getByRole("row").all(); - expect(rows).toHaveLength(rowCount); - - const colHeaders = [ - "Message ID", - "Sender", - "Date/time submitted", - "Incoming Report Id", - ]; - for (const [i, row] of rows.entries()) { - const cols = await row.getByRole("cell").allTextContents(); - expect(cols).toHaveLength(colHeaders.length); - - const { messageId, sender, submittedDate, reportId } = - i === 0 - ? MOCK_GET_MESSAGES[0] - : MOCK_GET_MESSAGES.find( - (i) => i.reportId === cols[3], - ) ?? { reportId: "INVALID" }; - // if first row, we expect column headers. else, the data row matching the report id - const expectedColContents = - i === 0 - ? colHeaders - : [ - messageId, - sender ?? "", - submittedDate - ? new Date(submittedDate).toLocaleString() - : "", - reportId ?? "", - ]; - - for (const [i, col] of cols.entries()) { - expect(col).toBe(expectedColContents[i]); - } - } - }); - - test("table column 'Message ID' will open message id details", async ({ - page, - }) => { - const messageIdCell = tableRows(page) - .nth(0) - .locator("td") - .nth(0) - .getByRole("link", { name: MESSAGE_ID }); - await messageIdCell.click(); - await expect(page).toHaveURL("/message-details/0"); - expect(page.locator("h1").getByText(MESSAGE_ID)).toBeTruthy(); - }); - - test("table column 'Incoming Report Id' will open report id details", async ({ - page, - }) => { - const reportId = "73e3cbc8-9920-4ab7-871f-843a1db4c074"; - const reportIdCell = tableRows(page) - .nth(0) - .locator("td") - .nth(3) - .getByRole("link", { - name: reportId, - }); - await reportIdCell.click(); - await openReportIdDetailPage(page, reportId); - }); - }); - - test.describe("on search without results", () => { - test.beforeEach(async ({ page }) => { - await page.route(messageIdSearch.API_MESSAGES, (route) => - route.fulfill({ - status: 200, - json: [], - }), - ); - await messageIdSearch.goto(page); - - await page.locator("#search-field").fill(MESSAGE_ID); - await page - .getByRole("button", { - name: "Search", - }) - .click(); - }); - - test("has correct title", async ({ page }) => { - await expect(page).toHaveURL(URL_MESSAGE_ID_SEARCH); - await expect(page).toHaveTitle(/Message ID search - Admin/); - }); - - test("has footer", async ({ page }) => { - await expect(page.locator("footer")).toBeAttached(); - }); - - test("shows no data", async ({ page }) => { - await expect(noData(page)).toBeAttached(); - }); - }); - }); - - test.describe("receiver user", () => { - test.use({ storageState: "e2e/.auth/receiver.json" }); - - test.beforeEach(async ({ page }) => { - await messageIdSearch.goto(page); - }); - - test("returns Page Not Found", async ({ page }) => { - await expect(page).toHaveTitle(/Page Not Found/); - }); - - test("has footer", async ({ page }) => { - await expect(page.locator("footer")).toBeAttached(); - }); - }); - - test.describe("sender user", () => { - test.use({ storageState: "e2e/.auth/sender.json" }); - - test.beforeEach(async ({ page }) => { - await messageIdSearch.goto(page); - }); - - test("returns Page Not Found", async ({ page }) => { - await expect(page).toHaveTitle(/Page Not Found/); - }); - - test("has footer", async ({ page }) => { - await expect(page.locator("footer")).toBeAttached(); - }); - }); -}); diff --git a/frontend-react/e2e/spec/all/our-network-page.spec.ts b/frontend-react/e2e/spec/all/our-network-page.spec.ts deleted file mode 100644 index d8345618291..00000000000 --- a/frontend-react/e2e/spec/all/our-network-page.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { expect, test } from "@playwright/test"; - -import * as sideNav from "../../pages/about-side-navigation"; -import * as ourNetwork from "../../pages/our-network"; -test.describe( - "Our network page", - { - tag: "@smoke", - }, - () => { - test.beforeEach(async ({ page }) => { - await ourNetwork.goto(page); - }); - - test("has correct title", async ({ page }) => { - await expect(page).toHaveTitle(/Our network/); - }); - - test.describe("Side navigation", () => { - test("has Our network link", async ({ page }) => { - await sideNav.clickNetwork(page); - await expect(page).toHaveURL(/.*about\/our-network/); - }); - - test("has Product roadmap link", async ({ page }) => { - await sideNav.clickRoadmap(page); - await expect(page).toHaveURL(/.*about\/roadmap/); - }); - - test("has News link", async ({ page }) => { - await sideNav.clickNews(page); - await expect(page).toHaveURL(/.*about\/news/); - }); - - test("has Case studies link", async ({ page }) => { - await sideNav.clickCaseStudies(page); - await expect(page).toHaveURL(/.*about\/case-studies/); - }); - - test("has Security link", async ({ page }) => { - await sideNav.clickSecurity(page); - await expect(page).toHaveURL(/.*about\/security/); - }); - - test("has Release notes link", async ({ page }) => { - await sideNav.clickReleaseNotes(page); - await expect(page).toHaveURL(/.*about\/release-notes/); - }); - }); - }, -); diff --git a/frontend-react/e2e/spec/all/about-page.spec.ts b/frontend-react/e2e/spec/all/public/about/about-page.spec.ts similarity index 84% rename from frontend-react/e2e/spec/all/about-page.spec.ts rename to frontend-react/e2e/spec/all/public/about/about-page.spec.ts index bf32d2b158b..f62b5f1db19 100644 --- a/frontend-react/e2e/spec/all/about-page.spec.ts +++ b/frontend-react/e2e/spec/all/public/about/about-page.spec.ts @@ -1,6 +1,5 @@ -import { scrollToFooter, scrollToTop } from "../../helpers/utils"; -import { AboutPage } from "../../pages/about"; -import { test as baseTest, expect } from "../../test"; +import { AboutPage } from "../../../../pages/public/about/about"; +import { test as baseTest, expect } from "../../../../test"; const URL_ABOUT = "/about"; @@ -38,6 +37,12 @@ const test = baseTest.extend({ }); test.describe("About page", () => { + test.describe("Header", () => { + test("has correct title + heading", async ({ aboutPage }) => { + await aboutPage.testHeader(); + }); + }); + test("nav contains the 'About' dropdown with 'About Reportstream' option", async ({ aboutPage }) => { const navItems = aboutPage.page.locator(".usa-nav li"); await expect(navItems).toContainText(["About"]); @@ -48,11 +53,6 @@ test.describe("About page", () => { await expect(aboutPage.page).toHaveURL(URL_ABOUT); }); - test("has correct title", async ({ aboutPage }) => { - await expect(aboutPage.page).toHaveTitle(aboutPage.title); - await expect(aboutPage.heading).toBeVisible(); - }); - test.describe("In this section", () => { test("has 'Our network' link", async ({ aboutPage }) => { await aboutPage.page.getByRole("link", { name: /Our network/ }).click(); @@ -156,17 +156,8 @@ test.describe("About page", () => { }); test.describe("Footer", () => { - test("has footer", async ({ aboutPage }) => { - await expect(aboutPage.footer).toBeAttached(); - }); - - test("explicit scroll to footer and then scroll to top", async ({ aboutPage }) => { - await expect(aboutPage.footer).not.toBeInViewport(); - await scrollToFooter(aboutPage.page); - await expect(aboutPage.footer).toBeInViewport(); - await expect(aboutPage.page.getByTestId("govBanner")).not.toBeInViewport(); - await scrollToTop(aboutPage.page); - await expect(aboutPage.page.getByTestId("govBanner")).toBeInViewport(); + test("has footer and explicit scroll to footer and scroll to top", async ({ aboutPage }) => { + await aboutPage.testFooter(); }); }); }); diff --git a/frontend-react/e2e/spec/all/public/about/our-network-page.spec.ts b/frontend-react/e2e/spec/all/public/about/our-network-page.spec.ts new file mode 100644 index 00000000000..d826f09b006 --- /dev/null +++ b/frontend-react/e2e/spec/all/public/about/our-network-page.spec.ts @@ -0,0 +1,62 @@ +import { aboutSideNav } from "../../../../helpers/internal-links"; +import { OurNetworkPage } from "../../../../pages/public/about/our-network"; +import { test as baseTest } from "../../../../test"; + +export interface OurNetworkPageFixtures { + ourNetworkPage: OurNetworkPage; +} + +const test = baseTest.extend({ + ourNetworkPage: async ( + { + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + isFrontendWarningsLog, + frontendWarningsLogPath, + }, + use, + ) => { + const page = new OurNetworkPage({ + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + isFrontendWarningsLog, + frontendWarningsLogPath, + }); + await page.goto(); + await use(page); + }, +}); + +test.describe( + "Our network page", + { + tag: "@smoke", + }, + () => { + test.describe("Header", () => { + test("has correct title + heading", async ({ ourNetworkPage }) => { + await ourNetworkPage.testHeader(); + }); + }); + + test.describe("Side navigation", () => { + test("has correct About sidenav items", async ({ ourNetworkPage }) => { + await ourNetworkPage.testSidenav(aboutSideNav); + }); + }); + + test.describe("Footer", () => { + test("has footer and explicit scroll to footer and scroll to top", async ({ ourNetworkPage }) => { + await ourNetworkPage.testFooter(); + }); + }); + }, +); diff --git a/frontend-react/e2e/spec/all/public/about/roadmap.spec.ts b/frontend-react/e2e/spec/all/public/about/roadmap.spec.ts new file mode 100644 index 00000000000..d3f71cbf108 --- /dev/null +++ b/frontend-react/e2e/spec/all/public/about/roadmap.spec.ts @@ -0,0 +1,82 @@ +import { aboutSideNav } from "../../../../helpers/internal-links"; +import { RoadmapPage } from "../../../../pages/public/about/roadmap"; +import { test as baseTest } from "../../../../test"; + +export interface RoadmapPageFixtures { + roadmapPage: RoadmapPage; +} + +const test = baseTest.extend({ + roadmapPage: async ( + { + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + isFrontendWarningsLog, + frontendWarningsLogPath, + }, + use, + ) => { + const page = new RoadmapPage({ + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + isFrontendWarningsLog, + frontendWarningsLogPath, + }); + await page.goto(); + await use(page); + }, +}); + +const cards = [ + { + name: "News", + }, + { + name: "Release notes", + }, + { + name: "Developer resources", + }, +]; + +test.describe( + "Product roadmap page", + { + tag: "@smoke", + }, + () => { + test.describe("Header", () => { + test("has correct title + heading", async ({ roadmapPage }) => { + await roadmapPage.testHeader(); + }); + }); + + test.describe("Side navigation", () => { + test("has correct About sidenav items", async ({ roadmapPage }) => { + await roadmapPage.testSidenav(aboutSideNav); + }); + }); + + test.describe("CTA", () => { + for (const card of cards) { + test(`should have ${card.name}`, async ({ roadmapPage }) => { + await roadmapPage.testCard(card); + }); + } + }); + + test.describe("Footer", () => { + test("has footer and explicit scroll to footer and scroll to top", async ({ roadmapPage }) => { + await roadmapPage.testFooter(); + }); + }); + }, +); diff --git a/frontend-react/e2e/spec/all/security.spec.ts b/frontend-react/e2e/spec/all/public/about/security.spec.ts similarity index 73% rename from frontend-react/e2e/spec/all/security.spec.ts rename to frontend-react/e2e/spec/all/public/about/security.spec.ts index a22b9b5c737..08976df88e4 100644 --- a/frontend-react/e2e/spec/all/security.spec.ts +++ b/frontend-react/e2e/spec/all/public/about/security.spec.ts @@ -1,6 +1,5 @@ -import { scrollToFooter, scrollToTop } from "../../helpers/utils"; -import { SecurityPage } from "../../pages/security"; -import { test as baseTest, expect } from "../../test"; +import { SecurityPage } from "../../../../pages/public/about/security"; +import { test as baseTest, expect } from "../../../../test"; const URL_SECURITY = "/about/security"; @@ -43,6 +42,12 @@ test.describe( tag: "@smoke", }, () => { + test.describe("Header", () => { + test("has correct title + heading", async ({ securityPage }) => { + await securityPage.testHeader(); + }); + }); + test("nav contains the 'About' dropdown with 'Security Reportstream' option", async ({ securityPage }) => { const navItems = securityPage.page.locator(".usa-nav li"); await expect(navItems).toContainText(["About"]); @@ -53,11 +58,6 @@ test.describe( await expect(securityPage.page).toHaveURL(URL_SECURITY); }); - test("has correct title", async ({ securityPage }) => { - await expect(securityPage.page).toHaveTitle(securityPage.title); - await expect(securityPage.heading).toBeVisible(); - }); - test.describe("Security section", () => { test("Accordion sections expand", async ({ securityPage }) => { // Not necessary to test all expansions. @@ -90,17 +90,8 @@ test.describe( }); test.describe("Footer", () => { - test("has footer", async ({ securityPage }) => { - await expect(securityPage.footer).toBeAttached(); - }); - - test("explicit scroll to footer and then scroll to top", async ({ securityPage }) => { - await expect(securityPage.footer).not.toBeInViewport(); - await scrollToFooter(securityPage.page); - await expect(securityPage.footer).toBeInViewport(); - await expect(securityPage.page.getByTestId("govBanner")).not.toBeInViewport(); - await scrollToTop(securityPage.page); - await expect(securityPage.page.getByTestId("govBanner")).toBeInViewport(); + test("has footer + test bottom-to-top page scroll", async ({ securityPage }) => { + await securityPage.testFooter(); }); }); }, diff --git a/frontend-react/e2e/spec/all/getting-started/receiving-data.spec.ts b/frontend-react/e2e/spec/all/public/getting-started/receiving-data.spec.ts similarity index 63% rename from frontend-react/e2e/spec/all/getting-started/receiving-data.spec.ts rename to frontend-react/e2e/spec/all/public/getting-started/receiving-data.spec.ts index 3e1eb2c7660..0a7755d0c4f 100644 --- a/frontend-react/e2e/spec/all/getting-started/receiving-data.spec.ts +++ b/frontend-react/e2e/spec/all/public/getting-started/receiving-data.spec.ts @@ -1,7 +1,6 @@ -import site from "../../../../src/content/site.json" assert { type: "json" }; -import { scrollToFooter, scrollToTop } from "../../../helpers/utils"; -import { ReceivingDataPage } from "../../../pages/getting-started/receiving-data"; -import { test as baseTest, expect } from "../../../test"; +import site from "../../../../../src/content/site.json" assert { type: "json" }; +import { ReceivingDataPage } from "../../../../pages/public/getting-started/receiving-data"; +import { test as baseTest, expect } from "../../../../test"; export interface ReceivingDataPageFixtures { receivingDataPage: ReceivingDataPage; @@ -37,9 +36,10 @@ const test = baseTest.extend({ }); test.describe("Receiving data page", () => { - test("has correct title", async ({ receivingDataPage }) => { - await expect(receivingDataPage.page).toHaveTitle(receivingDataPage.title); - await expect(receivingDataPage.heading).toBeVisible(); + test.describe("Header", () => { + test("has correct title + heading", async ({ receivingDataPage }) => { + await receivingDataPage.testHeader(); + }); }); test("has link to onboarding form", async ({ receivingDataPage }) => { @@ -73,17 +73,8 @@ test.describe("Receiving data page", () => { }); test.describe("Footer", () => { - test("has footer", async ({ receivingDataPage }) => { - await expect(receivingDataPage.footer).toBeAttached(); - }); - - test("explicit scroll to footer and then scroll to top", async ({ receivingDataPage }) => { - await expect(receivingDataPage.footer).not.toBeInViewport(); - await scrollToFooter(receivingDataPage.page); - await expect(receivingDataPage.footer).toBeInViewport(); - await expect(receivingDataPage.page.getByTestId("govBanner")).not.toBeInViewport(); - await scrollToTop(receivingDataPage.page); - await expect(receivingDataPage.page.getByTestId("govBanner")).toBeInViewport(); + test("has footer and explicit scroll to footer and scroll to top", async ({ receivingDataPage }) => { + await receivingDataPage.testFooter(); }); }); }); diff --git a/frontend-react/e2e/spec/all/getting-started/sending-data.spec.ts b/frontend-react/e2e/spec/all/public/getting-started/sending-data.spec.ts similarity index 64% rename from frontend-react/e2e/spec/all/getting-started/sending-data.spec.ts rename to frontend-react/e2e/spec/all/public/getting-started/sending-data.spec.ts index 3e1b9f5544d..fc915c2c7cd 100644 --- a/frontend-react/e2e/spec/all/getting-started/sending-data.spec.ts +++ b/frontend-react/e2e/spec/all/public/getting-started/sending-data.spec.ts @@ -1,7 +1,6 @@ -import site from "../../../../src/content/site.json" assert { type: "json" }; -import { scrollToFooter, scrollToTop } from "../../../helpers/utils"; -import { SendingDataPage } from "../../../pages/getting-started/sending-data.js"; -import { test as baseTest, expect } from "../../../test"; +import site from "../../../../../src/content/site.json" assert { type: "json" }; +import { SendingDataPage } from "../../../../pages/public/getting-started/sending-data.js"; +import { test as baseTest, expect } from "../../../../test"; export interface SendingDataPageFixtures { sendingDataPage: SendingDataPage; @@ -37,9 +36,10 @@ const test = baseTest.extend({ }); test.describe("Sending data page", () => { - test("has correct title", async ({ sendingDataPage }) => { - await expect(sendingDataPage.page).toHaveTitle(sendingDataPage.title); - await expect(sendingDataPage.heading).toBeVisible(); + test.describe("Header", () => { + test("has correct title + heading", async ({ sendingDataPage }) => { + await sendingDataPage.testHeader(); + }); }); test("has link to get started with ReportStream", async ({ sendingDataPage }) => { @@ -73,17 +73,8 @@ test.describe("Sending data page", () => { }); test.describe("Footer", () => { - test("has footer", async ({ sendingDataPage }) => { - await expect(sendingDataPage.footer).toBeAttached(); - }); - - test("explicit scroll to footer and then scroll to top", async ({ sendingDataPage }) => { - await expect(sendingDataPage.footer).not.toBeInViewport(); - await scrollToFooter(sendingDataPage.page); - await expect(sendingDataPage.footer).toBeInViewport(); - await expect(sendingDataPage.page.getByTestId("govBanner")).not.toBeInViewport(); - await scrollToTop(sendingDataPage.page); - await expect(sendingDataPage.page.getByTestId("govBanner")).toBeInViewport(); + test("has footer and explicit scroll to footer and scroll to top", async ({ sendingDataPage }) => { + await sendingDataPage.testFooter(); }); }); }); diff --git a/frontend-react/e2e/spec/all/public/homepage.spec.ts b/frontend-react/e2e/spec/all/public/homepage.spec.ts new file mode 100644 index 00000000000..6efc9037c11 --- /dev/null +++ b/frontend-react/e2e/spec/all/public/homepage.spec.ts @@ -0,0 +1,66 @@ +import site from "../../../../src/content/site.json" assert { type: "json" }; +import { HomePage } from "../../../pages/public/homepage"; +import { test as baseTest, expect } from "../../../test"; + +export interface HomePageFixtures { + homePage: HomePage; +} + +const test = baseTest.extend({ + homePage: async ( + { + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + isFrontendWarningsLog, + frontendWarningsLogPath, + }, + use, + ) => { + const page = new HomePage({ + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + isFrontendWarningsLog, + frontendWarningsLogPath, + }); + await page.goto(); + await use(page); + }, +}); + +test.describe( + "Homepage", + { + tag: "@smoke", + }, + () => { + test.describe("Header", () => { + test("has correct title + heading", async ({ homePage }) => { + await homePage.testHeader(); + }); + }); + + test.describe("CTA", () => { + test("has 'Contact us' button", async ({ homePage }) => { + const heroLocator = homePage.page.locator('[class*="hero-wrapper"]'); + const ctaURL = site.forms.connectWithRS.url; + const ctaLink = heroLocator.locator(`a[href="${ctaURL}"]`).first(); + + await expect(ctaLink).toBeVisible(); + }); + }); + + test.describe("Footer", () => { + test("has footer and explicit scroll to footer and scroll to top", async ({ homePage }) => { + await homePage.testFooter(); + }); + }); + }, +); diff --git a/frontend-react/e2e/spec/all/public/managing-your-connection/managing-your-connection-page.spec.ts b/frontend-react/e2e/spec/all/public/managing-your-connection/managing-your-connection-page.spec.ts new file mode 100644 index 00000000000..e10692aa357 --- /dev/null +++ b/frontend-react/e2e/spec/all/public/managing-your-connection/managing-your-connection-page.spec.ts @@ -0,0 +1,90 @@ +import { ManagingYourConnectionPage } from "../../../../pages/public/managing-your-connection/managing-your-connection"; +import { test as baseTest, expect } from "../../../../test"; + +const cards = [ + { + name: "For healthcare organizations", + links: ["Manage your public key", "View your submission history", "Login", "contact us"], + }, + { + name: "For public health agencies", + links: ["Refer healthcare organizations", "View your dashboard", "Login", "contact us"], + }, +]; + +export interface ManagingYourConnectionPageFixtures { + managingYourConnectionPage: ManagingYourConnectionPage; +} + +const test = baseTest.extend({ + managingYourConnectionPage: async ( + { + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + isFrontendWarningsLog, + frontendWarningsLogPath, + }, + use, + ) => { + const page = new ManagingYourConnectionPage({ + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + isFrontendWarningsLog, + frontendWarningsLogPath, + }); + await page.goto(); + await use(page); + }, +}); + +test.describe( + "Managing Your Connection page", + { + tag: "@smoke", + }, + () => { + test.describe("Header", () => { + test("has correct title + heading", async ({ managingYourConnectionPage }) => { + await managingYourConnectionPage.testHeader(); + }); + }); + + test.describe("Quick links", () => { + for (const card of cards) { + test(`should have ${card.name} links`, async ({ managingYourConnectionPage }) => { + const cardHeader = managingYourConnectionPage.page.locator(".usa-card__header", { + hasText: card.name, + }); + + await expect(cardHeader).toBeVisible(); + + const cardContainer = cardHeader.locator(".."); + + for (const link of card.links) { + await expect( + cardContainer.getByRole("link", { + name: `${link}`, + }), + ).toBeVisible(); + } + }); + } + }); + + test.describe("Footer", () => { + test("has footer and explicit scroll to footer and scroll to top", async ({ + managingYourConnectionPage, + }) => { + await managingYourConnectionPage.testFooter(); + }); + }); + }, +); diff --git a/frontend-react/e2e/spec/all/refer-healthcare-page.spec.ts b/frontend-react/e2e/spec/all/public/managing-your-connection/refer-healthcare-page.spec.ts similarity index 63% rename from frontend-react/e2e/spec/all/refer-healthcare-page.spec.ts rename to frontend-react/e2e/spec/all/public/managing-your-connection/refer-healthcare-page.spec.ts index 37421d676e4..27c3c74899c 100644 --- a/frontend-react/e2e/spec/all/refer-healthcare-page.spec.ts +++ b/frontend-react/e2e/spec/all/public/managing-your-connection/refer-healthcare-page.spec.ts @@ -1,6 +1,5 @@ -import { scrollToFooter, scrollToTop } from "../../helpers/utils"; -import { ReferHealthcarePage } from "../../pages/refer-healthcare"; -import { test as baseTest, expect } from "../../test"; +import { ReferHealthcarePage } from "../../../../pages/public/managing-your-connection/refer-healthcare"; +import { test as baseTest, expect } from "../../../../test"; export interface ReferHealthcarePageFixtures { referHealthcarePage: ReferHealthcarePage; @@ -41,9 +40,10 @@ test.describe( tag: "@smoke", }, () => { - test("has correct title", async ({ referHealthcarePage }) => { - await expect(referHealthcarePage.page).toHaveTitle(referHealthcarePage.title); - await expect(referHealthcarePage.heading).toBeVisible(); + test.describe("Header", () => { + test("has correct title + heading", async ({ referHealthcarePage }) => { + await referHealthcarePage.testHeader(); + }); }); test("has correct sidenav items", async ({ referHealthcarePage }) => { @@ -68,17 +68,8 @@ test.describe( }); test.describe("Footer", () => { - test("has footer", async ({ referHealthcarePage }) => { - await expect(referHealthcarePage.footer).toBeAttached(); - }); - - test("explicit scroll to footer and then scroll to top", async ({ referHealthcarePage }) => { - await expect(referHealthcarePage.footer).not.toBeInViewport(); - await scrollToFooter(referHealthcarePage.page); - await expect(referHealthcarePage.footer).toBeInViewport(); - await expect(referHealthcarePage.page.getByTestId("govBanner")).not.toBeInViewport(); - await scrollToTop(referHealthcarePage.page); - await expect(referHealthcarePage.page.getByTestId("govBanner")).toBeInViewport(); + test("has footer and explicit scroll to footer and scroll to top", async ({ referHealthcarePage }) => { + await referHealthcarePage.testFooter(); }); }); }, diff --git a/frontend-react/e2e/spec/all/public/resources-page.spec.ts b/frontend-react/e2e/spec/all/public/resources-page.spec.ts new file mode 100644 index 00000000000..1acd4e1883f --- /dev/null +++ b/frontend-react/e2e/spec/all/public/resources-page.spec.ts @@ -0,0 +1,69 @@ +import { DeveloperResourcesPage } from "../../../pages/public/resources"; +import { test as baseTest } from "../../../test"; + +export interface DeveloperResourcesPageFixtures { + developerResourcesPage: DeveloperResourcesPage; +} + +const test = baseTest.extend({ + developerResourcesPage: async ( + { + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + isFrontendWarningsLog, + frontendWarningsLogPath, + }, + use, + ) => { + const page = new DeveloperResourcesPage({ + page: _page, + isMockDisabled, + adminLogin, + senderLogin, + receiverLogin, + storageState, + isFrontendWarningsLog, + frontendWarningsLogPath, + }); + await page.goto(); + await use(page); + }, +}); + +const cards = [ + { + name: "API guide", + }, + { + name: "GitHub", + }, + { + name: "Release notes", + }, +]; + +test.describe("Developer Resources page", () => { + test.describe("Header", () => { + test("has correct title + heading", async ({ developerResourcesPage }) => { + await developerResourcesPage.testHeader(); + }); + }); + + test.describe("CTA", () => { + for (const card of cards) { + test(`should have ${card.name}`, async ({ developerResourcesPage }) => { + await developerResourcesPage.testCard(card); + }); + } + }); + + test.describe("Footer", () => { + test("has footer and explicit scroll to footer and scroll to top", async ({ developerResourcesPage }) => { + await developerResourcesPage.testFooter(); + }); + }); +}); diff --git a/frontend-react/e2e/spec/all/support-page.spec.ts b/frontend-react/e2e/spec/all/public/support-page.spec.ts similarity index 57% rename from frontend-react/e2e/spec/all/support-page.spec.ts rename to frontend-react/e2e/spec/all/public/support-page.spec.ts index 0948a85b187..fc18518233f 100644 --- a/frontend-react/e2e/spec/all/support-page.spec.ts +++ b/frontend-react/e2e/spec/all/public/support-page.spec.ts @@ -1,7 +1,6 @@ -import site from "../../../src/content/site.json" assert { type: "json" }; -import { scrollToFooter, scrollToTop } from "../../helpers/utils"; -import { SupportPage } from "../../pages/support.js"; -import { test as baseTest, expect } from "../../test"; +import site from "../../../../src/content/site.json" assert { type: "json" }; +import { SupportPage } from "../../../pages/public/support.js"; +import { test as baseTest, expect } from "../../../test"; const cards = [ { @@ -52,15 +51,14 @@ const test = baseTest.extend({ }); test.describe("Support page", () => { - test("has correct title", async ({ supportPage }) => { - await expect(supportPage.page).toHaveTitle(supportPage.title); - await expect(supportPage.heading).toBeVisible(); + test.describe("Header", () => { + test("has correct title + heading", async ({ supportPage }) => { + await supportPage.testHeader(); + }); }); test("Should have a way of contacting support", async ({ supportPage }) => { - const contactLink = supportPage.page - .locator(`a[href="${site.forms.contactUs.url}"]`) - .first(); + const contactLink = supportPage.page.locator(`a[href="${site.forms.contactUs.url}"]`).first(); await contactLink.scrollIntoViewIfNeeded(); await expect(contactLink).toBeVisible(); @@ -78,30 +76,13 @@ test.describe("Support page", () => { const viewAllLink = cardContainer.locator("a").last(); await viewAllLink.click(); - await expect( - supportPage.page.locator(`#${card.anchorID}`), - ).toBeVisible(); + await expect(supportPage.page.locator(`#${card.anchorID}`)).toBeVisible(); }); } test.describe("Footer", () => { - test("has footer", async ({ supportPage }) => { - await expect(supportPage.footer).toBeAttached(); - }); - - test("explicit scroll to footer and then scroll to top", async ({ - supportPage, - }) => { - await expect(supportPage.footer).not.toBeInViewport(); - await scrollToFooter(supportPage.page); - await expect(supportPage.footer).toBeInViewport(); - await expect( - supportPage.page.getByTestId("govBanner"), - ).not.toBeInViewport(); - await scrollToTop(supportPage.page); - await expect( - supportPage.page.getByTestId("govBanner"), - ).toBeInViewport(); + test("has footer and explicit scroll to footer and scroll to top", async ({ supportPage }) => { + await supportPage.testFooter(); }); }); }); diff --git a/frontend-react/e2e/spec/all/resources-page.spec.ts b/frontend-react/e2e/spec/all/resources-page.spec.ts deleted file mode 100644 index ab99383c089..00000000000 --- a/frontend-react/e2e/spec/all/resources-page.spec.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { expect, test } from "@playwright/test"; -import * as resources from "../../pages/resources"; - -// eslint-disable-next-line playwright/no-skipped-test -test.describe.skip("Developer Resources page", () => { - test.beforeEach(async ({ page }) => { - await resources.goto(page); - }); - - test("should have correct title", async ({ page }) => { - await expect(page).toHaveURL(/developer-resources/); - await expect(page).toHaveTitle(/Developer resources/); - }); - - // TODO: Fix - test.describe("Card navigation", () => { - const cardLinks = [ - { - name: "Security practices", - url: "/resources/security-practices", - }, - { - name: "System and settings", - url: "/resources/system-and-settings", - }, - { - name: "ReportStream API", - url: "/resources/api", - }, - { - name: "Guide to submitting data to ReportStream", - url: "/resources/getting-started-submitting-data", - }, - { - name: "Account Registration Guide", - url: "/resources/account-registration-guide", - }, - { - name: "ELR Onboarding Checklist", - url: "/resources/elr-checklist", - }, - { - name: "Guide to receiving ReportStream data", - url: "/resources/getting-started-public-health-departments", - }, - { - name: "Manual data download guide", - url: "/resources/data-download-guide", - }, - { - name: "ReportStream Referral Guide", - url: "/resources/referral-guide", - }, - ]; - - for (const cardLink of cardLinks) { - test(`should have ${cardLink.name} link`, async ({ page }) => { - await page.getByRole("link", { name: cardLink.name }).click(); - - await expect(page).toHaveURL(cardLink.url); - }); - } - - test("should redirect unauthenticated users to login page on managing public key", async ({ - page, - }) => { - await page - .getByRole("link", { - name: "Manage your public key", - }) - .click(); - - await expect(page).toHaveURL("/login"); - }); - }); -}); diff --git a/frontend-react/e2e/spec/all/roadmap.spec.ts b/frontend-react/e2e/spec/all/roadmap.spec.ts deleted file mode 100644 index a2905db3ce5..00000000000 --- a/frontend-react/e2e/spec/all/roadmap.spec.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { expect, test } from "@playwright/test"; - -import * as internalLinks from "../../helpers/internal-links"; -import * as sideNav from "../../pages/about-side-navigation"; -import * as roadmap from "../../pages/roadmap"; -import { URL_ROADMAP } from "../../pages/roadmap"; - -test.describe( - "Product roadmap page", - { - tag: "@smoke", - }, - () => { - test.beforeEach(async ({ page }) => { - await roadmap.goto(page); - }); - - test("has correct title", async ({ page }) => { - await expect(page).toHaveURL(URL_ROADMAP); - await expect(page).toHaveTitle(/Product roadmap/); - }); - - test.describe("Side navigation", () => { - test("has Our network link", async ({ page }) => { - await sideNav.clickNetwork(page); - await expect(page).toHaveURL(/.*about\/our-network/); - }); - - test("has Product roadmap link", async ({ page }) => { - await sideNav.clickRoadmap(page); - await expect(page).toHaveURL(/.*about\/roadmap/); - }); - - test("has News link", async ({ page }) => { - await sideNav.clickNews(page); - await expect(page).toHaveURL(/.*about\/news/); - }); - - test("has Case studies link", async ({ page }) => { - await sideNav.clickCaseStudies(page); - await expect(page).toHaveURL(/.*about\/case-studies/); - }); - - test("has Security link", async ({ page }) => { - await sideNav.clickSecurity(page); - await expect(page).toHaveURL(/.*about\/security/); - }); - - test("has Release notes link", async ({ page }) => { - await sideNav.clickReleaseNotes(page); - await expect(page).toHaveURL(/.*about\/release-notes/); - }); - }); - - test.describe("Additional resources Links", () => { - test("has News", async ({ page }) => { - await internalLinks.clickOnInternalLink("div", "CardGroup", "News", page); - await expect(page).toHaveURL(/.*about\/news/); - }); - - test("has Release notes", async ({ page }) => { - await internalLinks.clickOnInternalLink("div", "CardGroup", "Release notes", page); - await expect(page).toHaveURL(/.*about\/release-notes/); - }); - - test("has Developer resources", async ({ page }) => { - await internalLinks.clickOnInternalLink("div", "CardGroup", "Developer resources", page); - await expect(page).toHaveURL(/.*developer-resources/); - }); - }); - }, -); diff --git a/frontend-react/e2e/spec/all/timezone.spec.ts b/frontend-react/e2e/spec/all/timezone.spec.ts index 8c7c8cd3597..273227412c4 100644 --- a/frontend-react/e2e/spec/all/timezone.spec.ts +++ b/frontend-react/e2e/spec/all/timezone.spec.ts @@ -1,5 +1,7 @@ -import { expect, test } from "@playwright/test"; import { endOfDay, startOfDay } from "date-fns"; +import { test as baseTest, expect } from "../../test"; + +const test = baseTest.extend({}); test("playwright/browser timezone parity", async ({ page }) => { const now = new Date(); @@ -7,9 +9,7 @@ test("playwright/browser timezone parity", async ({ page }) => { const browserNow = new Date(browserNowIso); const timezoneId = Intl.DateTimeFormat().resolvedOptions().timeZoneName; - const browserTimezoneId = await page.evaluate( - () => Intl.DateTimeFormat().resolvedOptions().timeZoneName, - ); + const browserTimezoneId = await page.evaluate(() => Intl.DateTimeFormat().resolvedOptions().timeZoneName); const nowStart = startOfDay(now); const browserStartIso = await page.evaluate(() => { diff --git a/frontend-react/e2e/spec/chromium-only/public-pages-link-check.spec.ts b/frontend-react/e2e/spec/chromium-only/public-pages-link-check.spec.ts index 9c5cd9d9865..8c615cfa158 100644 --- a/frontend-react/e2e/spec/chromium-only/public-pages-link-check.spec.ts +++ b/frontend-react/e2e/spec/chromium-only/public-pages-link-check.spec.ts @@ -1,8 +1,9 @@ /* eslint-disable playwright/no-conditional-in-test */ import axios, { AxiosError } from "axios"; import * as fs from "fs"; -import * as publicPagesLinkCheck from "../../pages/public-pages-link-check"; -import { expect, test } from "../../test"; +import { test as baseTest, expect } from "../../test"; + +const test = baseTest.extend({}); // To save bandwidth, this test is within the /spec/chromium-only/ folder // Since we're just checking link validity. This is specified within our @@ -18,6 +19,8 @@ test.describe("Evaluate links on public facing pages", { tag: "@warning" }, () = const normalizeUrl = (href: string, baseUrl: string) => new URL(href, baseUrl).toString(); // Using our sitemap.xml, we'll create a pathnames array + // We cannot use our POM, we must + // create context manually with browser.newContext() test.beforeAll(async ({ browser }) => { const page = await browser.newPage(); const response = await page.goto("/sitemap.xml"); @@ -52,7 +55,7 @@ test.describe("Evaluate links on public facing pages", { tag: "@warning" }, () = // Set test timeout to be 1 minute instead of 30 seconds test.setTimeout(60000); for (const path of urlPaths) { - await publicPagesLinkCheck.publicPageGoto(page, path); + await page.goto(path); const baseUrl = new URL(page.url()).origin; const allATags = await page.getByRole("link", { includeHidden: true }).elementHandles(); diff --git a/frontend-react/package.json b/frontend-react/package.json index 54587e90a82..fc72052bb0e 100644 --- a/frontend-react/package.json +++ b/frontend-react/package.json @@ -5,10 +5,10 @@ "type": "module", "npmClient": "yarn", "dependencies": { - "@microsoft/applicationinsights-react-js": "^17.3.0", - "@microsoft/applicationinsights-web": "^3.3.0", + "@microsoft/applicationinsights-react-js": "^17.3.1", + "@microsoft/applicationinsights-web": "^3.3.1", "@okta/okta-react": "^6.9.0", - "@okta/okta-signin-widget": "^7.20.1", + "@okta/okta-signin-widget": "^7.21.2", "@rest-hooks/rest": "^3.0.3", "@tanstack/react-query": "^5.51.23", "@tanstack/react-query-devtools": "^5.51.23", @@ -40,7 +40,7 @@ "rehype-slug": "^5.1.0", "rest-hooks": "^6.1.7", "sanitize-html": "^2.13.0", - "tsx": "^4.16.2", + "tsx": "^4.17.0", "use-deep-compare-effect": "^1.8.1", "uuid": "^10.0.0", "web-vitals": "^3.4.0" @@ -111,21 +111,21 @@ "devDependencies": { "@mdx-js/react": "^3.0.1", "@mdx-js/rollup": "^3.0.1", - "@playwright/test": "^1.45.3", + "@playwright/test": "^1.46.1", "@rest-hooks/test": "^7.3.1", - "@storybook/addon-a11y": "^8.2.5", - "@storybook/addon-actions": "^8.2.5", - "@storybook/addon-essentials": "^8.2.5", - "@storybook/addon-interactions": "^8.2.5", - "@storybook/addon-links": "^8.2.5", - "@storybook/blocks": "^8.2.5", - "@storybook/components": "^8.2.5", - "@storybook/core-events": "^8.2.5", + "@storybook/addon-a11y": "^8.2.9", + "@storybook/addon-actions": "^8.2.9", + "@storybook/addon-essentials": "^8.2.9", + "@storybook/addon-interactions": "^8.2.9", + "@storybook/addon-links": "^8.2.9", + "@storybook/blocks": "^8.2.9", + "@storybook/components": "^8.2.9", + "@storybook/core-events": "^8.2.9", "@storybook/mdx2-csf": "1.1.0", - "@storybook/react": "^8.2.5", - "@storybook/react-vite": "^8.2.5", + "@storybook/react": "^8.2.9", + "@storybook/react-vite": "^8.2.9", "@storybook/testing-library": "^0.2.2", - "@storybook/theming": "^8.2.5", + "@storybook/theming": "^8.2.9", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.4.8", "@testing-library/react": "^16.0.0", @@ -148,10 +148,10 @@ "@vitejs/plugin-react": "^4.3.1", "@vitest/coverage-istanbul": "^2.0.4", "@vitest/ui": "^2.0.4", - "autoprefixer": "^10.4.19", - "browserslist": "^4.23.2", + "autoprefixer": "^10.4.20", + "browserslist": "^4.23.3", "browserslist-useragent-regexp": "^4.1.3", - "chromatic": "^11.5.6", + "chromatic": "^11.7.1", "cross-env": "^7.0.3", "dotenv-flow": "^4.1.0", "eslint": "8.57", @@ -165,25 +165,25 @@ "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.7", "eslint-plugin-storybook": "^0.8.0", - "eslint-plugin-testing-library": "^6.2.2", + "eslint-plugin-testing-library": "^6.3.0", "eslint-plugin-vitest": "^0.5.4", - "husky": "^9.1.1", - "jsdom": "^24.1.0", - "lint-staged": "^15.2.7", + "husky": "^9.1.5", + "jsdom": "^24.1.1", + "lint-staged": "^15.2.9", "mockdate": "^3.0.5", - "msw": "^2.3.3", + "msw": "^2.3.5", "msw-storybook-addon": "beta", "npm-run-all": "^4.1.5", "otpauth": "^9.3.1", "patch-package": "^8.0.0", - "postcss": "^8.4.39", + "postcss": "^8.4.41", "prettier": "^3.3.3", "react-error-boundary": "^4.0.13", "remark-frontmatter": "^5.0.0", "remark-mdx-frontmatter": "^5.0.0", "remark-mdx-toc": "^0.3.1", "sass": "^1.77.8", - "storybook": "^8.2.5", + "storybook": "^8.2.9", "storybook-addon-remix-react-router": "^3.0.0", "ts-node": "^10.9.2", "tslib": "^2.6.3", diff --git a/frontend-react/yarn.lock b/frontend-react/yarn.lock index 6282baa003f..24fcf298a0d 100644 --- a/frontend-react/yarn.lock +++ b/frontend-react/yarn.lock @@ -1579,6 +1579,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/aix-ppc64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/aix-ppc64@npm:0.23.0" + conditions: os=aix & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-arm64@npm:0.21.5" @@ -1586,6 +1593,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/android-arm64@npm:0.23.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-arm@npm:0.21.5" @@ -1593,6 +1607,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/android-arm@npm:0.23.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-x64@npm:0.21.5" @@ -1600,6 +1621,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/android-x64@npm:0.23.0" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/darwin-arm64@npm:0.21.5" @@ -1607,6 +1635,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/darwin-arm64@npm:0.23.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/darwin-x64@npm:0.21.5" @@ -1614,6 +1649,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/darwin-x64@npm:0.23.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/freebsd-arm64@npm:0.21.5" @@ -1621,6 +1663,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/freebsd-arm64@npm:0.23.0" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/freebsd-x64@npm:0.21.5" @@ -1628,6 +1677,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/freebsd-x64@npm:0.23.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-arm64@npm:0.21.5" @@ -1635,6 +1691,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-arm64@npm:0.23.0" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-arm@npm:0.21.5" @@ -1642,6 +1705,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-arm@npm:0.23.0" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-ia32@npm:0.21.5" @@ -1649,6 +1719,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-ia32@npm:0.23.0" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-loong64@npm:0.21.5" @@ -1656,6 +1733,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-loong64@npm:0.23.0" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-mips64el@npm:0.21.5" @@ -1663,6 +1747,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-mips64el@npm:0.23.0" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-ppc64@npm:0.21.5" @@ -1670,6 +1761,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-ppc64@npm:0.23.0" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-riscv64@npm:0.21.5" @@ -1677,6 +1775,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-riscv64@npm:0.23.0" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-s390x@npm:0.21.5" @@ -1684,6 +1789,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-s390x@npm:0.23.0" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-x64@npm:0.21.5" @@ -1691,6 +1803,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/linux-x64@npm:0.23.0" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/netbsd-x64@npm:0.21.5" @@ -1698,6 +1817,20 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/netbsd-x64@npm:0.23.0" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + +"@esbuild/openbsd-arm64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/openbsd-arm64@npm:0.23.0" + conditions: os=openbsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/openbsd-x64@npm:0.21.5" @@ -1705,6 +1838,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/openbsd-x64@npm:0.23.0" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/sunos-x64@npm:0.21.5" @@ -1712,6 +1852,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/sunos-x64@npm:0.23.0" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-arm64@npm:0.21.5" @@ -1719,6 +1866,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/win32-arm64@npm:0.23.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-ia32@npm:0.21.5" @@ -1726,6 +1880,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/win32-ia32@npm:0.23.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-x64@npm:0.21.5" @@ -1733,6 +1894,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.23.0": + version: 0.23.0 + resolution: "@esbuild/win32-x64@npm:0.23.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.0 resolution: "@eslint-community/eslint-utils@npm:4.4.0" @@ -1996,70 +2164,70 @@ __metadata: languageName: node linkType: hard -"@microsoft/applicationinsights-analytics-js@npm:3.3.0": - version: 3.3.0 - resolution: "@microsoft/applicationinsights-analytics-js@npm:3.3.0" +"@microsoft/applicationinsights-analytics-js@npm:3.3.1": + version: 3.3.1 + resolution: "@microsoft/applicationinsights-analytics-js@npm:3.3.1" dependencies: - "@microsoft/applicationinsights-common": 3.3.0 - "@microsoft/applicationinsights-core-js": 3.3.0 + "@microsoft/applicationinsights-common": 3.3.1 + "@microsoft/applicationinsights-core-js": 3.3.1 "@microsoft/applicationinsights-shims": 3.0.1 "@microsoft/dynamicproto-js": ^2.0.3 "@nevware21/ts-utils": ">= 0.11.3 < 2.x" peerDependencies: tslib: "*" - checksum: c36d86e5150cc06f2921d7b8edcabd6aead4afc54ce6ed862f55bb9a7cb1d3597a337eacc7792e1053b6ff6a71e8de4e4c025faf1736ea2e4e9a30ccec2b7b7d + checksum: e5c34f3cbafdd81a2e5ff87922d3b80754b41dccc50359ee407b4af70cba8e4ed57476f12a87364c7fbef33953ffe9bacdaf70c0e03eeb2a4fc3f447cb39f7ab languageName: node linkType: hard -"@microsoft/applicationinsights-cfgsync-js@npm:3.3.0": - version: 3.3.0 - resolution: "@microsoft/applicationinsights-cfgsync-js@npm:3.3.0" +"@microsoft/applicationinsights-cfgsync-js@npm:3.3.1": + version: 3.3.1 + resolution: "@microsoft/applicationinsights-cfgsync-js@npm:3.3.1" dependencies: - "@microsoft/applicationinsights-common": 3.3.0 - "@microsoft/applicationinsights-core-js": 3.3.0 + "@microsoft/applicationinsights-common": 3.3.1 + "@microsoft/applicationinsights-core-js": 3.3.1 "@microsoft/applicationinsights-shims": 3.0.1 "@microsoft/dynamicproto-js": ^2.0.3 "@nevware21/ts-async": ">= 0.5.2 < 2.x" "@nevware21/ts-utils": ">= 0.11.3 < 2.x" peerDependencies: tslib: "*" - checksum: 14520a9bcd83f2d88b2baac7c5417a65a7391da51ff458d1969e8501f410b0ee000f8926f2382b57472952399cc1383ace69fcadabf8007f26fe62a9f75ebe7f + checksum: eab8ba79732bceeba93f5888006a23321e33cf46c9c30c77ab325782f1e0b3621c9851ac6a7d0813398ff16a62833b6812fd0caccdb36760700f34fdaf09f463 languageName: node linkType: hard -"@microsoft/applicationinsights-channel-js@npm:3.3.0": - version: 3.3.0 - resolution: "@microsoft/applicationinsights-channel-js@npm:3.3.0" +"@microsoft/applicationinsights-channel-js@npm:3.3.1": + version: 3.3.1 + resolution: "@microsoft/applicationinsights-channel-js@npm:3.3.1" dependencies: - "@microsoft/applicationinsights-common": 3.3.0 - "@microsoft/applicationinsights-core-js": 3.3.0 + "@microsoft/applicationinsights-common": 3.3.1 + "@microsoft/applicationinsights-core-js": 3.3.1 "@microsoft/applicationinsights-shims": 3.0.1 "@microsoft/dynamicproto-js": ^2.0.3 "@nevware21/ts-async": ">= 0.5.2 < 2.x" "@nevware21/ts-utils": ">= 0.11.3 < 2.x" peerDependencies: tslib: "*" - checksum: 72c816e3d7007e0600eccaa0fd22f14869ec27f6c52f391e49648b78a36e5afbb0c563b3bb34e1638e0fe537739872c25154b6ef7ef9f128206c42bb00244ae4 + checksum: 880bec408d65620e1585996a1f78119114ed14e12fa81277f9c70799065d7f4471a1e771c498b3ff0cee009edd3559412ac45eb73099168092d013bf49481cdc languageName: node linkType: hard -"@microsoft/applicationinsights-common@npm:3.3.0, @microsoft/applicationinsights-common@npm:^3.3.0": - version: 3.3.0 - resolution: "@microsoft/applicationinsights-common@npm:3.3.0" +"@microsoft/applicationinsights-common@npm:3.3.1, @microsoft/applicationinsights-common@npm:^3.3.1": + version: 3.3.1 + resolution: "@microsoft/applicationinsights-common@npm:3.3.1" dependencies: - "@microsoft/applicationinsights-core-js": 3.3.0 + "@microsoft/applicationinsights-core-js": 3.3.1 "@microsoft/applicationinsights-shims": 3.0.1 "@microsoft/dynamicproto-js": ^2.0.3 "@nevware21/ts-utils": ">= 0.11.3 < 2.x" peerDependencies: tslib: "*" - checksum: 806aa5f3c2b95978aac2c8df75a3dafedb62503d3e99031c1fbdc08305a121eb20b3948a8f80dd807c93cec92a77eb6931c0465cb01a4b0d038e9190ac1f4fd9 + checksum: 3d2762807812f3dacc4675bce8ecad529d41d4990f4be3e1123047797bfcd42d8fa69b551ae722fbc5e1d42feb3ac62a17ca0f9cb047ff219154072aa5050c23 languageName: node linkType: hard -"@microsoft/applicationinsights-core-js@npm:3.3.0, @microsoft/applicationinsights-core-js@npm:^3.3.0": - version: 3.3.0 - resolution: "@microsoft/applicationinsights-core-js@npm:3.3.0" +"@microsoft/applicationinsights-core-js@npm:3.3.1, @microsoft/applicationinsights-core-js@npm:^3.3.1": + version: 3.3.1 + resolution: "@microsoft/applicationinsights-core-js@npm:3.3.1" dependencies: "@microsoft/applicationinsights-shims": 3.0.1 "@microsoft/dynamicproto-js": ^2.0.3 @@ -2067,47 +2235,47 @@ __metadata: "@nevware21/ts-utils": ">= 0.11.3 < 2.x" peerDependencies: tslib: "*" - checksum: 82dca7c2e1036de5db8ce3b0bb47a20f9bca5baaaa209449a40f875df384ead36a325b6c4209a1eaa1e95f4476cb6886e570e6a65ff588d35999561874636465 + checksum: e51dca60df15e576321c1a9aeea70357d7eb29b3ed08c5f361a3919f5100fa4736b8778397ee0995c2683f988d38fc7e45b6b0943cda1e799cbffb852477624f languageName: node linkType: hard -"@microsoft/applicationinsights-dependencies-js@npm:3.3.0": - version: 3.3.0 - resolution: "@microsoft/applicationinsights-dependencies-js@npm:3.3.0" +"@microsoft/applicationinsights-dependencies-js@npm:3.3.1": + version: 3.3.1 + resolution: "@microsoft/applicationinsights-dependencies-js@npm:3.3.1" dependencies: - "@microsoft/applicationinsights-common": 3.3.0 - "@microsoft/applicationinsights-core-js": 3.3.0 + "@microsoft/applicationinsights-common": 3.3.1 + "@microsoft/applicationinsights-core-js": 3.3.1 "@microsoft/applicationinsights-shims": 3.0.1 "@microsoft/dynamicproto-js": ^2.0.3 "@nevware21/ts-async": ">= 0.5.2 < 2.x" "@nevware21/ts-utils": ">= 0.11.3 < 2.x" peerDependencies: tslib: "*" - checksum: aa8f68d394dbef59b81100e9c9a1972161822e5031bd12bf1d0d01928cf7f2cec28c2105a494965cfda2e12718a5194c6488ac976f22a5dfb79c565b3d62dcfa + checksum: 30cf9882fa3b8fff62b6dd1a1ac27f00b466eac4a22394c284761a56f799857ba1711f1acec33168c1872f42938068a18de9b74c323460fc96d82e72792acadd languageName: node linkType: hard -"@microsoft/applicationinsights-properties-js@npm:3.3.0": - version: 3.3.0 - resolution: "@microsoft/applicationinsights-properties-js@npm:3.3.0" +"@microsoft/applicationinsights-properties-js@npm:3.3.1": + version: 3.3.1 + resolution: "@microsoft/applicationinsights-properties-js@npm:3.3.1" dependencies: - "@microsoft/applicationinsights-common": 3.3.0 - "@microsoft/applicationinsights-core-js": 3.3.0 + "@microsoft/applicationinsights-common": 3.3.1 + "@microsoft/applicationinsights-core-js": 3.3.1 "@microsoft/applicationinsights-shims": 3.0.1 "@microsoft/dynamicproto-js": ^2.0.3 "@nevware21/ts-utils": ">= 0.11.3 < 2.x" peerDependencies: tslib: "*" - checksum: 353b4ff85baa653c7025b5d0d87a8238bf941219773431ff81d22bb5686217e363ea9f4ac32b512399a07b4beda99ccf3766832195915a7fa860d70492b5ba16 + checksum: 5a229fe7920101d2ff118a2e3c3d83b22cb11ba21b86ddcdf46a37f93d4f62dd2ee08a109ff49d3aaa5b922c5b793677b171035d642c8be6421be7e3bb214ae8 languageName: node linkType: hard -"@microsoft/applicationinsights-react-js@npm:^17.3.0": - version: 17.3.0 - resolution: "@microsoft/applicationinsights-react-js@npm:17.3.0" +"@microsoft/applicationinsights-react-js@npm:^17.3.1": + version: 17.3.1 + resolution: "@microsoft/applicationinsights-react-js@npm:17.3.1" dependencies: - "@microsoft/applicationinsights-common": ^3.3.0 - "@microsoft/applicationinsights-core-js": ^3.3.0 + "@microsoft/applicationinsights-common": ^3.3.1 + "@microsoft/applicationinsights-core-js": ^3.3.1 "@microsoft/applicationinsights-shims": ^3.0.1 "@microsoft/dynamicproto-js": ^2.0.3 "@nevware21/ts-utils": ">= 0.11.3 < 2.x" @@ -2115,7 +2283,7 @@ __metadata: history: ">= 4.10.1" react: ">= 17.0.1" tslib: "*" - checksum: 73c1e72ff2324e78ba60afdf5c496834d81ad8f5c25159988b236e308b6ec320fed71c6ee8e6e4fa661a023de8dd99610e8c249b52655e669bc3111c9286f58c + checksum: 954b2fb18d0b0e2d78f082236a42f2b9b830e1c4b251c9328ef633ff0561db34a17c6e958c68b534c5c5cc2393466a96d737d8d7c8497f222d763f1276d016e9 languageName: node linkType: hard @@ -2128,24 +2296,24 @@ __metadata: languageName: node linkType: hard -"@microsoft/applicationinsights-web@npm:^3.3.0": - version: 3.3.0 - resolution: "@microsoft/applicationinsights-web@npm:3.3.0" - dependencies: - "@microsoft/applicationinsights-analytics-js": 3.3.0 - "@microsoft/applicationinsights-cfgsync-js": 3.3.0 - "@microsoft/applicationinsights-channel-js": 3.3.0 - "@microsoft/applicationinsights-common": 3.3.0 - "@microsoft/applicationinsights-core-js": 3.3.0 - "@microsoft/applicationinsights-dependencies-js": 3.3.0 - "@microsoft/applicationinsights-properties-js": 3.3.0 +"@microsoft/applicationinsights-web@npm:^3.3.1": + version: 3.3.1 + resolution: "@microsoft/applicationinsights-web@npm:3.3.1" + dependencies: + "@microsoft/applicationinsights-analytics-js": 3.3.1 + "@microsoft/applicationinsights-cfgsync-js": 3.3.1 + "@microsoft/applicationinsights-channel-js": 3.3.1 + "@microsoft/applicationinsights-common": 3.3.1 + "@microsoft/applicationinsights-core-js": 3.3.1 + "@microsoft/applicationinsights-dependencies-js": 3.3.1 + "@microsoft/applicationinsights-properties-js": 3.3.1 "@microsoft/applicationinsights-shims": 3.0.1 "@microsoft/dynamicproto-js": ^2.0.3 "@nevware21/ts-async": ">= 0.5.2 < 2.x" "@nevware21/ts-utils": ">= 0.11.3 < 2.x" peerDependencies: tslib: "*" - checksum: 49e6aa32450907b627ac7b16d682193eddfea6ff00fbca3366a66582bf962188c5f0cad64968726257a66c0c40f2ffd98b6eacbf24efe85ed4caf2036b4101e4 + checksum: 3371f4b5f2e278ab2bd320a23687abf94587004a751b509c3704e55d638fa6a8c8b42d8bdaac93de4ae94c4be00b655dac4151665c253042260b7bd63d00bd38 languageName: node linkType: hard @@ -2270,9 +2438,9 @@ __metadata: languageName: node linkType: hard -"@okta/okta-signin-widget@npm:^7.20.1": - version: 7.20.1 - resolution: "@okta/okta-signin-widget@npm:7.20.1" +"@okta/okta-signin-widget@npm:^7.21.2": + version: 7.21.2 + resolution: "@okta/okta-signin-widget@npm:7.21.2" dependencies: "@okta/okta-auth-js": ^7.7.0 "@sindresorhus/to-milliseconds": ^1.0.0 @@ -2296,7 +2464,7 @@ __metadata: dependenciesMeta: fsevents: optional: true - checksum: 43b4286d156d6d17dcc95e528fcc2f8e168693fd7e288bd976fb775b0c8930033fc48ad7318e565365bb763fca6a45e7f372423e967f12390a1ab02eb63c5fb1 + checksum: ac78f2055248aff5d09a50bbe086023cc1aee0b6c10f84e80eeac2488dfbd6e5647b10abb8b65b60a80c754159d50c828ae11a6769e255df588fc7b8b681fd19 languageName: node linkType: hard @@ -2364,14 +2532,14 @@ __metadata: languageName: node linkType: hard -"@playwright/test@npm:^1.45.3": - version: 1.45.3 - resolution: "@playwright/test@npm:1.45.3" +"@playwright/test@npm:^1.46.1": + version: 1.46.1 + resolution: "@playwright/test@npm:1.46.1" dependencies: - playwright: 1.45.3 + playwright: 1.46.1 bin: playwright: cli.js - checksum: 3e2c88d40f98cf94ab7947263804d1ee78c4bb21a35c8dbb64855eed5565ffc688509c5f07bda5438cba6c354374981448dcba3dbe326d1699b4fef75c9ce43d + checksum: 180d90c6421ee6a8c00a1ae65d91d5f69767e2de95c3f5582ac2bdf3a388e621fdd3ee00ecb7836db427bb0d4cee58111c14f133cb62150357d8e4f43e3e2295 languageName: node linkType: hard @@ -2610,21 +2778,21 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-a11y@npm:^8.2.5": - version: 8.2.5 - resolution: "@storybook/addon-a11y@npm:8.2.5" +"@storybook/addon-a11y@npm:^8.2.9": + version: 8.2.9 + resolution: "@storybook/addon-a11y@npm:8.2.9" dependencies: - "@storybook/addon-highlight": 8.2.5 + "@storybook/addon-highlight": 8.2.9 axe-core: ^4.2.0 peerDependencies: - storybook: ^8.2.5 - checksum: 697b19f2c6eb35fb92b3948180b47411cf47d1b8a93f897281fcaa4a68ec34b0890cfcb0c4754b5493631f3ad17561ef254d1ffec12a62952f3062080ae7e79d + storybook: ^8.2.9 + checksum: ccef3d51f276df8ebf7982df45090d0af1f976550ff85d69df271df6d5bb10dece230e60b7c944306fef0ec935423359adccdd2a09891d853aa6b0deefc7726c languageName: node linkType: hard -"@storybook/addon-actions@npm:8.2.5, @storybook/addon-actions@npm:^8.2.5": - version: 8.2.5 - resolution: "@storybook/addon-actions@npm:8.2.5" +"@storybook/addon-actions@npm:8.2.9, @storybook/addon-actions@npm:^8.2.9": + version: 8.2.9 + resolution: "@storybook/addon-actions@npm:8.2.9" dependencies: "@storybook/global": ^5.0.0 "@types/uuid": ^9.0.1 @@ -2632,47 +2800,47 @@ __metadata: polished: ^4.2.2 uuid: ^9.0.0 peerDependencies: - storybook: ^8.2.5 - checksum: 9978463095ebb6623f9f7e05478a73dd2064a34d14002b419d8bfb06d0334cef05f4d685bd1afae718b6e480003c466cd44db55e5cc6a750a8b44157c2ef82a3 + storybook: ^8.2.9 + checksum: 3d9cdc9bcfb4fe2cfc9baece1e29ec77fefbcb92c0c77edd9fcc54db506e55dba419e5b8cb2fe49073108a00e0e808e0d55eceb6772071ed9add864cc9b34fbf languageName: node linkType: hard -"@storybook/addon-backgrounds@npm:8.2.5": - version: 8.2.5 - resolution: "@storybook/addon-backgrounds@npm:8.2.5" +"@storybook/addon-backgrounds@npm:8.2.9": + version: 8.2.9 + resolution: "@storybook/addon-backgrounds@npm:8.2.9" dependencies: "@storybook/global": ^5.0.0 memoizerific: ^1.11.3 ts-dedent: ^2.0.0 peerDependencies: - storybook: ^8.2.5 - checksum: 332d35fed2e6ebf58ad6091a1721d12f56cbc0dcd40d358d6291f7a23c1dcf5c1b632212366756d61281b442888ea3de94634faf8dcbacca03ab672850da16b4 + storybook: ^8.2.9 + checksum: c1ad9c1b1b14dc20be0a36d90821d68d0c8e43cf7621bac5d2216bb20f855ae0824dd8259fb7188cafe8ea5eaf35e08dfa0d5deeb332e29db000fb514c7ff718 languageName: node linkType: hard -"@storybook/addon-controls@npm:8.2.5": - version: 8.2.5 - resolution: "@storybook/addon-controls@npm:8.2.5" +"@storybook/addon-controls@npm:8.2.9": + version: 8.2.9 + resolution: "@storybook/addon-controls@npm:8.2.9" dependencies: dequal: ^2.0.2 lodash: ^4.17.21 ts-dedent: ^2.0.0 peerDependencies: - storybook: ^8.2.5 - checksum: 9b53cec0235c8d70e2cf6b2ae2a55f43cff86951aedf1e064ff8feffa89a3b1a8c5a4c841aace551fd361c49659110d956ae1b8870af680110a0f4abfa1f64a6 + storybook: ^8.2.9 + checksum: 0834692a06a09e5795f5e909081f3c7f8b070afbee4c19c3bba291f7d07c88a93f399146499242f533effa31b237937748a4758c072e44262728e4bbbe3cb243 languageName: node linkType: hard -"@storybook/addon-docs@npm:8.2.5": - version: 8.2.5 - resolution: "@storybook/addon-docs@npm:8.2.5" +"@storybook/addon-docs@npm:8.2.9": + version: 8.2.9 + resolution: "@storybook/addon-docs@npm:8.2.9" dependencies: "@babel/core": ^7.24.4 "@mdx-js/react": ^3.0.0 - "@storybook/blocks": 8.2.5 - "@storybook/csf-plugin": 8.2.5 + "@storybook/blocks": 8.2.9 + "@storybook/csf-plugin": 8.2.9 "@storybook/global": ^5.0.0 - "@storybook/react-dom-shim": 8.2.5 + "@storybook/react-dom-shim": 8.2.9 "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 fs-extra: ^11.1.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -2681,121 +2849,121 @@ __metadata: rehype-slug: ^6.0.0 ts-dedent: ^2.0.0 peerDependencies: - storybook: ^8.2.5 - checksum: 0c1dda3af6c9c88088c04b2eaecb45eaa4655deb60f1e12bdee6a478068bdb32b2dc99f426613309021599cdbbf520ec160eb27afb14942c76063be5b0665918 + storybook: ^8.2.9 + checksum: 40eb37c287df1b89ced0bee511146bccaf4279001f4460947bd852d14f5e41d21b80ebd00881b571b2a7024b89bbfde002f099c47b5f41a8b7164b554830d3fb languageName: node linkType: hard -"@storybook/addon-essentials@npm:^8.2.5": - version: 8.2.5 - resolution: "@storybook/addon-essentials@npm:8.2.5" +"@storybook/addon-essentials@npm:^8.2.9": + version: 8.2.9 + resolution: "@storybook/addon-essentials@npm:8.2.9" dependencies: - "@storybook/addon-actions": 8.2.5 - "@storybook/addon-backgrounds": 8.2.5 - "@storybook/addon-controls": 8.2.5 - "@storybook/addon-docs": 8.2.5 - "@storybook/addon-highlight": 8.2.5 - "@storybook/addon-measure": 8.2.5 - "@storybook/addon-outline": 8.2.5 - "@storybook/addon-toolbars": 8.2.5 - "@storybook/addon-viewport": 8.2.5 + "@storybook/addon-actions": 8.2.9 + "@storybook/addon-backgrounds": 8.2.9 + "@storybook/addon-controls": 8.2.9 + "@storybook/addon-docs": 8.2.9 + "@storybook/addon-highlight": 8.2.9 + "@storybook/addon-measure": 8.2.9 + "@storybook/addon-outline": 8.2.9 + "@storybook/addon-toolbars": 8.2.9 + "@storybook/addon-viewport": 8.2.9 ts-dedent: ^2.0.0 peerDependencies: - storybook: ^8.2.5 - checksum: c709de585e58c846afe0ec5dbca8d1f11e8bc9e2c8b5f8699c6c9d0655e8b467f82a068fed78616b8d7e34b84cbf9577689678678e6afa2894128b961376637e + storybook: ^8.2.9 + checksum: 70cc46b9188cf61a30af578fa79d15135e6c51e9406f9d044668fd395c4c93b9a408481039da6dc824100016dd76da711daef79897252e982382d2262292103d languageName: node linkType: hard -"@storybook/addon-highlight@npm:8.2.5": - version: 8.2.5 - resolution: "@storybook/addon-highlight@npm:8.2.5" +"@storybook/addon-highlight@npm:8.2.9": + version: 8.2.9 + resolution: "@storybook/addon-highlight@npm:8.2.9" dependencies: "@storybook/global": ^5.0.0 peerDependencies: - storybook: ^8.2.5 - checksum: 78f7f8dff47bc5e73fdabd8676e2392017b4b4259f6da23c9fa8d3baf6e25e77a07eafd389ff8a09e5ccd5ee3dee1f084e3819c4ba118ef57d68ba0a7a26bc5c + storybook: ^8.2.9 + checksum: 119623ff7396473ea052691c8a8924065347bc1bd6b43d75491b0751f6ebaec4f129b764765079c03a9b5105a44bab5d08f65a0a8d6b9c527667f7273cb0881b languageName: node linkType: hard -"@storybook/addon-interactions@npm:^8.2.5": - version: 8.2.5 - resolution: "@storybook/addon-interactions@npm:8.2.5" +"@storybook/addon-interactions@npm:^8.2.9": + version: 8.2.9 + resolution: "@storybook/addon-interactions@npm:8.2.9" dependencies: "@storybook/global": ^5.0.0 - "@storybook/instrumenter": 8.2.5 - "@storybook/test": 8.2.5 + "@storybook/instrumenter": 8.2.9 + "@storybook/test": 8.2.9 polished: ^4.2.2 ts-dedent: ^2.2.0 peerDependencies: - storybook: ^8.2.5 - checksum: ac29240c815ba62a3b460f59d6a1c63492a6055b7cd10965dcdee118d310729c19400e8ab0251f49ac6f05169038cc3d4f4a1c8de62f624b4f4b6ba4e4e48d00 + storybook: ^8.2.9 + checksum: c9ed8734081aace1a6b936bd7f72803c6d2960476959c57fc8477cc3f8b10eb1887f6c2bd47239e9ca4f74c74cccdf6657758fd39e7c72485d86ddfb81d32d2c languageName: node linkType: hard -"@storybook/addon-links@npm:^8.2.5": - version: 8.2.5 - resolution: "@storybook/addon-links@npm:8.2.5" +"@storybook/addon-links@npm:^8.2.9": + version: 8.2.9 + resolution: "@storybook/addon-links@npm:8.2.9" dependencies: "@storybook/csf": 0.1.11 "@storybook/global": ^5.0.0 ts-dedent: ^2.0.0 peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.2.5 + storybook: ^8.2.9 peerDependenciesMeta: react: optional: true - checksum: b260051502ae327d28dfd9cebd5c968546d78cac90dc7712eb16333d10550a3cda74fe0551a60b7b5e8874996b6e0705b83307ad32f99db87a1cbe6d7f7023fb + checksum: e6b14e2cb6763f25027965c90404afb2572b27298e3d1fafa136176113ad4296b1ce48eaa8caf4f521fb6d3404921f17eb3cbe62061ebdc5f2324e0c85333742 languageName: node linkType: hard -"@storybook/addon-measure@npm:8.2.5": - version: 8.2.5 - resolution: "@storybook/addon-measure@npm:8.2.5" +"@storybook/addon-measure@npm:8.2.9": + version: 8.2.9 + resolution: "@storybook/addon-measure@npm:8.2.9" dependencies: "@storybook/global": ^5.0.0 tiny-invariant: ^1.3.1 peerDependencies: - storybook: ^8.2.5 - checksum: ae82a633b81bb66ef8ef838d06f897b88a0d8f420124943813ba93724a961242a703ce2d9cc0c2f35ebf8a20161c1f324f9c9c6a84176866b5cb10600fc9a1bc + storybook: ^8.2.9 + checksum: 71c16380a0fce731a588d5341726bb4ab592e499d1e48b16b2a652bf7908cc41fa8eb6a20aec961bf3ac7a65821bb3a33e9967a39a66f888791a5c6bebc50f08 languageName: node linkType: hard -"@storybook/addon-outline@npm:8.2.5": - version: 8.2.5 - resolution: "@storybook/addon-outline@npm:8.2.5" +"@storybook/addon-outline@npm:8.2.9": + version: 8.2.9 + resolution: "@storybook/addon-outline@npm:8.2.9" dependencies: "@storybook/global": ^5.0.0 ts-dedent: ^2.0.0 peerDependencies: - storybook: ^8.2.5 - checksum: 1aca00b1da68561bd920281148e2e9d74b8b57554dc2646874a0731c056b371ec2a540420a808286eebbfb720fea2d4487997c08627773d73db1b9208eb87a7e + storybook: ^8.2.9 + checksum: e1a60d7ededadf7dd9a9e0c20945c18416889e2b0607cb92cb4a09a7f97ceeafa74de5d387d781a88a4422e2e3052c51b2a9a157879c381eaf82ffd1116edc31 languageName: node linkType: hard -"@storybook/addon-toolbars@npm:8.2.5": - version: 8.2.5 - resolution: "@storybook/addon-toolbars@npm:8.2.5" +"@storybook/addon-toolbars@npm:8.2.9": + version: 8.2.9 + resolution: "@storybook/addon-toolbars@npm:8.2.9" peerDependencies: - storybook: ^8.2.5 - checksum: 685f3765799a8192d5e0f3f4b73c7cba2d6bcafb88867329696a1a93250cb2c107162d918a4fe3bfdb8b207ce7da7b4b1bcd2d410399dc3a9a7e5fe1d48f0dc0 + storybook: ^8.2.9 + checksum: fd77ce2087c88c518ac3b740f45bdcaf2ea42f766116f572c0edc02fa33531930de71623791a791121142dcde78c0d3d2b4549d0d22d099a8728cf4b8434a1e2 languageName: node linkType: hard -"@storybook/addon-viewport@npm:8.2.5": - version: 8.2.5 - resolution: "@storybook/addon-viewport@npm:8.2.5" +"@storybook/addon-viewport@npm:8.2.9": + version: 8.2.9 + resolution: "@storybook/addon-viewport@npm:8.2.9" dependencies: memoizerific: ^1.11.3 peerDependencies: - storybook: ^8.2.5 - checksum: 96acc774bbd65d90fa7be729a901698f78d0c2ff99c046881f9564c0a74385b399fb390808059b7146f68e50e257a6abaf24c90043a0e5ab0548e990c19de657 + storybook: ^8.2.9 + checksum: a7817d4c643d351da6a43f432cf03c91625dccf56b57d597550c7d5669f1d461944e93b7f4a9d00cfbdccaf3fb41cfa986b91c06477d00dbbf9dd9e86930bec3 languageName: node linkType: hard -"@storybook/blocks@npm:8.2.5, @storybook/blocks@npm:^8.2.5": - version: 8.2.5 - resolution: "@storybook/blocks@npm:8.2.5" +"@storybook/blocks@npm:8.2.9, @storybook/blocks@npm:^8.2.9": + version: 8.2.9 + resolution: "@storybook/blocks@npm:8.2.9" dependencies: "@storybook/csf": 0.1.11 "@storybook/global": ^5.0.0 @@ -2814,21 +2982,21 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.2.5 + storybook: ^8.2.9 peerDependenciesMeta: react: optional: true react-dom: optional: true - checksum: 91de0c443111f591fac114c906fa655d3d7750132f102c4d669e75a481382faca195f863baf265e2c66d86e0df9013129f1ff04c59a8c91758299e49a3496d01 + checksum: 6d55597046905aabd6af95180a66ccb3944f872d0d3f624e5e33f2f939dbf040b0ad2921d44d1612e31e24afaa4f1000a805573854916137d9212b35e7249f16 languageName: node linkType: hard -"@storybook/builder-vite@npm:8.2.5": - version: 8.2.5 - resolution: "@storybook/builder-vite@npm:8.2.5" +"@storybook/builder-vite@npm:8.2.9": + version: 8.2.9 + resolution: "@storybook/builder-vite@npm:8.2.9" dependencies: - "@storybook/csf-plugin": 8.2.5 + "@storybook/csf-plugin": 8.2.9 "@types/find-cache-dir": ^3.2.1 browser-assert: ^1.2.1 es-module-lexer: ^1.5.0 @@ -2839,7 +3007,7 @@ __metadata: ts-dedent: ^2.0.0 peerDependencies: "@preact/preset-vite": "*" - storybook: ^8.2.5 + storybook: ^8.2.9 typescript: ">= 4.3.x" vite: ^4.0.0 || ^5.0.0 vite-plugin-glimmerx: "*" @@ -2850,18 +3018,18 @@ __metadata: optional: true vite-plugin-glimmerx: optional: true - checksum: 8459d9319782baad194ad228c9794da58558a8c3563f79fd6380ce076528f2388b260be9eb70b558553f364131566e4d4bb26fcdcb8acaca3a31353ffbc7ad73 + checksum: 54f8c574854e888b7db8dc030e7f99a3ec1d514a5061015405d73f0ab2ad5ff950d63a7a929b0ab7f5179b836940aaf610c692b7a992940ec3f9f6655e967c88 languageName: node linkType: hard -"@storybook/codemod@npm:8.2.5": - version: 8.2.5 - resolution: "@storybook/codemod@npm:8.2.5" +"@storybook/codemod@npm:8.2.9": + version: 8.2.9 + resolution: "@storybook/codemod@npm:8.2.9" dependencies: "@babel/core": ^7.24.4 "@babel/preset-env": ^7.24.4 "@babel/types": ^7.24.0 - "@storybook/core": 8.2.5 + "@storybook/core": 8.2.9 "@storybook/csf": 0.1.11 "@types/cross-spawn": ^6.0.2 cross-spawn: ^7.0.3 @@ -2871,31 +3039,31 @@ __metadata: prettier: ^3.1.1 recast: ^0.23.5 tiny-invariant: ^1.3.1 - checksum: ccbd1e7619d8b3608fcbc24af2f9357f2c97a4c1c087052cf9d85297949c3474b30aa4b40c8a7f085416119000a5c816a4eca296e97a6607f12b13795f429949 + checksum: 447d8bac0943e503f1ed9f437f48ca79adc596197bd1fb118387813a64ce86587065a4745bd69c652177f5a1bf55da9a9ea98cb51d259adab8bab093014d2b47 languageName: node linkType: hard -"@storybook/components@npm:^8.2.5": - version: 8.2.5 - resolution: "@storybook/components@npm:8.2.5" +"@storybook/components@npm:^8.2.9": + version: 8.2.9 + resolution: "@storybook/components@npm:8.2.9" peerDependencies: - storybook: ^8.2.5 - checksum: 6c2b615a9ac782ff319f2089357482aad97bf26c040b2bb87c82c4ba3223f82d4b97372830670938dff0cd6cbf448a6e07bf4e188f80830a1f8e77c338e1976d + storybook: ^8.2.9 + checksum: 32153df19777ea751d1adb33da6e92b5647d19fd924753b2ca1c80bcc102ee0489eac7b02914a2e56406233940f6eca7f1aa3ba0c441a164cabc954679a67869 languageName: node linkType: hard -"@storybook/core-events@npm:^8.2.5": - version: 8.2.5 - resolution: "@storybook/core-events@npm:8.2.5" +"@storybook/core-events@npm:^8.2.9": + version: 8.2.9 + resolution: "@storybook/core-events@npm:8.2.9" peerDependencies: - storybook: ^8.2.5 - checksum: 4c92be909bfb3616e445f433aa9720b2b6fa62e4ad64124d5b22acf6a01ffbc3b4521e994ac30f1b5e05acc7cbf90994e9c27def210edcf5d1afe45ca1886eb3 + storybook: ^8.2.9 + checksum: 6ac658a75702c645695d82fbd69da5cf4d559050ffa1f0023729ad34c0d84965b2abeeb65efd168b0cdb049314de002c00267eaf692064e3efeae1337cc3ba52 languageName: node linkType: hard -"@storybook/core@npm:8.2.5": - version: 8.2.5 - resolution: "@storybook/core@npm:8.2.5" +"@storybook/core@npm:8.2.9": + version: 8.2.9 + resolution: "@storybook/core@npm:8.2.9" dependencies: "@storybook/csf": 0.1.11 "@types/express": ^4.17.21 @@ -2908,18 +3076,18 @@ __metadata: recast: ^0.23.5 util: ^0.12.4 ws: ^8.2.3 - checksum: 264bbc76efec9cef026c1d8ecb1e5803ec4d485a36232222fcff600231c3e641b322416d9bfae93fd3f90e1228ea20e46a0bd538ff7a28fe126d2f34be88125d + checksum: 52836a0763f192fad7dd1811621a362695889fb03dd4d1b931435a52fee440178db9c28f0de9fd56bb241683c0ff028272f180ccc7e8c748977382b2b3cc63d8 languageName: node linkType: hard -"@storybook/csf-plugin@npm:8.2.5": - version: 8.2.5 - resolution: "@storybook/csf-plugin@npm:8.2.5" +"@storybook/csf-plugin@npm:8.2.9": + version: 8.2.9 + resolution: "@storybook/csf-plugin@npm:8.2.9" dependencies: unplugin: ^1.3.1 peerDependencies: - storybook: ^8.2.5 - checksum: 28ab567521c14d127382ed5ea7caff6bcec8b149554f140df2bddfdeba78a2b6080ca4cd2bf1f67b9a05bc761c4eff5b50a5d7e565334898ae11412a865cea3f + storybook: ^8.2.9 + checksum: 514171f66a4e71849ee7a4efacc3051de0714fda56dfdb7581f5d08a268d5a9d0bee6264404bd766f631f2ab8a0358b4c226ecfcee8965d8560d1afc5d17c1b9 languageName: node linkType: hard @@ -2958,25 +3126,25 @@ __metadata: languageName: node linkType: hard -"@storybook/instrumenter@npm:8.2.5": - version: 8.2.5 - resolution: "@storybook/instrumenter@npm:8.2.5" +"@storybook/instrumenter@npm:8.2.9": + version: 8.2.9 + resolution: "@storybook/instrumenter@npm:8.2.9" dependencies: "@storybook/global": ^5.0.0 "@vitest/utils": ^1.3.1 util: ^0.12.4 peerDependencies: - storybook: ^8.2.5 - checksum: 8aa2ca657552e7b89e4c8fd4ed92c5d2312fd58038ee39c27e8fde31cd87b6321a7d60340c48a77768c27108fdba478e18cd958253e00c4e21c8901bf2d2928c + storybook: ^8.2.9 + checksum: 4c834c2ee700d1521bc59da826f67777554ac3fc216bac25238693624fdae6cfabf31e127495cfb2f9e22d7c29e74e816f89b3c5ec98a34d56968cbf701f4eb8 languageName: node linkType: hard -"@storybook/manager-api@npm:^8.2.5": - version: 8.2.5 - resolution: "@storybook/manager-api@npm:8.2.5" +"@storybook/manager-api@npm:^8.2.9": + version: 8.2.9 + resolution: "@storybook/manager-api@npm:8.2.9" peerDependencies: - storybook: ^8.2.5 - checksum: 2bae088d3a2119f9127bd667f22b675c1083eb121e79ae5d8edba2d814ca800bce9cacc28c9faa723ea0390c7c4d350bf45e0aa0ea020501d2d73b33762581dc + storybook: ^8.2.9 + checksum: 5ee66ebdc88f886c05425acff2c362681a6f778e2f27023a43c6c36767ef265175c58b8f23f74684dba64477b26b310fad7cf1761e6643325325a8724f53d93c languageName: node linkType: hard @@ -2987,34 +3155,34 @@ __metadata: languageName: node linkType: hard -"@storybook/preview-api@npm:^8.2.5": - version: 8.2.5 - resolution: "@storybook/preview-api@npm:8.2.5" +"@storybook/preview-api@npm:^8.2.9": + version: 8.2.9 + resolution: "@storybook/preview-api@npm:8.2.9" peerDependencies: - storybook: ^8.2.5 - checksum: 913bd916660c17b6d741c37da0a070d54140540d55b5e389b9baab1fd179d10b6be5c03710deb4ff48fbbdfabffbc1a42a492549a2aa34063a65e399c662747b + storybook: ^8.2.9 + checksum: 4973ca3ede45e363a54071a200ee1aeeea9c735ee1e6ba93d46c756bae905057a0e6c30882964aee14fb1631029c5f86b2a4cb8deaee4a80aa5770bf63a00eb3 languageName: node linkType: hard -"@storybook/react-dom-shim@npm:8.2.5": - version: 8.2.5 - resolution: "@storybook/react-dom-shim@npm:8.2.5" +"@storybook/react-dom-shim@npm:8.2.9": + version: 8.2.9 + resolution: "@storybook/react-dom-shim@npm:8.2.9" peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.2.5 - checksum: 11d4ceee89d592ee6be3ee44e62d6f331dd9298a4f0744db88ad83e5bf8d07417b7ff7ae19f9e03515e1d8575d26b7d6274fd41799237fc02e6f63c3295a13ba + storybook: ^8.2.9 + checksum: 85ecef5aa9af253aae6edb78eaac0e9135f8aa7b58e8a00b7913c13f7a023d7fb8585c459b15900148671236a0d38872d3fee34a48e9c04674ff9adbdede3827 languageName: node linkType: hard -"@storybook/react-vite@npm:^8.2.5": - version: 8.2.5 - resolution: "@storybook/react-vite@npm:8.2.5" +"@storybook/react-vite@npm:^8.2.9": + version: 8.2.9 + resolution: "@storybook/react-vite@npm:8.2.9" dependencies: "@joshwooding/vite-plugin-react-docgen-typescript": 0.3.1 "@rollup/pluginutils": ^5.0.2 - "@storybook/builder-vite": 8.2.5 - "@storybook/react": 8.2.5 + "@storybook/builder-vite": 8.2.9 + "@storybook/react": 8.2.9 find-up: ^5.0.0 magic-string: ^0.30.0 react-docgen: ^7.0.0 @@ -3023,22 +3191,22 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.2.5 + storybook: ^8.2.9 vite: ^4.0.0 || ^5.0.0 - checksum: 555234db319b445ec2e39b2963d8547fadb51158e9237ca09b6348462a6a403c278012ec89894b79f9c9226868ad44b1e5db82ef86f3e0ad7686aee4a331497c + checksum: 78022cd4e49ea5991cf7f56830c697c1d97e83314b26e3ad26c3cba25784c1dbafc6b1693b68f281bbdc4427d004592f1f41b4ec017b7dbbc388ea51cab2d266 languageName: node linkType: hard -"@storybook/react@npm:8.2.5, @storybook/react@npm:^8.2.5": - version: 8.2.5 - resolution: "@storybook/react@npm:8.2.5" +"@storybook/react@npm:8.2.9, @storybook/react@npm:^8.2.9": + version: 8.2.9 + resolution: "@storybook/react@npm:8.2.9" dependencies: - "@storybook/components": ^8.2.5 + "@storybook/components": ^8.2.9 "@storybook/global": ^5.0.0 - "@storybook/manager-api": ^8.2.5 - "@storybook/preview-api": ^8.2.5 - "@storybook/react-dom-shim": 8.2.5 - "@storybook/theming": ^8.2.5 + "@storybook/manager-api": ^8.2.9 + "@storybook/preview-api": ^8.2.9 + "@storybook/react-dom-shim": 8.2.9 + "@storybook/theming": ^8.2.9 "@types/escodegen": ^0.0.6 "@types/estree": ^0.0.51 "@types/node": ^18.0.0 @@ -3057,21 +3225,21 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^8.2.5 + storybook: ^8.2.9 typescript: ">= 4.2.x" peerDependenciesMeta: typescript: optional: true - checksum: 9c31315680cdbe6f31c2490bc30fb60100a2d973862848e28e10b1a739d32e2ac1ad9287899b5368d49db5b6768ff23fb6153b3fc576534699c23c622d27e659 + checksum: 87a6091eccf049e7ba6408b3de47b729ab444de59cc423a2f349fcfd936f2bdf0c258297725516947803063a44ff080ff71e6606f8f3d28a12e667ccef82a760 languageName: node linkType: hard -"@storybook/test@npm:8.2.5": - version: 8.2.5 - resolution: "@storybook/test@npm:8.2.5" +"@storybook/test@npm:8.2.9": + version: 8.2.9 + resolution: "@storybook/test@npm:8.2.9" dependencies: "@storybook/csf": 0.1.11 - "@storybook/instrumenter": 8.2.5 + "@storybook/instrumenter": 8.2.9 "@testing-library/dom": 10.1.0 "@testing-library/jest-dom": 6.4.5 "@testing-library/user-event": 14.5.2 @@ -3079,8 +3247,8 @@ __metadata: "@vitest/spy": 1.6.0 util: ^0.12.4 peerDependencies: - storybook: ^8.2.5 - checksum: bad664ffa4a32151ab640f98f586cc5ebf2d7b987c45d83ee44a7abdc87eafe0f004903e1475d68389ae337b5e3c9bf41b196a237fcb5a00292f4f36d62f366f + storybook: ^8.2.9 + checksum: 58809d15fa16772cc8036868e4c6271bafd45de488e6a9a4419c8f2afd380ba4b64cd4783fb1a2c069edf5b02a907246e0cae852ac0e5fd8c6116f24186e6daa languageName: node linkType: hard @@ -3095,12 +3263,12 @@ __metadata: languageName: node linkType: hard -"@storybook/theming@npm:^8.2.5": - version: 8.2.5 - resolution: "@storybook/theming@npm:8.2.5" +"@storybook/theming@npm:^8.2.9": + version: 8.2.9 + resolution: "@storybook/theming@npm:8.2.9" peerDependencies: - storybook: ^8.2.5 - checksum: 7163c186d861b273564d2efd775196c8f798baed7a42eb42db2bee3b759012de67651424465ebaa82299b3e97b742c691899fbd222140f66d5dfa9455fefd19c + storybook: ^8.2.9 + checksum: 13d71e1b69fb254d5de6349c60c7ac0518cbaec0c26a591c820752dd5b25fe66d8a32e4b081bd9470c0b07648e478eb88ab628d10196918ee2806c9640c2ee2f languageName: node linkType: hard @@ -4614,12 +4782,12 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^6.2.0": - version: 6.2.0 - resolution: "ansi-escapes@npm:6.2.0" +"ansi-escapes@npm:^7.0.0": + version: 7.0.0 + resolution: "ansi-escapes@npm:7.0.0" dependencies: - type-fest: ^3.0.0 - checksum: f0bc667d5f1ededc3ea89b73c34f0cba95473525b07e1290ddfd3fc868c94614e95f3549f5c4fd0c05424af7d3fd298101fb3d9a52a597d3782508b340783bd7 + environment: ^1.0.0 + checksum: 19baa61e68d1998c03b3b8bd023653a6c2667f0ed6caa9a00780ffd6f0a14f4a6563c57a38b3c0aba71bd704cd49c4c8df41be60bd81c957409f91e9dd49051f languageName: node linkType: hard @@ -4951,21 +5119,21 @@ __metadata: languageName: node linkType: hard -"autoprefixer@npm:^10.4.19": - version: 10.4.19 - resolution: "autoprefixer@npm:10.4.19" +"autoprefixer@npm:^10.4.20": + version: 10.4.20 + resolution: "autoprefixer@npm:10.4.20" dependencies: - browserslist: ^4.23.0 - caniuse-lite: ^1.0.30001599 + browserslist: ^4.23.3 + caniuse-lite: ^1.0.30001646 fraction.js: ^4.3.7 normalize-range: ^0.1.2 - picocolors: ^1.0.0 + picocolors: ^1.0.1 postcss-value-parser: ^4.2.0 peerDependencies: postcss: ^8.1.0 bin: autoprefixer: bin/autoprefixer - checksum: 3a4bc5bace05e057396dca2b306503efc175e90e8f2abf5472d3130b72da1d54d97c0ee05df21bf04fe66a7df93fd8c8ec0f1aca72a165f4701a02531abcbf11 + checksum: 187cec2ec356631932b212f76dc64f4419c117fdb2fb9eeeb40867d38ba5ca5ba734e6ceefc9e3af4eec8258e60accdf5cbf2b7708798598fde35cdc3de562d6 languageName: node linkType: hard @@ -5188,17 +5356,17 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.23.2": - version: 4.23.2 - resolution: "browserslist@npm:4.23.2" +"browserslist@npm:^4.23.3": + version: 4.23.3 + resolution: "browserslist@npm:4.23.3" dependencies: - caniuse-lite: ^1.0.30001640 - electron-to-chromium: ^1.4.820 - node-releases: ^2.0.14 + caniuse-lite: ^1.0.30001646 + electron-to-chromium: ^1.5.4 + node-releases: ^2.0.18 update-browserslist-db: ^1.1.0 bin: browserslist: cli.js - checksum: 8212af37f6ca6355da191cf2d4ad49bd0b82854888b9a7e103638fada70d38cbe36d28feeeaa98344cb15d9128f9f74bcc8ce1bfc9011b5fd14381c1c6fb542c + checksum: 7906064f9970aeb941310b2fcb8b4ace4a1b50aa657c986677c6f1553a8cabcc94ee9c5922f715baffbedaa0e6cf0831b6fed7b059dde6873a4bfadcbe069c7e languageName: node linkType: hard @@ -5296,17 +5464,17 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001599, caniuse-lite@npm:^1.0.30001629": +"caniuse-lite@npm:^1.0.30001629": version: 1.0.30001636 resolution: "caniuse-lite@npm:1.0.30001636" checksum: b0347fd2c8d346680a64d98b061c59cb8fbf149cdd03005a447fae4d21e6286d5bd161b43eefe3221c6624aacb3cda4e838ae83c95ff5313a547f84ca93bcc70 languageName: node linkType: hard -"caniuse-lite@npm:^1.0.30001640": - version: 1.0.30001643 - resolution: "caniuse-lite@npm:1.0.30001643" - checksum: e39991c13a0fd8f5c2aa99c9128188e4c4e9d6a203c3da6270c36285460ef152c5e9410ee4db560aa723904668946afe50541dce9636ab5e61434ba71dc22955 +"caniuse-lite@npm:^1.0.30001646": + version: 1.0.30001651 + resolution: "caniuse-lite@npm:1.0.30001651" + checksum: c31a5a01288e70cdbbfb5cd94af3df02f295791673173b8ce6d6a16db4394a6999197d44190be5a6ff06b8c2c7d2047e94dfd5e5eb4c103ab000fca2d370afc7 languageName: node linkType: hard @@ -5453,9 +5621,9 @@ __metadata: languageName: node linkType: hard -"chromatic@npm:^11.5.6": - version: 11.5.6 - resolution: "chromatic@npm:11.5.6" +"chromatic@npm:^11.7.1": + version: 11.7.1 + resolution: "chromatic@npm:11.7.1" peerDependencies: "@chromatic-com/cypress": ^0.*.* || ^1.0.0 "@chromatic-com/playwright": ^0.*.* || ^1.0.0 @@ -5468,7 +5636,7 @@ __metadata: chroma: dist/bin.js chromatic: dist/bin.js chromatic-cli: dist/bin.js - checksum: 968ff879301f963e25bed6f7be1f796dee90b06bbc1824004c97aa83dd83a959d39c4639e04e6cd947e5d4bcf0ab09d5908d0960dfb7b5f9483541f222a787ab + checksum: 874c52a04216303af707572186321a2c83c382c2ab287b763dbba9f8690f00d39da48cb3e72f342862882fd10e107f2527ffe1f26ce6b8d07bbef6b20cec76b9 languageName: node linkType: hard @@ -5509,12 +5677,12 @@ __metadata: languageName: node linkType: hard -"cli-cursor@npm:^4.0.0": - version: 4.0.0 - resolution: "cli-cursor@npm:4.0.0" +"cli-cursor@npm:^5.0.0": + version: 5.0.0 + resolution: "cli-cursor@npm:5.0.0" dependencies: - restore-cursor: ^4.0.0 - checksum: ab3f3ea2076e2176a1da29f9d64f72ec3efad51c0960898b56c8a17671365c26e67b735920530eaf7328d61f8bd41c27f46b9cf6e4e10fe2fa44b5e8c0e392cc + restore-cursor: ^5.0.0 + checksum: 1eb9a3f878b31addfe8d82c6d915ec2330cec8447ab1f117f4aa34f0137fbb3137ec3466e1c9a65bcb7557f6e486d343f2da57f253a2f668d691372dfa15c090 languageName: node linkType: hard @@ -5972,7 +6140,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:~4.3.4": +"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.5": version: 4.3.5 resolution: "debug@npm:4.3.5" dependencies: @@ -5993,6 +6161,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:~4.3.6": + version: 4.3.6 + resolution: "debug@npm:4.3.6" + dependencies: + ms: 2.1.2 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 1630b748dea3c581295e02137a9f5cbe2c1d85fea35c1e6597a65ca2b16a6fce68cec61b299d480787ef310ba927dc8c92d3061faba0ad06c6a724672f66be7f + languageName: node + linkType: hard + "decimal.js@npm:^10.4.3": version: 10.4.3 resolution: "decimal.js@npm:10.4.3" @@ -6327,10 +6507,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.820": - version: 1.5.0 - resolution: "electron-to-chromium@npm:1.5.0" - checksum: 82e362dd5851f5ad312ab6699c5d36221deb1fbd4ab2c28f43c3244dd7d28aee28c3239491564dd4bb57f3ed8291a88c4f7983c61aad6db98459e96400923b8d +"electron-to-chromium@npm:^1.5.4": + version: 1.5.12 + resolution: "electron-to-chromium@npm:1.5.12" + checksum: 9ce8d5be88357e71213c12f3d2d47b74666bb17d5dfbd30be77e4c1ed6782c7349803018b0fbefd95bf99ae69ab5918a50cdebf0d5b2c4c9a2e5d0e897d2e32b languageName: node linkType: hard @@ -6411,6 +6591,13 @@ __metadata: languageName: node linkType: hard +"environment@npm:^1.0.0": + version: 1.1.0 + resolution: "environment@npm:1.1.0" + checksum: dd3c1b9825e7f71f1e72b03c2344799ac73f2e9ef81b78ea8b373e55db021786c6b9f3858ea43a436a2c4611052670ec0afe85bc029c384cc71165feee2f4ba6 + languageName: node + linkType: hard + "err-code@npm:^2.0.2": version: 2.0.3 resolution: "err-code@npm:2.0.3" @@ -6601,7 +6788,7 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0, esbuild@npm:^0.21.3, esbuild@npm:~0.21.5": +"esbuild@npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0, esbuild@npm:^0.21.3": version: 0.21.5 resolution: "esbuild@npm:0.21.5" dependencies: @@ -6681,6 +6868,89 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:~0.23.0": + version: 0.23.0 + resolution: "esbuild@npm:0.23.0" + dependencies: + "@esbuild/aix-ppc64": 0.23.0 + "@esbuild/android-arm": 0.23.0 + "@esbuild/android-arm64": 0.23.0 + "@esbuild/android-x64": 0.23.0 + "@esbuild/darwin-arm64": 0.23.0 + "@esbuild/darwin-x64": 0.23.0 + "@esbuild/freebsd-arm64": 0.23.0 + "@esbuild/freebsd-x64": 0.23.0 + "@esbuild/linux-arm": 0.23.0 + "@esbuild/linux-arm64": 0.23.0 + "@esbuild/linux-ia32": 0.23.0 + "@esbuild/linux-loong64": 0.23.0 + "@esbuild/linux-mips64el": 0.23.0 + "@esbuild/linux-ppc64": 0.23.0 + "@esbuild/linux-riscv64": 0.23.0 + "@esbuild/linux-s390x": 0.23.0 + "@esbuild/linux-x64": 0.23.0 + "@esbuild/netbsd-x64": 0.23.0 + "@esbuild/openbsd-arm64": 0.23.0 + "@esbuild/openbsd-x64": 0.23.0 + "@esbuild/sunos-x64": 0.23.0 + "@esbuild/win32-arm64": 0.23.0 + "@esbuild/win32-ia32": 0.23.0 + "@esbuild/win32-x64": 0.23.0 + dependenciesMeta: + "@esbuild/aix-ppc64": + optional: true + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-arm64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 22138538225d5ce79f84fc0d3d3e31b57a91ef50ef00f2d6a9c8a4be4ed28d4b1d0ed14239e54341d1b9a7079f25e69761d0266f3c255da94e647b079b790421 + languageName: node + linkType: hard + "escalade@npm:^3.1.1, escalade@npm:^3.1.2": version: 3.1.2 resolution: "escalade@npm:3.1.2" @@ -6930,14 +7200,14 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-testing-library@npm:^6.2.2": - version: 6.2.2 - resolution: "eslint-plugin-testing-library@npm:6.2.2" +"eslint-plugin-testing-library@npm:^6.3.0": + version: 6.3.0 + resolution: "eslint-plugin-testing-library@npm:6.3.0" dependencies: "@typescript-eslint/utils": ^5.58.0 peerDependencies: eslint: ^7.5.0 || ^8.0.0 - checksum: df55ca98415bad3e7d6b21adcd0f817d7c6df59cb32099528e45b39e8d7edf95e040334106fc7574fd402ed624ca65215ccf37745e1da1a9c90cfe00b99c5be4 + checksum: 4813b3d90d449ebbce8c53389ca356aa63a122c897eea6580fd3e87ee4bcea7e3b56b9822c0e65d78c2cc6490cb0eb3d3b27ca5445e5de60181469718a70f645 languageName: node linkType: hard @@ -8392,13 +8662,13 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^7.0.4": - version: 7.0.4 - resolution: "https-proxy-agent@npm:7.0.4" +"https-proxy-agent@npm:^7.0.5": + version: 7.0.5 + resolution: "https-proxy-agent@npm:7.0.5" dependencies: agent-base: ^7.0.2 debug: 4 - checksum: daaab857a967a2519ddc724f91edbbd388d766ff141b9025b629f92b9408fc83cee8a27e11a907aede392938e9c398e240d643e178408a59e4073539cde8cfe9 + checksum: 2e1a28960f13b041a50702ee74f240add8e75146a5c37fc98f1960f0496710f6918b3a9fe1e5aba41e50f58e6df48d107edd9c405c5f0d73ac260dabf2210857 languageName: node linkType: hard @@ -8425,12 +8695,12 @@ __metadata: languageName: node linkType: hard -"husky@npm:^9.1.1": - version: 9.1.1 - resolution: "husky@npm:9.1.1" +"husky@npm:^9.1.5": + version: 9.1.5 + resolution: "husky@npm:9.1.5" bin: husky: bin.js - checksum: d907eee996353b0b6da8e40b6cf6c1274d57470cd984f028a8baf3e17b7bbab57d796345d552971c6629521685297a1ee093128e4bd45c8fc1867c0dbf38086b + checksum: c240018e852666dc12a93ca84751f1440bdf436468ba872c7b7b3cee54f5f1d7b4222a117988b27ca437093efdeb128778897ab0e409361336676a2c3012c8a7 languageName: node linkType: hard @@ -9143,9 +9413,9 @@ __metadata: languageName: node linkType: hard -"jsdom@npm:^24.1.0": - version: 24.1.0 - resolution: "jsdom@npm:24.1.0" +"jsdom@npm:^24.1.1": + version: 24.1.1 + resolution: "jsdom@npm:24.1.1" dependencies: cssstyle: ^4.0.1 data-urls: ^5.0.0 @@ -9153,11 +9423,11 @@ __metadata: form-data: ^4.0.0 html-encoding-sniffer: ^4.0.0 http-proxy-agent: ^7.0.2 - https-proxy-agent: ^7.0.4 + https-proxy-agent: ^7.0.5 is-potential-custom-element-name: ^1.0.1 - nwsapi: ^2.2.10 + nwsapi: ^2.2.12 parse5: ^7.1.2 - rrweb-cssom: ^0.7.0 + rrweb-cssom: ^0.7.1 saxes: ^6.0.0 symbol-tree: ^3.2.4 tough-cookie: ^4.1.4 @@ -9166,14 +9436,14 @@ __metadata: whatwg-encoding: ^3.1.1 whatwg-mimetype: ^4.0.0 whatwg-url: ^14.0.0 - ws: ^8.17.0 + ws: ^8.18.0 xml-name-validator: ^5.0.0 peerDependencies: canvas: ^2.11.2 peerDependenciesMeta: canvas: optional: true - checksum: c24b244308cabb2aabee2bdcc5041c810bc45320d0e8721c3eaaf5700d5f8498730809b10da4a97065abfccd937132700d64ae275491424c6b1b20a21d58d2e2 + checksum: c3f3c9c8f6ac4ce308de6f005980d0f3da4d504686a0fc20c59760f1e3be714d48adf3d31f8d3a352d8adb4961e6cfebfc6b6c3c9841408cf6e7f8c0cd4dcdc4 languageName: node linkType: hard @@ -9377,10 +9647,10 @@ __metadata: languageName: node linkType: hard -"lilconfig@npm:~3.1.1": - version: 3.1.1 - resolution: "lilconfig@npm:3.1.1" - checksum: dc8a4f4afde3f0fac6bd36163cc4777a577a90759b8ef1d0d766b19ccf121f723aa79924f32af5b954f3965268215e046d0f237c41c76e5ef01d4e6d1208a15e +"lilconfig@npm:~3.1.2": + version: 3.1.2 + resolution: "lilconfig@npm:3.1.2" + checksum: 4e8b83ddd1d0ad722600994e6ba5d858ddca14f0587aa6b9c8185e17548149b5e13d4d583d811e9e9323157fa8c6a527e827739794c7502b59243c58e210b8c3 languageName: node linkType: hard @@ -9391,37 +9661,37 @@ __metadata: languageName: node linkType: hard -"lint-staged@npm:^15.2.7": - version: 15.2.7 - resolution: "lint-staged@npm:15.2.7" +"lint-staged@npm:^15.2.9": + version: 15.2.9 + resolution: "lint-staged@npm:15.2.9" dependencies: chalk: ~5.3.0 commander: ~12.1.0 - debug: ~4.3.4 + debug: ~4.3.6 execa: ~8.0.1 - lilconfig: ~3.1.1 - listr2: ~8.2.1 + lilconfig: ~3.1.2 + listr2: ~8.2.4 micromatch: ~4.0.7 pidtree: ~0.6.0 string-argv: ~0.3.2 - yaml: ~2.4.2 + yaml: ~2.5.0 bin: lint-staged: bin/lint-staged.js - checksum: 0f21d1b44c046fcfc0388dab66d45d244818afdb24bdf57e7593640c7ca82cc55be7d75e086708e453fac0c0d9ab8760b2cde053944f7b2121c2dd65f6367ffe + checksum: 7f804c24d0374b48d26f11a051342141b84c9954f77225bab5ae221b1e4d47a72f722f39b13169267592c28b2614ad39f9722fd86bc4cdfdf93e8601ff66b0e1 languageName: node linkType: hard -"listr2@npm:~8.2.1": - version: 8.2.1 - resolution: "listr2@npm:8.2.1" +"listr2@npm:~8.2.4": + version: 8.2.4 + resolution: "listr2@npm:8.2.4" dependencies: cli-truncate: ^4.0.0 colorette: ^2.0.20 eventemitter3: ^5.0.1 - log-update: ^6.0.0 - rfdc: ^1.3.1 + log-update: ^6.1.0 + rfdc: ^1.4.1 wrap-ansi: ^9.0.0 - checksum: a37c032850fc01f45cf6144f2b66d0c56a596b708de1acbd52e7c396a2eb188d027ad132c93a0ad946d7932a581dfcfc2e4318bb301926b01877cb4903d09fbd + checksum: b1cdcae653ff967a9b28637e346df2d6614165b4ad1e9e36b1403bc972550c51f57ec0e6d307dc3921ceea0601e244e848ab79457c6d570ab1f088b577a63d90 languageName: node linkType: hard @@ -9510,16 +9780,16 @@ __metadata: languageName: node linkType: hard -"log-update@npm:^6.0.0": - version: 6.0.0 - resolution: "log-update@npm:6.0.0" +"log-update@npm:^6.1.0": + version: 6.1.0 + resolution: "log-update@npm:6.1.0" dependencies: - ansi-escapes: ^6.2.0 - cli-cursor: ^4.0.0 - slice-ansi: ^7.0.0 + ansi-escapes: ^7.0.0 + cli-cursor: ^5.0.0 + slice-ansi: ^7.1.0 strip-ansi: ^7.1.0 wrap-ansi: ^9.0.0 - checksum: 8803ceba2fb28626951b85de598c8d5a4f5e39f1f767cc54fd925412cc7780ba89ce1dbec24dc96fa46f89d226e1ae984534aa729dc9c9b734e36bb805428ffa + checksum: 817a9ba6c5cbc19e94d6359418df8cfe8b3244a2903f6d53354e175e243a85b782dc6a98db8b5e457ee2f09542ca8916c39641b9cd3b0e6ef45e9481d50c918a languageName: node linkType: hard @@ -10297,12 +10567,12 @@ __metadata: linkType: hard "micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:~4.0.7": - version: 4.0.7 - resolution: "micromatch@npm:4.0.7" + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" dependencies: braces: ^3.0.3 picomatch: ^2.3.1 - checksum: 3cde047d70ad80cf60c787b77198d680db3b8c25b23feb01de5e2652205d9c19f43bd81882f69a0fd1f0cde6a7a122d774998aad3271ddb1b8accf8a0f480cf7 + checksum: 79920eb634e6f400b464a954fcfa589c4e7c7143209488e44baf627f9affc8b1e306f41f4f0deedde97e69cb725920879462d3e750ab3bd3c1aed675bb3a8966 languageName: node linkType: hard @@ -10345,6 +10615,13 @@ __metadata: languageName: node linkType: hard +"mimic-function@npm:^5.0.0": + version: 5.0.1 + resolution: "mimic-function@npm:5.0.1" + checksum: eb5893c99e902ccebbc267c6c6b83092966af84682957f79313311edb95e8bb5f39fb048d77132b700474d1c86d90ccc211e99bae0935447a4834eb4c882982c + languageName: node + linkType: hard + "min-indent@npm:^1.0.0, min-indent@npm:^1.0.1": version: 1.0.1 resolution: "min-indent@npm:1.0.1" @@ -10539,9 +10816,9 @@ __metadata: languageName: node linkType: hard -"msw@npm:^2.3.3": - version: 2.3.3 - resolution: "msw@npm:2.3.3" +"msw@npm:^2.3.5": + version: 2.3.5 + resolution: "msw@npm:2.3.5" dependencies: "@bundled-es-modules/cookie": ^2.0.0 "@bundled-es-modules/statuses": ^1.0.1 @@ -10567,7 +10844,7 @@ __metadata: optional: true bin: msw: cli/index.js - checksum: 1a1cf596df9ade96fecba9533afde676ea1e93ddfba70d00486753eefc04a8fb90e80c3747deec0bd737664b8f8eb908416e014c67158cb8b9ceecac24858d9a + checksum: 0867b11ad26cf54d6b4a01a416524ce56ee62c165ce753cb82cc404882e24dc96ecf81c1900348d96afce8f17da32ec640953c9b4c1319c3d84f97a75f4c735a languageName: node linkType: hard @@ -10692,6 +10969,13 @@ __metadata: languageName: node linkType: hard +"node-releases@npm:^2.0.18": + version: 2.0.18 + resolution: "node-releases@npm:2.0.18" + checksum: ef55a3d853e1269a6d6279b7692cd6ff3e40bc74947945101138745bfdc9a5edabfe72cb19a31a8e45752e1910c4c65c77d931866af6357f242b172b7283f5b3 + languageName: node + linkType: hard + "nopt@npm:^6.0.0": version: 6.0.0 resolution: "nopt@npm:6.0.0" @@ -10780,10 +11064,10 @@ __metadata: languageName: node linkType: hard -"nwsapi@npm:^2.2.10": - version: 2.2.10 - resolution: "nwsapi@npm:2.2.10" - checksum: 5f1d361b38c47ab49727d5ea8bbfeb5867ae6de0e538eec9a8b77c88005ddde36d8b930e0730b50ee5e5dda949112c0f9ffed1bf15e7e1b3cd9cfa319f5a9b6f +"nwsapi@npm:^2.2.12": + version: 2.2.12 + resolution: "nwsapi@npm:2.2.12" + checksum: 4dbce7ecbcf336eef1edcbb5161cbceea95863e63a16d9bcec8e81cbb260bdab3d07e6c7b58354d465dc803eef6d0ea4fb20220a80fa148ae65f18d56df81799 languageName: node linkType: hard @@ -10922,6 +11206,15 @@ __metadata: languageName: node linkType: hard +"onetime@npm:^7.0.0": + version: 7.0.0 + resolution: "onetime@npm:7.0.0" + dependencies: + mimic-function: ^5.0.0 + checksum: eb08d2da9339819e2f9d52cab9caf2557d80e9af8c7d1ae86e1a0fef027d00a88e9f5bd67494d350df360f7c559fbb44e800b32f310fb989c860214eacbb561c + languageName: node + linkType: hard + "open@npm:^7.4.2": version: 7.4.2 resolution: "open@npm:7.4.2" @@ -11398,27 +11691,27 @@ __metadata: languageName: node linkType: hard -"playwright-core@npm:1.45.3": - version: 1.45.3 - resolution: "playwright-core@npm:1.45.3" +"playwright-core@npm:1.46.1": + version: 1.46.1 + resolution: "playwright-core@npm:1.46.1" bin: playwright-core: cli.js - checksum: cecb58877b2c643403d7a72c24a7aa0fdd087a3c7f9a5ea5403851336ea831d8e304b1f159aacbbabd12e5c47eaac054333746c9e5431ec07b13d64dbf3b50ec + checksum: 99a03f97d76af02b0565aa09758eb03427e13497ef7e9e9044fd1184a5f050e7545a517dd7ad8988a68b3ea76e2ba0d411f81e410b1cb29f5ac9161c49689822 languageName: node linkType: hard -"playwright@npm:1.45.3": - version: 1.45.3 - resolution: "playwright@npm:1.45.3" +"playwright@npm:1.46.1": + version: 1.46.1 + resolution: "playwright@npm:1.46.1" dependencies: fsevents: 2.3.2 - playwright-core: 1.45.3 + playwright-core: 1.46.1 dependenciesMeta: fsevents: optional: true bin: playwright: cli.js - checksum: d9d23b155ccd001553214f710561b01e48eb409676102f8ab94c0b4aa5ac5f398becc1a96528b0554944e07e34189503d891913e0e0a4aa58ad36b9c08747983 + checksum: 9e721cb27f919cd92ea8c7461f4a8e97e32eade9da036baa4747f483549e33a8d77585f51e228d6639a1e28a76b0c1d59ca0a05dbd07aa3cfe427be7aaeb9ec8 languageName: node linkType: hard @@ -11467,6 +11760,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.4.41": + version: 8.4.41 + resolution: "postcss@npm:8.4.41" + dependencies: + nanoid: ^3.3.7 + picocolors: ^1.0.1 + source-map-js: ^1.2.0 + checksum: f865894929eb0f7fc2263811cc853c13b1c75103028b3f4f26df777e27b201f1abe21cb4aa4c2e901c80a04f6fb325ee22979688fe55a70e2ea82b0a517d3b6f + languageName: node + linkType: hard + "prelude-ls@npm:^1.2.1": version: 1.2.1 resolution: "prelude-ls@npm:1.2.1" @@ -11753,26 +12057,26 @@ __metadata: dependencies: "@mdx-js/react": ^3.0.1 "@mdx-js/rollup": ^3.0.1 - "@microsoft/applicationinsights-react-js": ^17.3.0 - "@microsoft/applicationinsights-web": ^3.3.0 + "@microsoft/applicationinsights-react-js": ^17.3.1 + "@microsoft/applicationinsights-web": ^3.3.1 "@okta/okta-react": ^6.9.0 - "@okta/okta-signin-widget": ^7.20.1 - "@playwright/test": ^1.45.3 + "@okta/okta-signin-widget": ^7.21.2 + "@playwright/test": ^1.46.1 "@rest-hooks/rest": ^3.0.3 "@rest-hooks/test": ^7.3.1 - "@storybook/addon-a11y": ^8.2.5 - "@storybook/addon-actions": ^8.2.5 - "@storybook/addon-essentials": ^8.2.5 - "@storybook/addon-interactions": ^8.2.5 - "@storybook/addon-links": ^8.2.5 - "@storybook/blocks": ^8.2.5 - "@storybook/components": ^8.2.5 - "@storybook/core-events": ^8.2.5 + "@storybook/addon-a11y": ^8.2.9 + "@storybook/addon-actions": ^8.2.9 + "@storybook/addon-essentials": ^8.2.9 + "@storybook/addon-interactions": ^8.2.9 + "@storybook/addon-links": ^8.2.9 + "@storybook/blocks": ^8.2.9 + "@storybook/components": ^8.2.9 + "@storybook/core-events": ^8.2.9 "@storybook/mdx2-csf": 1.1.0 - "@storybook/react": ^8.2.5 - "@storybook/react-vite": ^8.2.5 + "@storybook/react": ^8.2.9 + "@storybook/react-vite": ^8.2.9 "@storybook/testing-library": ^0.2.2 - "@storybook/theming": ^8.2.5 + "@storybook/theming": ^8.2.9 "@tanstack/react-query": ^5.51.23 "@tanstack/react-query-devtools": ^5.51.23 "@testing-library/dom": ^10.4.0 @@ -11799,11 +12103,11 @@ __metadata: "@vitejs/plugin-react": ^4.3.1 "@vitest/coverage-istanbul": ^2.0.4 "@vitest/ui": ^2.0.4 - autoprefixer: ^10.4.19 + autoprefixer: ^10.4.20 axios: ^1.7.4 - browserslist: ^4.23.2 + browserslist: ^4.23.3 browserslist-useragent-regexp: ^4.1.3 - chromatic: ^11.5.6 + chromatic: ^11.7.1 classnames: ^2.5.1 cross-env: ^7.0.3 date-fns: ^3.6.0 @@ -11822,23 +12126,23 @@ __metadata: eslint-plugin-react-hooks: ^4.6.2 eslint-plugin-react-refresh: ^0.4.7 eslint-plugin-storybook: ^0.8.0 - eslint-plugin-testing-library: ^6.2.2 + eslint-plugin-testing-library: ^6.3.0 eslint-plugin-vitest: ^0.5.4 export-to-csv-fix-source-map: ^0.2.1 focus-trap-react: ^10.2.3 history: ^5.3.0 html-to-text: ^9.0.5 - husky: ^9.1.1 - jsdom: ^24.1.0 - lint-staged: ^15.2.7 + husky: ^9.1.5 + jsdom: ^24.1.1 + lint-staged: ^15.2.9 lodash: ^4.17.21 mockdate: ^3.0.5 - msw: ^2.3.3 + msw: ^2.3.5 msw-storybook-addon: beta npm-run-all: ^4.1.5 otpauth: ^9.3.1 patch-package: ^8.0.0 - postcss: ^8.4.39 + postcss: ^8.4.41 prettier: ^3.3.3 react: ^18.3.1 react-dom: ^18.3.1 @@ -11860,11 +12164,11 @@ __metadata: rest-hooks: ^6.1.7 sanitize-html: ^2.13.0 sass: ^1.77.8 - storybook: ^8.2.5 + storybook: ^8.2.9 storybook-addon-remix-react-router: ^3.0.0 ts-node: ^10.9.2 tslib: ^2.6.3 - tsx: ^4.16.2 + tsx: ^4.17.0 typescript: ^5.5.4 undici: ^6.19.7 use-deep-compare-effect: ^1.8.1 @@ -12453,13 +12757,13 @@ __metadata: languageName: node linkType: hard -"restore-cursor@npm:^4.0.0": - version: 4.0.0 - resolution: "restore-cursor@npm:4.0.0" +"restore-cursor@npm:^5.0.0": + version: 5.1.0 + resolution: "restore-cursor@npm:5.1.0" dependencies: - onetime: ^5.1.0 - signal-exit: ^3.0.2 - checksum: 5b675c5a59763bf26e604289eab35711525f11388d77f409453904e1e69c0d37ae5889295706b2c81d23bd780165084d040f9b68fffc32cc921519031c4fa4af + onetime: ^7.0.0 + signal-exit: ^4.1.0 + checksum: 838dd54e458d89cfbc1a923b343c1b0f170a04100b4ce1733e97531842d7b440463967e521216e8ab6c6f8e89df877acc7b7f4c18ec76e99fb9bf5a60d358d2c languageName: node linkType: hard @@ -12477,10 +12781,10 @@ __metadata: languageName: node linkType: hard -"rfdc@npm:^1.3.1": - version: 1.3.1 - resolution: "rfdc@npm:1.3.1" - checksum: d5d1e930aeac7e0e0a485f97db1356e388bdbeff34906d206fe524dd5ada76e95f186944d2e68307183fdc39a54928d4426bbb6734851692cfe9195efba58b79 +"rfdc@npm:^1.4.1": + version: 1.4.1 + resolution: "rfdc@npm:1.4.1" + checksum: 3b05bd55062c1d78aaabfcea43840cdf7e12099968f368e9a4c3936beb744adb41cbdb315eac6d4d8c6623005d6f87fdf16d8a10e1ff3722e84afea7281c8d13 languageName: node linkType: hard @@ -12578,10 +12882,10 @@ __metadata: languageName: node linkType: hard -"rrweb-cssom@npm:^0.7.0": - version: 0.7.0 - resolution: "rrweb-cssom@npm:0.7.0" - checksum: 32976e45405bd376b6d3e8aada2455233f0c2aa9081c66b1fe59bf8074575fb9d60a9a286edd4925aca8bda7d6734b4170ca087ca528bb77840236ddfe0dd0b9 +"rrweb-cssom@npm:^0.7.1": + version: 0.7.1 + resolution: "rrweb-cssom@npm:0.7.1" + checksum: 62e410ddbaaba6abc196c3bbfa8de4952e0a134d9f2b454ee293039bf9931322d806e14d52ed122a5c2bd332a868b9da2e99358fb6232c33758b5ede86d992c8 languageName: node linkType: hard @@ -12938,7 +13242,7 @@ __metadata: languageName: node linkType: hard -"slice-ansi@npm:^7.0.0": +"slice-ansi@npm:^7.1.0": version: 7.1.0 resolution: "slice-ansi@npm:7.1.0" dependencies: @@ -13130,14 +13434,14 @@ __metadata: languageName: node linkType: hard -"storybook@npm:^8.2.5": - version: 8.2.5 - resolution: "storybook@npm:8.2.5" +"storybook@npm:^8.2.9": + version: 8.2.9 + resolution: "storybook@npm:8.2.9" dependencies: "@babel/core": ^7.24.4 "@babel/types": ^7.24.0 - "@storybook/codemod": 8.2.5 - "@storybook/core": 8.2.5 + "@storybook/codemod": 8.2.9 + "@storybook/core": 8.2.9 "@types/semver": ^7.3.4 "@yarnpkg/fslib": 2.10.3 "@yarnpkg/libzip": 2.3.0 @@ -13166,7 +13470,7 @@ __metadata: getstorybook: ./bin/index.cjs sb: ./bin/index.cjs storybook: ./bin/index.cjs - checksum: 9efe7bcca5b682fccfbc92b3b233f35ed9e5465ff8e24f49a1cf76f5c4984ef10eee279c2e75c67745dc64db601e652f182ed916ea5e09b0111d7bd707d80eca + checksum: bf36183ada2ea362fad4225937cdc4ac9833b1d8d66d847dd9c42f7db4825ce79b9aadcf17151845487e7aa192255729f33267225562fc9d0653a8d9a3c56331 languageName: node linkType: hard @@ -13786,11 +14090,11 @@ __metadata: languageName: node linkType: hard -"tsx@npm:^4.16.2": - version: 4.16.2 - resolution: "tsx@npm:4.16.2" +"tsx@npm:^4.17.0": + version: 4.17.0 + resolution: "tsx@npm:4.17.0" dependencies: - esbuild: ~0.21.5 + esbuild: ~0.23.0 fsevents: ~2.3.3 get-tsconfig: ^4.7.5 dependenciesMeta: @@ -13798,7 +14102,7 @@ __metadata: optional: true bin: tsx: dist/cli.mjs - checksum: bd481097d4614b9d40e7e2c44f7078d9f92b0050e959574a7a88ad8af33327d787f9d76c89689ee19b1275270038705fb9851055ae1a20e15d1c62a34d51a8fd + checksum: 36fb12cef74b177d11601f04aa1b768e7ce74ad0c4d8983a6050c6e19657a30362b07ac84ac5796fd1923b51904e801b42d26768a563a6139b310f8ffb0f08b1 languageName: node linkType: hard @@ -13846,13 +14150,6 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^3.0.0": - version: 3.6.1 - resolution: "type-fest@npm:3.6.1" - checksum: f7e39bf6b74a883661ec8642707f49c33cfcdc6221e1ba36b1d329c1cf301d87351b3ca0839b894cbfe47dc62140c0ce47e69c88f76800b678e0b67b7fe826e6 - languageName: node - linkType: hard - "type-fest@npm:^4.9.0": version: 4.10.2 resolution: "type-fest@npm:4.10.2" @@ -14975,7 +15272,22 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.17.0, ws@npm:^8.2.3": +"ws@npm:^8.18.0": + version: 8.18.0 + resolution: "ws@npm:8.18.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 91d4d35bc99ff6df483bdf029b9ea4bfd7af1f16fc91231a96777a63d263e1eabf486e13a2353970efc534f9faa43bdbf9ee76525af22f4752cbc5ebda333975 + languageName: node + linkType: hard + +"ws@npm:^8.2.3": version: 8.17.1 resolution: "ws@npm:8.17.1" peerDependencies: @@ -15032,7 +15344,7 @@ __metadata: languageName: node linkType: hard -"yaml@npm:^2.0.0, yaml@npm:^2.2.2, yaml@npm:~2.4.2": +"yaml@npm:^2.0.0, yaml@npm:^2.2.2": version: 2.4.2 resolution: "yaml@npm:2.4.2" bin: @@ -15041,6 +15353,15 @@ __metadata: languageName: node linkType: hard +"yaml@npm:~2.5.0": + version: 2.5.0 + resolution: "yaml@npm:2.5.0" + bin: + yaml: bin.mjs + checksum: a116dca5c61641d9bf1f1016c6e71daeb1ed4915f5930ed237d45ab7a605aa5d92c332ff64879a6cd088cabede008c778774e3060ffeb4cd617d28088e4b2d83 + languageName: node + linkType: hard + "yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1" diff --git a/operations/slack-boltjs-app b/operations/slack-boltjs-app index d123ef28e22..a5f682a3381 160000 --- a/operations/slack-boltjs-app +++ b/operations/slack-boltjs-app @@ -1 +1 @@ -Subproject commit d123ef28e22c52abc0ca27a5c65292010a18d204 +Subproject commit a5f682a3381fc1b5c07dce1a6a99350175e15719 diff --git a/prime-router/settings/STLTs/ND/nd-doh.yml b/prime-router/settings/STLTs/ND/nd-doh.yml index 3d8549a8e7a..754fa8f7320 100644 --- a/prime-router/settings/STLTs/ND/nd-doh.yml +++ b/prime-router/settings/STLTs/ND/nd-doh.yml @@ -18,7 +18,7 @@ topic: "full-elr" customerStatus: "active" translation: ! - schemaName: "azure:/metadata/hl7_mapping/receivers/STLTs/ND/ND-receiver-transform.yml" + schemaName: "azure:/hl7_mapping/receivers/STLTs/ND/ND-receiver-transform.yml" useTestProcessingMode: false useBatchHeaders: true receivingApplicationName: "Maven" @@ -64,25 +64,27 @@ jurisdictionalFilter: - "(Bundle.entry.resource.ofType(ServiceRequest)[0].requester.resolve().organization.resolve().address.state = 'ND') or (Bundle.entry.resource.ofType(Patient).address.state = 'ND')" qualityFilter: - - "((Bundle.entry.resource.ofType(Specimen).collection.collectedPeriod.exists() or Bundle.entry.resource.ofType(Specimen).collection.collected.exists()) or Bundle.entry.resource.ofType(serviceRequest).occurrence.exists() or Bundle.entry.resource.ofType(Observation).effective.exists())" + - "((Bundle.entry.resource.ofType(Specimen).collection.collectedPeriod.exists() or Bundle.entry.resource.ofType(Specimen).collection.collected.exists()) or Bundle.entry.resource.ofType(ServiceRequest).occurrence.exists() or Bundle.entry.resource.ofType(Observation).effective.exists())" - "(Bundle.entry.resource.ofType(Patient).address.line.exists() or Bundle.entry.resource.ofType(Patient).address.postalCode.exists() or Bundle.entry.resource.ofType(Patient).telecom.exists())" - "Bundle.entry.resource.ofType(MessageHeader).id.exists()" - "Bundle.entry.resource.ofType(Patient).birthDate.exists()" - "Bundle.entry.resource.ofType(Patient).name.family.exists()" - "Bundle.entry.resource.ofType(Patient).name.given.count() > 0" - "Bundle.entry.resource.ofType(Specimen).type.exists()" - routingFilter: [] + routingFilter: + # marches(test_result_status, F, C, P) + - "Bundle.entry.resource.ofType(DiagnosticReport).where(status in 'final'|'corrected'|'preliminary'|'amended').exists()" processingModeFilter: [] reverseTheQualityFilter: false conditionFilter: - # Accept COVID only - - "(%resource.code.coding.extension('https://reportstream.cdc.gov/fhir/StructureDefinition/condition-code').value.where(code in ('840539006')).exists())" + # RSV: 55735004 (+), COVID: 840539006 (+), FLU (A or B by Antigen): 6142004 (+), FLU (A or B by Culture and Identification Method),: 541131000124102, They also want MPOX but, we are not ready to send MPOX yet. + - "%resource.where(interpretation.coding.code = 'A').code.coding.extension('https://reportstream.cdc.gov/fhir/StructureDefinition/condition-code').value.where(code in ('55735004'|'840539006'|'6142004'|'541131000124102')).exists()" mappedConditionFilter: [] deidentify: false deidentifiedValue: "" timing: operation: "MERGE" - numberPerDay: 12 + numberPerDay: 1440 initialTime: "01:25" timeZone: "EASTERN" maxReportCount: 100 @@ -93,11 +95,10 @@ transport: ! host: "mft.nd.gov" port: "22" - filePath: "/Home/dohdcmsg/nddoh_elr/hl7" + filePath: "/Home/dohdcmsg/nddoh_elr/hl7_test" credentialName: null type: "SFTP" externalName: null enrichmentSchemaNames: [] timeZone: null - dateTimeFormat: "OFFSET" - + dateTimeFormat: "OFFSET" \ No newline at end of file diff --git a/prime-router/src/main/kotlin/cli/tests/AuthTests.kt b/prime-router/src/main/kotlin/cli/tests/AuthTests.kt index d679e57fbfd..8a74452beee 100644 --- a/prime-router/src/main/kotlin/cli/tests/AuthTests.kt +++ b/prime-router/src/main/kotlin/cli/tests/AuthTests.kt @@ -26,11 +26,9 @@ import gov.cdc.prime.router.tokens.AuthUtils import gov.cdc.prime.router.tokens.DatabaseJtiCache import gov.cdc.prime.router.tokens.Scope import io.ktor.client.plugins.timeout -import io.ktor.client.request.accept import io.ktor.client.request.get import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode -import kotlinx.coroutines.runBlocking import java.io.File import java.io.IOException import java.net.URLEncoder @@ -1172,107 +1170,84 @@ class Server2ServerAuthTests : CoolTest() { ) val orgEndpoint = "${environment.url}/api/settings/organizations" - val client = HttpClientUtils.createDefaultHttpClient( - userToken - ) - - val clientAdmin = HttpClientUtils.createDefaultHttpClient( - adminToken - ) - // Case: GET All Org Settings (Admin-only endpoint) // Unhappy Path: user on admin-only endpoint - val response = runBlocking { - client.get(orgEndpoint) { - timeout { - requestTimeoutMillis = 45000 - // default timeout is 15s; raising higher due to slow Function startup issues - } - accept(ContentType.Application.Json) - } - } + val response = HttpClientUtils.get( + url = orgEndpoint, + accessToken = userToken, + timeout = 45000, // default timeout is 15s; raising higher due to slow Function startup issues + acceptedContent = ContentType.Application.Json + ) if (response.status != HttpStatusCode.Unauthorized) { bad( "***$name Test settings/organizations Unhappy Path (user-GET All Orgs) FAILED:" + - " Expected HttpStatus ${HttpStatusCode.Unauthorized}. Got ${response.status.value}" + " Expected HttpStatus ${HttpStatusCode.Unauthorized}. Got ${response.status.value}" ) return false } // Happy Path: admin on admin-only endpoint - val response2 = runBlocking { - clientAdmin.get(orgEndpoint) { - timeout { - requestTimeoutMillis = 45000 - // default timeout is 15s; raising higher due to slow Function startup issues - } - accept(ContentType.Application.Json) - } - } + val response2 = HttpClientUtils.get( + url = orgEndpoint, + accessToken = adminToken, + timeout = 45000, // default timeout is 15s; raising higher due to slow Function startup issues + acceptedContent = ContentType.Application.Json + ) if (response2.status != HttpStatusCode.OK) { bad( "***$name Test settings/organizations Happy Path (admin-GET All Orgs) FAILED:" + - " Expected HttpStatus ${HttpStatusCode.OK}. Got ${response2.status.value}" + " Expected HttpStatus ${HttpStatusCode.OK}. Got ${response2.status.value}" ) return false } // Case: GET Receivers for an Org (Endpoint allowed for admins and members of the org) // Happy Path: user on user-allowed endpoint - val response3 = runBlocking { - client.get("$orgEndpoint/${authorizedOrg.name}/receivers") { - timeout { - requestTimeoutMillis = 45000 - // default timeout is 15s; raising higher due to slow Function startup issues - } - accept(ContentType.Application.Json) - } - } + val response3 = HttpClientUtils.get( + url = "$orgEndpoint/${authorizedOrg.name}/receivers", + accessToken = userToken, + timeout = 45000, // default timeout is 15s; raising higher due to slow Function startup issues + acceptedContent = ContentType.Application.Json + ) if (response3.status != HttpStatusCode.OK) { bad( "***$name Test settings/organizations Happy Path (user-GET Org Receivers) FAILED:" + - " Expected HttpStatus ${HttpStatusCode.OK}. Got ${response3.status.value}" + " Expected HttpStatus ${HttpStatusCode.OK}. Got ${response3.status.value}" ) return false } // Happy Path: admin on user-allowed endpoint - val response4 = runBlocking { - clientAdmin.get("$orgEndpoint/${authorizedOrg.name}/receivers") { - timeout { - requestTimeoutMillis = 45000 - // default timeout is 15s; raising higher due to slow Function startup issues - } - accept(ContentType.Application.Json) - } - } + val response4 = HttpClientUtils.get( + url = "$orgEndpoint/${authorizedOrg.name}/receivers", + accessToken = adminToken, + timeout = 45000, // default timeout is 15s; raising higher due to slow Function startup issues + acceptedContent = ContentType.Application.Json + ) if (response4.status != HttpStatusCode.OK) { bad( "***$name Test settings/organizations Happy Path (admin-GET Org Receivers) FAILED:" + - " Expected HttpStatus ${HttpStatusCode.OK}. Got ${response4.status.value}" + " Expected HttpStatus ${HttpStatusCode.OK}. Got ${response4.status.value}" ) return false } // UnhappyPath: user on an unauthorized org name - val response5 = runBlocking { - client.get("$orgEndpoint/${unauthorizedOrg.name}/receivers") { - timeout { - requestTimeoutMillis = 45000 - // default timeout is 15s; raising higher due to slow Function startup issues - } - accept(ContentType.Application.Json) - } - } + val response5 = HttpClientUtils.get( + url = "$orgEndpoint/${unauthorizedOrg.name}/receivers", + accessToken = userToken, + timeout = 45000, // default timeout is 15s; raising higher due to slow Function startup issues + acceptedContent = ContentType.Application.Json + ) if (response5.status != HttpStatusCode.Unauthorized) { bad( "***$name Test settings/organizations Unhappy Path (user-GET Unauthorized Org Receivers) FAILED:" + - " Expected HttpStatus ${HttpStatusCode.Unauthorized}. Got ${response5.status.value}" + " Expected HttpStatus ${HttpStatusCode.Unauthorized}. Got ${response5.status.value}" ) return false } diff --git a/prime-router/src/main/kotlin/common/HttpClientUtils.kt b/prime-router/src/main/kotlin/common/HttpClientUtils.kt index 4e80e8f64f2..b406ef0bcb3 100644 --- a/prime-router/src/main/kotlin/common/HttpClientUtils.kt +++ b/prime-router/src/main/kotlin/common/HttpClientUtils.kt @@ -5,11 +5,9 @@ import io.ktor.client.call.body import io.ktor.client.engine.apache.Apache import io.ktor.client.plugins.HttpTimeout import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.client.plugins.defaultRequest import io.ktor.client.plugins.timeout import io.ktor.client.request.accept import io.ktor.client.request.forms.submitForm -import io.ktor.client.request.header import io.ktor.client.request.headers import io.ktor.client.request.parameter import io.ktor.client.request.request @@ -32,10 +30,30 @@ class HttpClientUtils { const val REQUEST_TIMEOUT_MILLIS: Long = 130000 // need to be public to be used by inline const val SETTINGS_REQUEST_TIMEOUT_MILLIS = 30000 + private val httpClient: HttpClient = + HttpClient(Apache) { + install(ContentNegotiation) { + json( + Json { + prettyPrint = true + isLenient = true + ignoreUnknownKeys = true + } + ) + } + install(HttpTimeout) + engine { + followRedirects = true + socketTimeout = TIMEOUT + connectTimeout = TIMEOUT + connectionRequestTimeout = TIMEOUT + } + } + /** * GET (query resource) operation to the given endpoint resource [url] * @param url: required, the url to the resource endpoint - * @param tokens: null default, the access token needed to call the endpoint + * @param accessToken: null default, the access token needed to call the endpoint * @param headers: null default, the headers of the request * @param acceptedContent: default application/json the accepted content type * @param timeout: default to a system base value in millis @@ -73,7 +91,7 @@ class HttpClientUtils { /** * GET (query resource) operation to the given endpoint resource [url] * @param url: required, the url to the resource endpoint - * @param tokens: null default, the access token needed to call the endpoint + * @param accessToken: null default, the access token needed to call the endpoint * @param headers: null default, the headers of the request * @param acceptedContent: default application/json the accepted content type * @param timeout: default to a system base value in millis @@ -106,7 +124,7 @@ class HttpClientUtils { /** * PUT (modify resource) operation to the given endpoint resource [url] * @param url: required, the url to the resource endpoint - * @param tokens: null default, the access token needed to call the endpoint + * @param accessToken: null default, the access token needed to call the endpoint * @param headers: null default, the headers of the request * @param acceptedContent: default application/json the accepted content type * @param timeout: default to a system base value in millis @@ -147,7 +165,7 @@ class HttpClientUtils { /** * PUT (modify resource) operation to the given endpoint resource [url] * @param url: required, the url to the resource endpoint - * @param tokens: null default, the access token needed to call the endpoint + * @param accessToken: null default, the access token needed to call the endpoint * @param headers: null default, the headers of the request * @param acceptedContent: default application/json the accepted content type * @param timeout: default to a system base value in millis @@ -183,7 +201,7 @@ class HttpClientUtils { /** * POST (create resource) operation to the given endpoint resource [url] * @param url: required, the url to the resource endpoint - * @param tokens: null default, the access token needed to call the endpoint + * @param accessToken: null default, the access token needed to call the endpoint * @param headers: null default, the headers of the request * @param acceptedContent: default application/json the accepted content type * @param timeout: default to a system base value in millis @@ -223,7 +241,7 @@ class HttpClientUtils { /** * POST (create resource) operation to the given endpoint resource [url] * @param url: required, the url to the resource endpoint - * @param tokens: null default, the access token needed to call the endpoint + * @param accessToken: null default, the access token needed to call the endpoint * @param headers: null default, the headers of the request * @param acceptedContent: default application/json the accepted content type * @param timeout: default to a system base value in millis @@ -260,7 +278,7 @@ class HttpClientUtils { * Submit form to the endpoint as indicated by [url] * * @param url: required, the url to the resource endpoint - * @param tokens: null default, the access token needed to call the endpoint + * @param accessToken: null default, the access token needed to call the endpoint * @param headers: null default, the headers of the request * @param acceptedContent: default application/json the accepted content type * @param timeout: default to a system base value in millis @@ -294,7 +312,7 @@ class HttpClientUtils { * Submit form to the endpoint as indicated by [url] * * @param url: required, the url to the resource endpoint - * @param tokens: null default, the access token needed to call the endpoint + * @param accessToken: null default, the access token needed to call the endpoint * @param headers: null default, the headers of the request * @param acceptedContent: default application/json the accepted content type * @param timeout: default to a system base value in millis @@ -312,7 +330,7 @@ class HttpClientUtils { httpClient: HttpClient? = null, ): HttpResponse { return runBlocking { - (httpClient ?: createDefaultHttpClient(accessToken)).submitForm( + (httpClient ?: getDefaultHttpClient()).submitForm( url, formParameters = Parameters.build { formParams?.forEach { param -> @@ -331,7 +349,11 @@ class HttpClientUtils { } } } - + accessToken?.let { + headers { + append("Authorization", "Bearer $accessToken") + } + } accept(acceptedContent) } } @@ -340,7 +362,7 @@ class HttpClientUtils { /** * HEAD operation to the given endpoint resource [url] * @param url: required, the url to the resource endpoint - * @param tokens: null default, the access token needed to call the endpoint + * @param accessToken: null default, the access token needed to call the endpoint * @param headers: null default, the headers of the request * @param acceptedContent: default application/json the accepted content type * @param timeout: default to a system base value in millis @@ -376,9 +398,9 @@ class HttpClientUtils { /** * HEAD operation to the given endpoint resource [url] * @param url: required, the url to the resource endpoint - * @param tokens: null default, the access token needed to call the endpoint + * @param accessToken: null default, the access token needed to call the endpoint * @param headers: null default, the headers of the request - * @param acceptContent: default application/json the accepted content type + * @param acceptedContent: default application/json the accepted content type * @param timeout: default to a system base value in millis * @param queryParameters: null default, query parameters of the request * @param httpClient: null default, a http client injected by caller @@ -411,7 +433,7 @@ class HttpClientUtils { * A thin wrapper on top of the underlying 3rd party http client, e.g. ktor http client * with: * @param url: required, the url to the resource endpoint - * @param tokens: null default, the access token needed to call the endpoint + * @param accessToken: null default, the access token needed to call the endpoint * @param headers: null default, the headers of the request * @param acceptedContent: default application/json the accepted content type * @param timeout: default to a system base value in millis @@ -450,7 +472,7 @@ class HttpClientUtils { * A thin wrapper on top of the underlying 3rd party http client, e.g. ktor http client * with: * @param url: required, the url to the resource endpoint - * @param tokens: null default, the access token needed to call the endpoint + * @param accessToken: null default, the access token needed to call the endpoint * @param headers: null default, the headers of the request * @param acceptedContent: default application/json the accepted content type * @param timeout: default to a system base value in millis @@ -496,17 +518,16 @@ class HttpClientUtils { httpClient: HttpClient? = null, ): HttpResponse { return runBlocking { - (httpClient ?: createDefaultHttpClient(accessToken)).request(url) { + (httpClient ?: getDefaultHttpClient()).request(url) { this.method = method timeout { requestTimeoutMillis = timeout } url { queryParameters?.forEach { - parameter(it.key, it.value.toString()) + parameter(it.key, it.value) } } - headers?.let { headers { headers.forEach { @@ -514,6 +535,11 @@ class HttpClientUtils { } } } + accessToken?.let { + headers { + append("Authorization", "Bearer $accessToken") + } + } acceptedContent?.let { accept(acceptedContent) contentType(acceptedContent) @@ -526,48 +552,15 @@ class HttpClientUtils { } /** - * Create a http client with sensible default settings + * Get a http client with sensible default settings * note: most configuration parameters are overridable * e.g. expectSuccess default to false because most of the time * the caller wants to handle the whole range of response status - * @param bearerTokens null default, the access token needed to call the endpoint + * * @return a HttpClient with all sensible defaults */ - fun createDefaultHttpClient(accessToken: String?): HttpClient { - return HttpClient(Apache) { - // installs logging into the call to post to the server - // commented out - not to override underlying default logger settings - // enable to trace http client internals when needed - // install(Logging) { - // logger = Logger.SIMPLE - // level = LogLevel.INFO - // } - // not using Bearer Auth handler due to refresh token behavior - accessToken?.let { - defaultRequest { - header("Authorization", "Bearer $it") - } - } - install(ContentNegotiation) { - json( - Json { - prettyPrint = true - isLenient = true - ignoreUnknownKeys = true - } - ) - } - - install(HttpTimeout) - engine { - followRedirects = true - socketTimeout = TIMEOUT - connectTimeout = TIMEOUT - connectionRequestTimeout = TIMEOUT - customizeClient { - } - } - } + fun getDefaultHttpClient(): HttpClient { + return httpClient } } } \ No newline at end of file diff --git a/prime-router/src/main/kotlin/history/db/DeliveryHistoryDatabaseAccess.kt b/prime-router/src/main/kotlin/history/db/DeliveryHistoryDatabaseAccess.kt index dc19cc72f45..d3031091398 100644 --- a/prime-router/src/main/kotlin/history/db/DeliveryHistoryDatabaseAccess.kt +++ b/prime-router/src/main/kotlin/history/db/DeliveryHistoryDatabaseAccess.kt @@ -248,8 +248,10 @@ class DeliveryHistoryDatabaseAccess( ) .from( Tables.ACTION.join(Tables.REPORT_FILE).on( - Tables.REPORT_FILE.ACTION_ID.eq(Tables.ACTION.ACTION_ID), + Tables.REPORT_FILE.ACTION_ID.eq(Tables.ACTION.ACTION_ID) ) + .and(Tables.ACTION.RECEIVING_ORG.eq(Tables.REPORT_FILE.RECEIVING_ORG)) + .and(Tables.ACTION.RECEIVING_ORG_SVC.eq(Tables.REPORT_FILE.RECEIVING_ORG_SVC)) .join(Tables.SETTING) .on( Tables.REPORT_FILE.RECEIVING_ORG diff --git a/prime-router/src/testIntegration/resources/datatests/FHIR_to_HL7/sample_ND_20240819-0001.fhir b/prime-router/src/testIntegration/resources/datatests/FHIR_to_HL7/sample_ND_20240819-0001.fhir new file mode 100644 index 00000000000..606a5d47598 --- /dev/null +++ b/prime-router/src/testIntegration/resources/datatests/FHIR_to_HL7/sample_ND_20240819-0001.fhir @@ -0,0 +1 @@ +{"resourceType":"Bundle","identifier":{"value":"12256b9a-0ca9-47ed-95b4-0d4ca60c4324"},"type":"message","timestamp":"2024-08-15T12:47:18.310Z","entry":[{"fullUrl":"MessageHeader/59845db6-6210-4008-8369-67cb166bc651","resource":{"resourceType":"MessageHeader","id":"59845db6-6210-4008-8369-67cb166bc651","meta":{"tag":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0103","code":"P","display":"Production"}]},"eventCoding":{"system":"http://terminology.hl7.org/CodeSystem/v2-0003","code":"R01","display":"ORU/ACK - Unsolicited transmission of an observation message"},"destination":[{"name":"PRIME ReportStream","endpoint":"https://prime.cdc.gov/api/reports?option=SkipInvalidItems"}],"sender":{"reference":"Organization/719ec8ad-cf59-405a-9832-c4065945c130"},"source":{"extension":[{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/software-binary-id","valueString":"680ba6e"},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/software-install-date","valueInstant":"2024-08-12T21:07:36Z"},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/software-vendor-org","valueReference":{"reference":"Organization/07640c5d-87cd-488b-9343-a226c5166539"}}],"software":"PRIME SimpleReport","version":"680ba6e","endpoint":"https://simplereport.gov"},"focus":[{"reference":"Provenance/342b0559-0c77-4bb0-9c98-bae5a6cf17d6"},{"reference":"DiagnosticReport/12256b9a-0ca9-47ed-95b4-0d4ca60c4324"}]}},{"fullUrl":"Provenance/342b0559-0c77-4bb0-9c98-bae5a6cf17d6","resource":{"resourceType":"Provenance","id":"342b0559-0c77-4bb0-9c98-bae5a6cf17d6","recorded":"2024-08-15T12:47:18.310Z","activity":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0003","code":"R01","display":"ORU/ACK - Unsolicited transmission of an observation message"}]},"agent":[{"who":{"reference":"Organization/719ec8ad-cf59-405a-9832-c4065945c130"}}]}},{"fullUrl":"DiagnosticReport/12256b9a-0ca9-47ed-95b4-0d4ca60c4324","resource":{"resourceType":"DiagnosticReport","id":"12256b9a-0ca9-47ed-95b4-0d4ca60c4324","identifier":[{"value":"12256b9a-0ca9-47ed-95b4-0d4ca60c4324"}],"basedOn":[{"reference":"ServiceRequest/5b0a1598-edcb-4a5d-af54-8acd393f734e"}],"status":"final","code":{"coding":[{"system":"http://loinc.org","code":"94558-4"}]},"subject":{"reference":"Patient/10083d1d-dc8b-4ea0-91fa-8744cf0f013b"},"effectiveDateTime":"2024-08-15T12:32:18+00:00","issued":"2024-08-15T12:47:18+00:00","specimen":[{"reference":"Specimen/baf09fcd-95d1-48d7-9b12-3f52d57d69f4"}],"result":[{"reference":"Observation/cf10c0e9-3050-4eeb-a7e0-0dfe76f46af3"}]}},{"fullUrl":"Patient/10083d1d-dc8b-4ea0-91fa-8744cf0f013b","resource":{"resourceType":"Patient","id":"10083d1d-dc8b-4ea0-91fa-8744cf0f013b","extension":[{"url":"http://ibm.com/fhir/cdm/StructureDefinition/local-race-cd","valueCodeableConcept":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v3-Race","code":"1002-5"}],"text":"native"}},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/ethnic-group","valueCodeableConcept":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0189","code":"H","display":"Hispanic or Latino"}],"text":"Hispanic or Latino"}}],"identifier":[{"value":"10083d1d-dc8b-4ea0-91fa-8744cf0f013b"}],"name":[{"family":"Wolf","given":["Karolann"]}],"telecom":[{"system":"phone","value":"(800) 232 4636","use":"mobile"},{"system":"email","value":"wolf@test.com","use":"home"}],"gender":"female","birthDate":"1970-02-01","address":[{"line": ["123 Main St"],"city": "Tamuning","state": "ND","postalCode":"55987","country":"USA"}],"managingOrganization":{"reference":"Organization/719ec8ad-cf59-405a-9832-c4065945c130"}}},{"fullUrl":"Organization/719ec8ad-cf59-405a-9832-c4065945c130","resource":{"resourceType":"Organization","id":"719ec8ad-cf59-405a-9832-c4065945c130","identifier":[{"use":"official","type":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0301","code":"CLIA"}]},"value":"12D4567890"}],"name":"Testing Lab","telecom":[{"system":"phone","value":"(530) 867 5309","use":"work"}],"address":[{"line":["123 Beach Way"],"city":"Denver","state":"CO","postalCode":"80210","country":"USA"}]}},{"fullUrl":"Practitioner/ee29ccf5-631d-4b35-a6d4-30a61c0eb8d9","resource":{"resourceType":"Practitioner","id":"ee29ccf5-631d-4b35-a6d4-30a61c0eb8d9","identifier":[{"system":"http://hl7.org/fhir/sid/us-npi","value":"1245319599"}],"name":[{"family":"McTester","given":["Phil"]}],"telecom":[{"system":"phone","value":"(530) 867 5309","use":"work"}],"address":[{"line":["321 Ocean Drive"],"city":"Denver","state":"CO","postalCode":"80210","country":"USA"}]}},{"fullUrl":"Specimen/baf09fcd-95d1-48d7-9b12-3f52d57d69f4","resource":{"resourceType":"Specimen","id":"baf09fcd-95d1-48d7-9b12-3f52d57d69f4","identifier":[{"value":"f93386a8-d266-4ee9-aa1e-654bf8cee76d"}],"type":{"coding":[{"system":"http://snomed.info/sct","code":"433801000124107"}],"text":"Nasopharyngeal and oropharyngeal swab"},"subject":{"reference":"Patient/10083d1d-dc8b-4ea0-91fa-8744cf0f013b"},"receivedTime":"2024-08-15T12:32:18+00:00","collection":{"collectedDateTime":"2024-08-15T12:32:18+00:00","bodySite":{"coding":[{"system":"http://snomed.info/sct","code":"87100004"}],"text":"Topography unknown (body structure)"}}}},{"fullUrl":"ServiceRequest/5b0a1598-edcb-4a5d-af54-8acd393f734e","resource":{"resourceType":"ServiceRequest","id":"5b0a1598-edcb-4a5d-af54-8acd393f734e","extension":[{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/order-control","valueCodeableConcept":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0119","code":"RE"}]}},{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/order-effective-date","valueDateTime":"2024-08-15T12:47:18+00:00"}],"status":"completed","intent":"order","code":{"coding":[{"system":"http://loinc.org","code":"94558-4"}]},"subject":{"reference":"Patient/10083d1d-dc8b-4ea0-91fa-8744cf0f013b"},"requester":{"reference":"PractitionerRole/8fc81c25-0c0f-46bc-8692-efab10e4c19f"},"performer":[{"reference":"Organization/719ec8ad-cf59-405a-9832-c4065945c130"}],"supportingInfo":[{"reference":"Observation/e704237f-1c4a-38ca-ba72-a6b683f68f16"},{"reference":"Observation/fb84b03d-e40a-3b93-8c11-d63dafab905e"},{"reference":"Observation/40079ae2-599d-39ff-bb44-4838c90888da"},{"reference":"Observation/b4848674-cbd7-33ff-91a0-999df0c72987"},{"reference":"Observation/dfee47a9-6993-36de-b041-9aa6baaa4519"}]}},{"fullUrl":"Device/65006a82-d9ac-45b3-8ca1-67dc16010555","resource":{"resourceType":"Device","id":"65006a82-d9ac-45b3-8ca1-67dc16010555","identifier":[{"value":"No Equipment"}],"manufacturer":"Abbott Diagnostics Scarborough, Inc.","deviceName":[{"name":"BinaxNOW COVID-19 Ag 2 Card","type":"model-name"}]}},{"fullUrl":"PractitionerRole/8fc81c25-0c0f-46bc-8692-efab10e4c19f","resource":{"resourceType":"PractitionerRole","id":"8fc81c25-0c0f-46bc-8692-efab10e4c19f","practitioner":{"reference":"Practitioner/ee29ccf5-631d-4b35-a6d4-30a61c0eb8d9"},"organization":{"reference":"Organization/719ec8ad-cf59-405a-9832-c4065945c130"}}},{"fullUrl":"Organization/07640c5d-87cd-488b-9343-a226c5166539","resource":{"resourceType":"Organization","id":"07640c5d-87cd-488b-9343-a226c5166539","name":"SimpleReport"}},{"fullUrl":"Observation/cf10c0e9-3050-4eeb-a7e0-0dfe76f46af3","resource":{"resourceType":"Observation","id":"cf10c0e9-3050-4eeb-a7e0-0dfe76f46af3","status":"final","code":{"coding":[{"system":"http://loinc.org","code":"94558-4"}],"text":"COVID-19"},"subject":{"reference":"Patient/10083d1d-dc8b-4ea0-91fa-8744cf0f013b"},"issued":"2024-08-15T12:47:18.249Z","performer":[{"reference":"Organization/719ec8ad-cf59-405a-9832-c4065945c130"}],"valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"260373001","display":"Detected"}]},"interpretation":[{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0078","code":"A","display":"Abnormal"}]}],"method":{"extension":[{"url":"https://reportstream.cdc.gov/fhir/StructureDefinition/testkit-name-id","valueCoding":{"code":"BinaxNOW COVID-19 Ag 2 Card_Abbott Diagnostics Scarborough, Inc."}}],"coding":[{"display":"BinaxNOW COVID-19 Ag 2 Card"}]},"specimen":{"reference":"Specimen/baf09fcd-95d1-48d7-9b12-3f52d57d69f4"},"device":{"reference":"Device/65006a82-d9ac-45b3-8ca1-67dc16010555"}}},{"fullUrl":"Observation/e704237f-1c4a-38ca-ba72-a6b683f68f16","resource":{"resourceType":"Observation","id":"e704237f-1c4a-38ca-ba72-a6b683f68f16","identifier":[{"use":"official","type":{"coding":[{"system":"http://loinc.org","code":"81959-9","display":"Public health laboratory ask at order entry panel"}]}}],"status":"final","code":{"coding":[{"system":"http://loinc.org","code":"95419-8","display":"Has symptoms related to condition of interest"}],"text":"Has symptoms related to condition of interest"},"subject":{"reference":"Patient/10083d1d-dc8b-4ea0-91fa-8744cf0f013b"},"valueCodeableConcept":{"coding":[{"system":"http://terminology.hl7.org/ValueSet/v2-0136","code":"N","display":"No"}]}}},{"fullUrl":"Observation/fb84b03d-e40a-3b93-8c11-d63dafab905e","resource":{"resourceType":"Observation","id":"fb84b03d-e40a-3b93-8c11-d63dafab905e","identifier":[{"use":"official","type":{"coding":[{"system":"http://loinc.org","code":"81959-9","display":"Public health laboratory ask at order entry panel"}]}}],"status":"final","code":{"coding":[{"system":"http://loinc.org","code":"82810-3","display":"Pregnancy status"}],"text":"Pregnancy status"},"subject":{"reference":"Patient/10083d1d-dc8b-4ea0-91fa-8744cf0f013b"},"valueCodeableConcept":{"coding":[{"system":"http://snomed.info/sct","code":"60001007","display":"Not pregnant"}]}}},{"fullUrl":"Observation/40079ae2-599d-39ff-bb44-4838c90888da","resource":{"resourceType":"Observation","id":"40079ae2-599d-39ff-bb44-4838c90888da","identifier":[{"use":"official","type":{"coding":[{"system":"http://loinc.org","code":"81959-9","display":"Public health laboratory ask at order entry panel"}]}}],"status":"final","code":{"coding":[{"system":"http://loinc.org","code":"95418-0","display":"Employed in a healthcare setting"}],"text":"Employed in a healthcare setting"},"subject":{"reference":"Patient/10083d1d-dc8b-4ea0-91fa-8744cf0f013b"},"valueCodeableConcept":{"coding":[{"system":"http://terminology.hl7.org/ValueSet/v2-0136","code":"Y","display":"Yes"}]}}},{"fullUrl":"Observation/b4848674-cbd7-33ff-91a0-999df0c72987","resource":{"resourceType":"Observation","id":"b4848674-cbd7-33ff-91a0-999df0c72987","identifier":[{"use":"official","type":{"coding":[{"system":"http://loinc.org","code":"81959-9","display":"Public health laboratory ask at order entry panel"}]}}],"status":"final","code":{"coding":[{"system":"http://loinc.org","code":"95421-4","display":"Resides in a congregate care setting"}],"text":"Resides in a congregate care setting"},"subject":{"reference":"Patient/10083d1d-dc8b-4ea0-91fa-8744cf0f013b"},"valueCodeableConcept":{"coding":[{"system":"http://terminology.hl7.org/ValueSet/v2-0136","code":"Y","display":"Yes"}]}}},{"fullUrl":"Observation/dfee47a9-6993-36de-b041-9aa6baaa4519","resource":{"resourceType":"Observation","id":"dfee47a9-6993-36de-b041-9aa6baaa4519","identifier":[{"use":"official","type":{"coding":[{"system":"http://loinc.org","code":"81959-9","display":"Public health laboratory ask at order entry panel"}]}}],"status":"final","code":{"coding":[{"system":"http://loinc.org","code":"76691-5","display":"Gender identity"}],"text":"Gender identity"},"subject":{"reference":"Patient/10083d1d-dc8b-4ea0-91fa-8744cf0f013b"},"valueCodeableConcept":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v3-NullFlavor","code":"UNK","display":"Non-binary gender identity"}]}}}]} \ No newline at end of file diff --git a/prime-router/src/testIntegration/resources/datatests/FHIR_to_HL7/sample_ND_20240819-0001.hl7 b/prime-router/src/testIntegration/resources/datatests/FHIR_to_HL7/sample_ND_20240819-0001.hl7 new file mode 100644 index 00000000000..8158f6366d6 --- /dev/null +++ b/prime-router/src/testIntegration/resources/datatests/FHIR_to_HL7/sample_ND_20240819-0001.hl7 @@ -0,0 +1,12 @@ +MSH|^~\&|CDC PRIME - Atlanta, Georgia (Dekalb)^2.16.840.1.114222.4.1.237821^ISO|Testing Lab^12D4567890^CLIA|Maven^2.16.840.1.114222.4.3.4.34.1.1^ISO|NDDOH^2.16.840.1.113883.3.89.109.100.1.3^ISO|20240815124718+0000||ORU^R01^ORU_R01|12256b9a-0ca9-47ed-95b4-0d4ca60c4324|P|2.5.1|||NE|NE|USA|UNICODE UTF-8|ENG^English^ISO||PHLabReport-NoAck^ELR_Receiver^2.16.840.1.113883.9.11^ISO +SFT|Centers for Disease Control and Prevention|0.2-SNAPSHOT|PRIME ReportStream|0.2-SNAPSHOT||20240812210736+0000 +PID|1||10083d1d-dc8b-4ea0-91fa-8744cf0f013b^^^Testing Lab&12D4567890&CLIA^PI^Testing Lab&12D4567890&CLIA||Wolf^Karolann^^^^^L||19700201|F||1002-5^native^HL70005^^^^2.5.1^^native|123 Main St^^Tamuning^ND^55987^USA||(800) 232 4636^PRS^CP^^1^800^2324636^^^^^(800) 232 4636~^NET^Internet^wolf@test.com|||||||||H^Hispanic or Latino^HL70189^^^^2.9^^Hispanic or Latino||||||||N +ORC|RE|12256b9a-0ca9-47ed-95b4-0d4ca60c4324^Testing Lab^12D4567890^CLIA|12256b9a-0ca9-47ed-95b4-0d4ca60c4324^Testing Lab^12D4567890^CLIA|||||||||1245319599^McTester^Phil^^^^^^NPI&2.16.840.1.113883.4.6&ISO^L^^^NPI||(530) 867 5309^WPN^PH^^1^530^8675309^^^^^(530) 867 5309|20240815124718+0000||||||Testing Lab^L^^^^CLIA&2.16.840.1.113883.4.7&ISO^XX^^^12D4567890|123 Beach Way^^Denver^CO^80210^USA|(530) 867 5309^WPN^PH^^1^530^8675309^^^^^(530) 867 5309|321 Ocean Drive^^Denver^CO^80210^USA +OBR|1|12256b9a-0ca9-47ed-95b4-0d4ca60c4324^Testing Lab^12D4567890^CLIA|12256b9a-0ca9-47ed-95b4-0d4ca60c4324^Testing Lab^12D4567890^CLIA|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory system specimen by Rapid immunoassay^LN|||20240815123218+0000|||||||||1245319599^McTester^Phil^^^^^^NPI&2.16.840.1.113883.4.6&ISO^L^^^NPI|(530) 867 5309^WPN^PH^^1^530^8675309^^^^^(530) 867 5309|||||20240815124718+0000|||F +OBX|1|CWE|94558-4^SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory system specimen by Rapid immunoassay^LN^^^^^^COVID-19||260373001^Detected^SCT|||A^Abnormal^HL70078^^^^2.7|||F|||20240815123218+0000|12D4567890^Testing Lab^CLIA||BinaxNOW COVID-19 Ag 2 Card_Abbott Diagnostics Scarborough, Inc.^BinaxNOW COVID-19 Ag 2 Card^^^^^^^BinaxNOW COVID-19 Ag 2 Card_Abbott Diagnostics Scarborough, Inc.|No Equipment|20240815124718+0000||||Testing Lab^L^^^^CLIA&2.16.840.1.113883.4.7&ISO^XX^^^12D4567890|123 Beach Way^^Denver^CO^80210^USA +OBX|2|CWE|95419-8^Has symptoms related to condition of interest^LN^^^^2.69^^Has symptoms related to condition of interest||N^No^HL70136||||||F|||20240815123218+0000|12D4567890^Testing Lab^CLIA||||||||Testing Lab^L^^^^CLIA&2.16.840.1.113883.4.7&ISO^XX^^^12D4567890|123 Beach Way^^Denver^CO^80210^USA|||||QST +OBX|3|CWE|82810-3^Pregnancy status^LN^^^^2.68^^Pregnancy status||60001007^Not pregnant^SCT||||||F|||20240815123218+0000|12D4567890^Testing Lab^CLIA||||||||Testing Lab^L^^^^CLIA&2.16.840.1.113883.4.7&ISO^XX^^^12D4567890|123 Beach Way^^Denver^CO^80210^USA|||||QST +OBX|4|CWE|95418-0^Employed in a healthcare setting^LN^^^^2.69^^Employed in a healthcare setting||Y^Yes^HL70136||||||F|||20240815123218+0000|12D4567890^Testing Lab^CLIA||||||||Testing Lab^L^^^^CLIA&2.16.840.1.113883.4.7&ISO^XX^^^12D4567890|123 Beach Way^^Denver^CO^80210^USA|||||QST +OBX|5|CWE|95421-4^Resides in a congregate care setting^LN^^^^2.69^^Resides in a congregate care setting||Y^Yes^HL70136||||||F|||20240815123218+0000|12D4567890^Testing Lab^CLIA||||||||Testing Lab^L^^^^CLIA&2.16.840.1.113883.4.7&ISO^XX^^^12D4567890|123 Beach Way^^Denver^CO^80210^USA|||||QST +OBX|6|CWE|76691-5^Gender identity^LN^^^^^^Gender identity||UNK^Non-binary gender identity^NULLFL||||||F|||20240815123218+0000|12D4567890^Testing Lab^CLIA||||||||Testing Lab^L^^^^CLIA&2.16.840.1.113883.4.7&ISO^XX^^^12D4567890|123 Beach Way^^Denver^CO^80210^USA|||||QST +SPM|1|12256b9a-0ca9-47ed-95b4-0d4ca60c4324&Testing Lab&12D4567890&CLIA^12256b9a-0ca9-47ed-95b4-0d4ca60c4324&Testing Lab&12D4567890&CLIA||433801000124107^Nasopharyngeal and oropharyngeal swab^SCT^^^^2.67^^Nasopharyngeal and oropharyngeal swab||||87100004^Topography unknown (body structure)^SCT^^^^^^Topography unknown (body structure)|||||||||20240815123218+0000|20240815123218+0000 \ No newline at end of file diff --git a/prime-router/src/testIntegration/resources/datatests/translation-test-config.csv b/prime-router/src/testIntegration/resources/datatests/translation-test-config.csv index c2133f2ad15..48a4472482d 100644 --- a/prime-router/src/testIntegration/resources/datatests/translation-test-config.csv +++ b/prime-router/src/testIntegration/resources/datatests/translation-test-config.csv @@ -61,6 +61,7 @@ FHIR_to_HL7/sample_NH_20240716-0001.fhir,,FHIR_to_HL7/sample_NH_202407016-0001.h FHIR_to_HL7/sample_IL_20240802-0001.fhir,,FHIR_to_HL7/sample_IL_20240802-0001.hl7,classpath:/metadata/hl7_mapping/receivers/STLTs/IL/IL-receiver-transform.yml,HL7,PASS,"","","classpath:/metadata/fhir_transforms/senders/SimpleReport/simple-report-sender-transform.yml","","","" FHIR_to_HL7/sample_WA_20240719-0001.fhir,,FHIR_to_HL7/sample_WA_20240719-0001.hl7,classpath:/metadata/hl7_mapping/receivers/STLTs/WA/WA-receiver-transform.yml,HL7,PASS,"","","classpath:/metadata/fhir_transforms/senders/SimpleReport/simple-report-sender-transform.yml","wa-phd.full-elr","","" FHIR_to_HL7/sample_OH_20240718-0001.fhir,,FHIR_to_HL7/sample_OH_20240718-0001.hl7,classpath:/metadata/hl7_mapping/receivers/STLTs/OH/OH-receiver-transform.yml,HL7,PASS,"","","classpath:/metadata/fhir_transforms/senders/SimpleReport/simple-report-sender-transform.yml","","","" +FHIR_to_HL7/sample_ND_20240819-0001.fhir,,FHIR_to_HL7/sample_ND_20240819-0001.hl7,classpath:/metadata/hl7_mapping/receivers/STLTs/ND/ND-receiver-transform.yml,HL7,PASS,"","","classpath:/metadata/fhir_transforms/senders/SimpleReport/simple-report-sender-transform.yml","","","" FHIR_to_HL7/sample_LA_20240806-0001.fhir,,FHIR_to_HL7/sample_LA_20240806-0001.hl7,classpath:/metadata/hl7_mapping/receivers/STLTs/LA/LA-receiver-transform.yml,HL7,PASS,"","","classpath:/metadata/fhir_transforms/senders/SimpleReport/simple-report-sender-transform.yml","","","classpath:/metadata/fhir_transforms/receivers/reference-range-enrichment.yml" FHIR_to_HL7/sample_MN_20240816-0001.fhir,,FHIR_to_HL7/sample_MN_20240816-0001.hl7,classpath:/metadata/hl7_mapping/receivers/STLTs/MN/MN-receiver-transform.yml,HL7,PASS,"","","classpath:/metadata/fhir_transforms/senders/SimpleReport/simple-report-sender-transform.yml","","","" FHIR_to_HL7/sample_MA_20240805-0001.fhir,,FHIR_to_HL7/sample_MA_20240805-0001.hl7,classpath:/metadata/hl7_mapping/receivers/STLTs/MA/MA-receiver-transform.yml,HL7,PASS,"","","classpath:/metadata/fhir_transforms/senders/SimpleReport/simple-report-sender-transform.yml","","",""