From cd195103f1fdbc19d72e7b7997167604cfe51d33 Mon Sep 17 00:00:00 2001 From: Thomas Stetson Date: Fri, 13 Jun 2025 15:44:46 -0400 Subject: [PATCH 01/12] add more orchestrator and notification tests; needs work --- ...ter-notification-severity-critical.spec.ts | 4 +- .../filterNotificationSeverityHigh.spec.ts | 51 ++++++++++ .../filterNotificationSeverityLow.spec.ts | 51 ++++++++++ .../filterNotificationSeverityNormal.spec.ts | 51 ++++++++++ .../markNotificationAsRead.spec.ts | 28 ++++++ .../markNotificationAsSaved.spec.ts | 29 ++++++ .../markNotificationAsUnRead.spec.ts | 33 +++++++ .../orchestrator/greetingWorkflow.spec.ts | 22 +++++ .../greetingWorkflowReRun.spec.ts | 23 +++++ ...tingWorkflowRunsDetailsValidations.spec.ts | 24 +++++ .../greetingWorkflowRunsValidations.spec.ts | 87 +++++++++++++++++ .../greetingWorkflowValidations.spec.ts | 93 +++++++++++++++++++ .../userOnBoardingWorkflow.spec.ts | 15 +++ .../playwright/support/pages/orchestrator.ts | 86 +++++++++++++++++ e2e-tests/playwright/utils/navbar.ts | 3 +- 15 files changed, 597 insertions(+), 3 deletions(-) create mode 100644 e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityHigh.spec.ts create mode 100644 e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityLow.spec.ts create mode 100644 e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityNormal.spec.ts create mode 100644 e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsRead.spec.ts create mode 100644 e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsSaved.spec.ts create mode 100644 e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsUnRead.spec.ts create mode 100644 e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflow.spec.ts create mode 100644 e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowReRun.spec.ts create mode 100644 e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowRunsDetailsValidations.spec.ts create mode 100644 e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowRunsValidations.spec.ts create mode 100644 e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowValidations.spec.ts create mode 100644 e2e-tests/playwright/e2e/plugins/orchestrator/userOnBoardingWorkflow.spec.ts diff --git a/e2e-tests/playwright/e2e/plugins/notifications/filter-notification-severity-critical.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/filter-notification-severity-critical.spec.ts index ca94cb6f3b..7a2bbae652 100644 --- a/e2e-tests/playwright/e2e/plugins/notifications/filter-notification-severity-critical.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/notifications/filter-notification-severity-critical.spec.ts @@ -27,11 +27,11 @@ test.describe("Filter critical notification tests", () => { await RhdhAuthApiHack.getToken(page); }); - test("Fiter notifcations by serverity - critical", async () => { + test("Filter notifications by severity - critical", async () => { const r = (Math.random() + 1).toString(36).substring(7); const severity = "critical"; const notificationsApi = await RhdhNotficationsApi.build("test-token"); - // Used boradcast here, but we should use type: entity and entityRef: ["user:/"] + // Used broadcast here, but we should use type: entity and entityRef: ["user:/"] const notification: Notifications = { recipients: { type: "broadcast", diff --git a/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityHigh.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityHigh.spec.ts new file mode 100644 index 0000000000..210290ab57 --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityHigh.spec.ts @@ -0,0 +1,51 @@ +import { test } from "@playwright/test"; +import { UIhelper } from "../../../utils/ui-helper"; +import { Common } from "../../../utils/common"; +import RhdhNotficationsApi from "../../../support/api/notifications"; +import { Notifications } from "../../../support/api/notifications-api-structures"; +import { RhdhAuthApiHack } from "../../../support/api/rhdh-auth-api-hack"; +import { Orchestrator } from "../../../support/pages/orchestrator"; +import { NotificationPage } from "../../../support/pages/notifications"; + +test.describe("Filter high notification tests", () => { + let uiHelper: UIhelper; + let common: Common; + let orchestrator: Orchestrator; + let notificationPage: NotificationPage; + let apiToken: string; + + test.beforeEach(async ({ page }) => { + uiHelper = new UIhelper(page); + common = new Common(page); + orchestrator = new Orchestrator(page); + notificationPage = new NotificationPage(page); + await common.loginAsKeycloakUser(); + apiToken = await RhdhAuthApiHack.getToken(page); + console.log(`apiToken -> ${apiToken}`) + }); + + test("Filter notifications by severity - high", async () => { + const r = (Math.random() + 1).toString(36).substring(7); + const severity = "high"; + const notificationsApi = await RhdhNotficationsApi.build("test-token"); + // Used broadcast here, but we should use type: entity and entityRef: ["user:/"] + const notification: Notifications = { + recipients: { + type: "broadcast", + entityRef: [""], + }, + payload: { + title: `UI Notification Mark all as read ${severity}-${r}`, + description: `Test UI Notification Mark all as read ${severity}-${r}`, + severity: severity, + topic: `Testing UI Notification Mark all as read ${severity}-${r}`, + }, + }; + await notificationsApi.createNotification(notification); + await uiHelper.openSidebar("Notifications"); + await notificationPage.selectSeverity("High"); + await notificationPage.notificationContains( + `UI Notification Mark all as read ${severity}-${r}`, + ); + }); +}); diff --git a/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityLow.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityLow.spec.ts new file mode 100644 index 0000000000..bd866f1efe --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityLow.spec.ts @@ -0,0 +1,51 @@ +import { test } from "@playwright/test"; +import { UIhelper } from "../../../utils/ui-helper"; +import { Common } from "../../../utils/common"; +import RhdhNotficationsApi from "../../../support/api/notifications"; +import { Notifications } from "../../../support/api/notifications-api-structures"; +import { RhdhAuthApiHack } from "../../../support/api/rhdh-auth-api-hack"; +import { Orchestrator } from "../../../support/pages/orchestrator"; +import { NotificationPage } from "../../../support/pages/notifications"; + +test.describe("Filter low notification tests", () => { + let uiHelper: UIhelper; + let common: Common; + let orchestrator: Orchestrator; + let notificationPage: NotificationPage; + let apiToken: string; + + test.beforeEach(async ({ page }) => { + uiHelper = new UIhelper(page); + common = new Common(page); + orchestrator = new Orchestrator(page); + notificationPage = new NotificationPage(page); + await common.loginAsKeycloakUser(); + apiToken = await RhdhAuthApiHack.getToken(page); + console.log(`apiToken -> ${apiToken}`) + }); + + test("Filter notifications by severity - low", async () => { + const r = (Math.random() + 1).toString(36).substring(7); + const severity = "low"; + const notificationsApi = await RhdhNotficationsApi.build("test-token"); + // Used broadcast here, but we should use type: entity and entityRef: ["user:/"] + const notification: Notifications = { + recipients: { + type: "broadcast", + entityRef: [""], + }, + payload: { + title: `UI Notification Mark all as read ${severity}-${r}`, + description: `Test UI Notification Mark all as read ${severity}-${r}`, + severity: severity, + topic: `Testing UI Notification Mark all as read ${severity}-${r}`, + }, + }; + await notificationsApi.createNotification(notification); + await uiHelper.openSidebar("Notifications"); + await notificationPage.selectSeverity("Low"); + await notificationPage.notificationContains( + `UI Notification Mark all as read ${severity}-${r}`, + ); + }); +}); diff --git a/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityNormal.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityNormal.spec.ts new file mode 100644 index 0000000000..fe603415bb --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityNormal.spec.ts @@ -0,0 +1,51 @@ +import { test } from "@playwright/test"; +import { UIhelper } from "../../../utils/ui-helper"; +import { Common } from "../../../utils/common"; +import RhdhNotficationsApi from "../../../support/api/notifications"; +import { Notifications } from "../../../support/api/notifications-api-structures"; +import { RhdhAuthApiHack } from "../../../support/api/rhdh-auth-api-hack"; +import { Orchestrator } from "../../../support/pages/orchestrator"; +import { NotificationPage } from "../../../support/pages/notifications"; + +test.describe("Filter normal notification tests", () => { + let uiHelper: UIhelper; + let common: Common; + let orchestrator: Orchestrator; + let notificationPage: NotificationPage; + let apiToken: string; + + test.beforeEach(async ({ page }) => { + uiHelper = new UIhelper(page); + common = new Common(page); + orchestrator = new Orchestrator(page); + notificationPage = new NotificationPage(page); + await common.loginAsKeycloakUser(); + apiToken = await RhdhAuthApiHack.getToken(page); + console.log(`apiToken -> ${apiToken}`) + }); + + test("Filter notifications by severity - normal", async () => { + const r = (Math.random() + 1).toString(36).substring(7); + const severity = "normal"; + const notificationsApi = await RhdhNotficationsApi.build("test-token"); + // Used broadcast here, but we should use type: entity and entityRef: ["user:/"] + const notification: Notifications = { + recipients: { + type: "broadcast", + entityRef: [""], + }, + payload: { + title: `UI Notification Mark all as read ${severity}-${r}`, + description: `Test UI Notification Mark all as read ${severity}-${r}`, + severity: severity, + topic: `Testing UI Notification Mark all as read ${severity}-${r}`, + }, + }; + await notificationsApi.createNotification(notification); + await uiHelper.openSidebar("Notifications"); + await notificationPage.selectSeverity("Normal"); + await notificationPage.notificationContains( + `UI Notification Mark all as read ${severity}-${r}`, + ); + }); +}); diff --git a/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsRead.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsRead.spec.ts new file mode 100644 index 0000000000..6271b17dc7 --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsRead.spec.ts @@ -0,0 +1,28 @@ +import { test } from "@playwright/test"; +import { OrchestratorPages } from "../../utils/orchestrator_common/orchestratorPages"; +import { createNotification } from "../../utils/common/api"; + +test("OCP-74287: Mark notification as read @notifications @parallel", async ({ page }) => { + let r = (Math.random() + 1).toString(36).substring(7); + await createNotification( + "entity", + ["user:default/guest"], + `UI Notification Mark as read ${r}`, + `Test UI Notification Mark as read ${r}`, + "normal", + `Testing UI Notification Mark as read ${r}` + ); + const orchestratorPages = new OrchestratorPages(page); + await orchestratorPages.goto(); + await orchestratorPages.loginAsGuest(); + await orchestratorPages.clickNotificationsNavBarItem(); + await orchestratorPages.clickNotificationsPersonalTab(); + await orchestratorPages.notificationTextExists( + `UI Notification Mark as read ${r}` + ); + await orchestratorPages.markNotificationAsRead(`UI Notification Mark as read ${r}`); + await orchestratorPages.viewRead(); + await orchestratorPages.notificationTextExists( + RegExp(`UI Notification Mark as read ${r}.*(a few seconds ago)|(a minute ago)`) + ); +}); diff --git a/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsSaved.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsSaved.spec.ts new file mode 100644 index 0000000000..f39ee1023e --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsSaved.spec.ts @@ -0,0 +1,29 @@ +import { test } from "@playwright/test"; +import { OrchestratorPages } from "../../utils/orchestrator_common/orchestratorPages"; +import { createNotification } from "../../utils/common/api"; + +test("OCP-74573: Implement filter on Saved - https://issues.redhat.com/browse/FLPATH-1001 @notifications @parallel", async ({ + page, +}) => { + let r = (Math.random() + 1).toString(36).substring(7); + await createNotification( + "entity", + ["user:default/guest"], + `UI Notification save selected ${r}`, + `Test UI Notification save selected ${r}`, + "normal", + `Testing UI Notification save selected ${r}` + ); + const orchestratorPages = new OrchestratorPages(page); + await orchestratorPages.goto(); + await orchestratorPages.loginAsGuest(); + await orchestratorPages.clickNotificationsNavBarItem(); + await orchestratorPages.selectNotification(); + await orchestratorPages.saveSelected(); + await orchestratorPages.viewSaved(); + await orchestratorPages.notificationContains( + RegExp( + `UI Notification save selected ${r}.*(a few seconds ago)|(a minute ago)` + ) + ); +}); diff --git a/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsUnRead.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsUnRead.spec.ts new file mode 100644 index 0000000000..560f63d14a --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsUnRead.spec.ts @@ -0,0 +1,33 @@ +import { test } from "@playwright/test"; +import { OrchestratorPages } from "../../utils/orchestrator_common/orchestratorPages"; +import { createNotification } from "../../utils/common/api"; + +test("OCP-74288: Mark notification as un-read @notifications @parallel", async ({ page }) => { + let r = (Math.random() + 1).toString(36).substring(7); + await createNotification( + "entity", + ["user:default/guest"], + `UI Notification Mark as unread - ${r}`, + `Test UI Notification Mark as unread - ${r}`, + "normal", + `Testing UI Notification Mark as unread - ${r}` + ); + const orchestratorPages = new OrchestratorPages(page); + await orchestratorPages.goto(); + await orchestratorPages.loginAsGuest(); + await orchestratorPages.clickNotificationsNavBarItem(); + await orchestratorPages.clickNotificationsPersonalTab(); + await orchestratorPages.notificationContains( + `UI Notification Mark as unread - ${r}` + ); + await orchestratorPages.markLastNotificationAsRead(); + await orchestratorPages.viewRead(); + await orchestratorPages.notificationContains( + `UI Notification Mark as unread - ${r}` + ); + await orchestratorPages.markLastNotificationAsUnRead(); + await orchestratorPages.viewUnRead(); + await orchestratorPages.notificationContains( + `UI Notification Mark as unread - ${r}` + ); +}); diff --git a/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflow.spec.ts b/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflow.spec.ts new file mode 100644 index 0000000000..c51dd9ea84 --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflow.spec.ts @@ -0,0 +1,22 @@ +import { test } from "@playwright/test"; +import { UIhelper } from "../../../utils/ui-helper"; +import { Common } from "../../../utils/common"; +import { Orchestrator } from "../../../support/pages/orchestrator"; + +test.describe("Greeting workflow tests", () => { + let uiHelper: UIhelper; + let common: Common; + let orchestrator: Orchestrator; + + test.beforeEach(async ({ page }) => { + uiHelper = new UIhelper(page); + orchestrator = new Orchestrator(page); + await common.loginAsKeycloakUser(); + }); + + test("Orchestrator greeting workflow execution", async () => { + await uiHelper.openSidebar("Orchestrator"); + await orchestrator.selectGreetingWorkflowItem(); + await orchestrator.runGreetingWorkflow(); + }); +}); diff --git a/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowReRun.spec.ts b/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowReRun.spec.ts new file mode 100644 index 0000000000..d25018b367 --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowReRun.spec.ts @@ -0,0 +1,23 @@ +import { test } from "@playwright/test"; +import { UIhelper } from "../../../utils/ui-helper"; +import { Common } from "../../../utils/common"; +import { Orchestrator } from "../../../support/pages/orchestrator"; + +test.describe("Greeting workflow tests", () => { + let uiHelper: UIhelper; + let common: Common; + let orchestrator: Orchestrator; + + test.beforeEach(async ({ page }) => { + uiHelper = new UIhelper(page); + orchestrator = new Orchestrator(page); + await common.loginAsKeycloakUser(); + }); + + test("Orchestrator greeting workflow re-run execution", async () => { + await uiHelper.openSidebar("Orchestrator"); + await orchestrator.selectGreetingWorkflowItem(); + await orchestrator.runGreetingWorkflow(); + await orchestrator.reRunGreetingWorkflow(); + }); +}); diff --git a/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowRunsDetailsValidations.spec.ts b/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowRunsDetailsValidations.spec.ts new file mode 100644 index 0000000000..879ee1733e --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowRunsDetailsValidations.spec.ts @@ -0,0 +1,24 @@ +import { test } from "@playwright/test"; +import { UIhelper } from "../../../utils/ui-helper"; +import { Common } from "../../../utils/common"; +import { Orchestrator } from "../../../support/pages/orchestrator"; + +test.describe("Greeting workflow Run Details tests", () => { + let uiHelper: UIhelper; + let common: Common; + let orchestrator: Orchestrator; + + test.beforeEach(async ({ page }) => { + uiHelper = new UIhelper(page); + orchestrator = new Orchestrator(page); + await common.loginAsKeycloakUser(); + }); + + test("Orchestrator Workflow Run Details Validations", async () => { + await uiHelper.openSidebar("Orchestrator"); + await orchestrator.selectGreetingWorkflowItem(); + await orchestrator.runGreetingWorkflow(); + await orchestrator.reRunGreetingWorkflow(); + await orchestrator.validateWorkflowRunsDetails(); + }); +}); diff --git a/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowRunsValidations.spec.ts b/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowRunsValidations.spec.ts new file mode 100644 index 0000000000..580ecddb69 --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowRunsValidations.spec.ts @@ -0,0 +1,87 @@ +import { test } from "@playwright/test"; +import { UIhelper } from "../../../utils/ui-helper"; +import { Common } from "../../../utils/common"; +import { Orchestrator } from "../../../support/pages/orchestrator"; + +test.describe("Greeting workflow Run Details tests", () => { + let uiHelper: UIhelper; + let common: Common; + let orchestrator: Orchestrator; + + test.beforeEach(async ({ page }) => { + uiHelper = new UIhelper(page); + orchestrator = new Orchestrator(page); + await common.loginAsKeycloakUser(); + }); + + test("OCP-71620: Orchestrator Workflow Runs Validations @greeting @parallel", async ({ page }) => { + await page.goto("/"); + await CommonPageTestUtils.getNavBarItem(page, "Orchestrator").click(); + if (`${process.env.MILESTONE}` == '3') { + await OrchestratorPageTestUtils.workflowRunsTab(page).click(); + } + else { + await OrchestratorPageTestUtils.allRunsTab(page).click(); + } + let headerName = "All runs" + if (`${process.env.MILESTONE}` == '3') { + headerName = "Workflow Runs" + const workflowRunsHeader = page.getByRole("heading", { + name: headerName, + }); + await expect(workflowRunsHeader).toBeVisible(); + await expect(workflowRunsHeader).toHaveText(headerName); + } + await expect( + OrchestratorPageTestUtils.workflowInstanceTableCell(page, 0, 0) + ).toBeVisible(); + await expect(page.getByTestId("select").first()).toHaveAttribute( + "aria-label", + "Status" + ); + await page.getByTestId("select").first().click(); + await expect(page.getByRole("option", { name: "All" })).toHaveText("All"); + await expect(page.getByRole("option", { name: "Active" })).toHaveText( + "Active" + ); + await expect(page.getByRole("option", { name: "Error" })).toHaveText("Error"); + await expect(page.getByRole("option", { name: "Completed" })).toHaveText( + "Completed" + ); + await expect(page.getByRole("option", { name: "Aborted" })).toHaveText( + "Aborted" + ); + await expect(page.getByRole("option", { name: "Suspended" })).toHaveText( + "Suspended" + ); + await page.getByRole("option", { name: "All" }).click(); + await expect(CommonPageTestUtils.getColumnWithText(page, "ID")).toBeVisible(); + let name = "" + let status = "" + if (`${process.env.MILESTONE}` == '3') { + name = "Name" + } else { + name = "Workflow name" + } + if (`${process.env.MILESTONE}` >= '6') { + status = "Run Status" + } else { + status = "Status" + } + await expect( + CommonPageTestUtils.getColumnWithText(page, name) + ).toBeVisible(); + await expect( + CommonPageTestUtils.getColumnWithText(page, status) + ).toBeVisible(); + await expect( + CommonPageTestUtils.getColumnWithText(page, "Category") + ).toBeVisible(); + await expect( + CommonPageTestUtils.getColumnWithText(page, "Started") + ).toBeVisible(); + await expect( + CommonPageTestUtils.getColumnWithText(page, "Duration") + ).toBeVisible(); + }); +}); diff --git a/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowValidations.spec.ts b/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowValidations.spec.ts new file mode 100644 index 0000000000..a882dd6fda --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowValidations.spec.ts @@ -0,0 +1,93 @@ +import { test, expect } from "@playwright/test"; +import OrchestratorPageTestUtils from "../../utils/orchestrator/TestUtils/OrchestratorPageLocators"; +import CommonPageTestUtils from "../../utils/orchestrator_common/Common"; +import LoginPageTestUtils from "../../utils/orchestrator_common/Login"; +import Workflows from "../../utils/orchestrator_common/Workflows"; + +export enum WorkflowNames { + MOVE2KUBE = "Move2Kube workflow", + GREETING_WORKFLOW = "Greeting workflow", + MTA_ANALYSIS = "MTA Analysis", +} + +test("OCP-71621: Orchestrator Workflow Validations @greeting @parallel", async ({ page }) => { + await page.goto("/"); + await LoginPageTestUtils.signInAsGuest(page).click(); + await CommonPageTestUtils.getNavBarItem(page, "Orchestrator").click(); + await OrchestratorPageTestUtils.workflowsTab(page).click(); + const workflowHeader = page.getByRole("heading", { name: "Workflows" }); + await expect(workflowHeader).toBeVisible(); + await expect(workflowHeader).toHaveText("Workflows"); + await expect(Workflows.workflowsTable(page)).toBeVisible(); + if (`${process.env.MILESTONE}` >= "6") { + expect( + await CommonPageTestUtils.getInputByAriaLabel(page, "Search") + ).toHaveAttribute("placeholder", "Search"); + } else { expect( + await CommonPageTestUtils.getInputByAriaLabel(page, "Search") + ).toHaveAttribute("placeholder", "Filter"); + } + await expect( + CommonPageTestUtils.getColumnWithText(page, "Name") + ).toBeVisible(); + await expect( + CommonPageTestUtils.getColumnWithText(page, "Category") + ).toBeVisible(); + if (`${process.env.MILESTONE}` >= "6") { + await expect( + CommonPageTestUtils.getColumnWithText(page, "Workflow status") + ).toBeVisible(); + } + await expect( + CommonPageTestUtils.getColumnWithText(page, "Last run") + ).toBeVisible(); + await expect( + CommonPageTestUtils.getColumnWithText(page, "Last run status") + ).toBeVisible(); + if (`${process.env.MILESTONE}` == "3") { + await expect(CommonPageTestUtils.getColumnWithText(page, "Avg. duration")).toBeVisible(); + } + await expect( + CommonPageTestUtils.getColumnWithText(page, "Actions") + ).toBeVisible(); + const workFlowRow = page.locator( + `tr:has-text("${WorkflowNames.GREETING_WORKFLOW}")` + ); + await expect(workFlowRow.locator("td").nth(0)).toHaveText( + "Greeting workflow" + ); + await expect(workFlowRow.locator("td").nth(1)).toHaveText("Infrastructure"); + // await expect(workFlowRow.locator('td').nth(2)).toHaveText(RegExp("(\d{1,2})/(\d{1,2})/(\d{2,4}), (\d{1,2}):(\d{1,2}):(\d{1,2}) (AM|PM)")) + if (`${process.env.MILESTONE}` >= "6") { + await expect(workFlowRow.locator("td").nth(2)).toHaveText("Available"); + await expect(workFlowRow.locator("td").nth(3)).toHaveText(/^\d{1,2}\/\d{1,2}\/\d{4}, \d{1,2}:\d{1,2}:\d{1,2} (AM|PM)$/); + await expect(workFlowRow.locator("td").nth(4)).toHaveText("Completed"); + } else { + await expect(workFlowRow.locator("td").nth(3)).toHaveText("Completed"); + } + if (`${process.env.MILESTONE}` == "3") { + await expect(workFlowRow.locator("td").nth(4)).toHaveText("a few seconds"); + await expect(workFlowRow.locator("td").nth(5)).toHaveText( + "YAML based greeting workflow" + ); + } else if (`${process.env.MILESTONE}` >= "6") { + await expect(workFlowRow.locator("td").nth(5)).toHaveText( + "YAML based greeting workflow" + ); + } else { + await expect(workFlowRow.locator("td").nth(4)).toHaveText( + "YAML based greeting workflow" + ); + } + if (`${process.env.MILESTONE}` == "3") { + await expect( + workFlowRow.getByRole("button", { name: "Execute" }) + ).toBeVisible(); + await expect(workFlowRow.getByRole("button", { name: "View" })).toBeVisible(); + } + else { + await expect(workFlowRow.getByRole('button', { name: 'Run', exact: true }).first()).toBeVisible(); + await expect(workFlowRow.getByRole('button', { name: 'View runs' }).first()).toBeVisible(); + if (`${process.env.MILESTONE}` >= "6") await expect(workFlowRow.getByRole('button', { name: 'View input schema' }).first()).toBeVisible(); + } +}); diff --git a/e2e-tests/playwright/e2e/plugins/orchestrator/userOnBoardingWorkflow.spec.ts b/e2e-tests/playwright/e2e/plugins/orchestrator/userOnBoardingWorkflow.spec.ts new file mode 100644 index 0000000000..35840e533f --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/orchestrator/userOnBoardingWorkflow.spec.ts @@ -0,0 +1,15 @@ +import { test } from "@playwright/test"; +import { OrchestratorPages } from "../../utils/orchestrator_common/orchestratorPages"; + +test("OCP-xxxxx: Orchestrator user on-boarding workflow execution @onboarding @parallel", async ({ + page, +}) => { + const orchestratorPages = new OrchestratorPages(page); + await orchestratorPages.goto(); + await orchestratorPages.loginAsGuest(); + await orchestratorPages.clickOrchestratorNavBarItem(); + await orchestratorPages.selectUserOnboardingWorkflowItem(); + await orchestratorPages.runUserOnboardingWorkflow(); + await orchestratorPages.clickNotificationsNavBarItem(); + await orchestratorPages.notificationContains(/Onboarding user.*completed/); +}); diff --git a/e2e-tests/playwright/support/pages/orchestrator.ts b/e2e-tests/playwright/support/pages/orchestrator.ts index 96fa385e57..88f8f30f33 100644 --- a/e2e-tests/playwright/support/pages/orchestrator.ts +++ b/e2e-tests/playwright/support/pages/orchestrator.ts @@ -31,6 +31,92 @@ export class Orchestrator { await this.page.getByRole("link", { name: "User Onboarding" }).click(); } + async selectGreetingWorkflowItem() { + const workflowHeader = this.page.getByRole("heading", { + name: "Workflows", + }); + await expect(workflowHeader).toBeVisible(); + await expect(workflowHeader).toHaveText("Workflows"); + await expect(Workflows.workflowsTable(this.page)).toBeVisible(); + await this.page.getByRole("link", { name: "Greeting workflow" }).click(); + } + + async runGreetingWorkflow(language = "English", status = "Completed") { + const runButton = this.page.getByRole('button', { name: "Run" }); + await expect(runButton).toBeVisible(); + await runButton.click(); + if (`${process.env.MILESTONE}` == "2") { + await this.page.locator("#root_language").click(); + await this.page.getByRole("option", { name: language}).click(); + } else { + await this.page.getByLabel("Language").click(); + await this.page.getByRole("option", { name: "English" }).click(); + } + await this.page.getByRole('button', { name: "Next"}).click(); + await this.page.getByRole('button', { name: "Run"}).click(); + await expect(this.page.getByText(`${status}`, { exact: true })).toBeVisible( + { + timeout: 600000, + } + ); + } + + async reRunGreetingWorkflow(language = "English", status = "Completed") { + if (`${process.env.MILESTONE}` >= "6") { + await expect( + this.page.getByText("Run again") + ).toBeVisible(); + await this.page.getByText("Run again").click(); + } else { + await expect( + this.page.getByRole('button', { name: "Rerun"}) + ).toBeVisible(); + await this.page.getByRole('button', { name: "Rerun"}).click(); + } + if (`${process.env.MILESTONE}` == "2") { + await this.page.locator("#root_language").click(); + await this.page.getByRole("option", { name: language}).click(); + } else { + await this.page.getByLabel("Language").click(); + await this.page.getByRole("option", { name: "English" }).click(); + } + await this.page.getByRole('button', { name: "Next"}).click(); + await this.page.getByRole('button', { name: "Run"}).click(); + await expect(this.page.getByText(`${status}`, { exact: true })).toBeVisible( + { + timeout: 600000, + } + ); + } + + async validateWorkflowRunsDetails() { + await expect(this.page.getByText("Details")).toBeVisible(); + await expect(this.page.getByText("Results")).toBeVisible(); + if (`${process.env.MILESTONE}` == '3') { + await expect(this.page.getByText("Workflow definition")).toBeVisible(); + } + await expect(this.page.getByText("Workflow progress")).toBeVisible(); + let workFlowStatusRegex: RegExp + if (`${process.env.MILESTONE}` >= "6") { + workFlowStatusRegex = /^Completed$/ + } else { + workFlowStatusRegex = /^Status Completed$/ + } + await expect( + this.page + .locator("div") + .filter({ hasText: workFlowStatusRegex }) + .first() + ).toBeVisible(); + if (`${process.env.MILESTONE}` == '3') { + await expect(this.page.locator('[data-testid="kogito-iframe"]')).toBeVisible(); + } + } + + async validateWorkflowRuns() { + + } + async getPageUrl() { return this.page.url(); } diff --git a/e2e-tests/playwright/utils/navbar.ts b/e2e-tests/playwright/utils/navbar.ts index fc15ac2454..651984ed1f 100644 --- a/e2e-tests/playwright/utils/navbar.ts +++ b/e2e-tests/playwright/utils/navbar.ts @@ -10,4 +10,5 @@ export type SidebarTabs = | "Docs" | "Clusters" | "Tech Radar" - | "Notifications"; + | "Notifications" + | "Orchestrator"; From b6b8130189aaa2b75f754c45c00ecb419215d872 Mon Sep 17 00:00:00 2001 From: Thomas Stetson Date: Mon, 16 Jun 2025 17:23:50 -0400 Subject: [PATCH 02/12] collapse some spec files, polish notification and workflow tests, onboarding still needs work --- ...ter-notification-severity-critical.spec.ts | 54 ------- .../filterNotificationSeverityHigh.spec.ts | 51 ------- .../filterNotificationSeverityLow.spec.ts | 51 ------- .../filterNotificationSeverityNormal.spec.ts | 51 ------- .../filterNotificationsBySeverity.spec.ts | 55 +++++++ .../markNotificationAsRead.spec.ts | 28 ---- .../markNotificationAsSaved.spec.ts | 29 ---- .../markNotificationAsUnRead.spec.ts | 33 ---- .../notifications/markNotifications.spec.ts | 109 ++++++++++++++ .../orchestrator/greetingWorkflow.spec.ts | 15 +- ...tingWorkflowRunsDetailsValidations.spec.ts | 24 --- .../greetingWorkflowRunsValidations.spec.ts | 87 ----------- .../greetingWorkflowValidations.spec.ts | 93 ------------ .../userOnBoardingWorkflow.spec.ts | 35 +++-- ....ts => workflowAllRunsValidations.spec.ts} | 9 +- .../playwright/support/pages/notifications.ts | 10 +- .../playwright/support/pages/orchestrator.ts | 141 ++++++++++++++++-- .../playwright/support/pages/workflows.ts | 2 +- 18 files changed, 345 insertions(+), 532 deletions(-) delete mode 100644 e2e-tests/playwright/e2e/plugins/notifications/filter-notification-severity-critical.spec.ts delete mode 100644 e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityHigh.spec.ts delete mode 100644 e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityLow.spec.ts delete mode 100644 e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityNormal.spec.ts create mode 100644 e2e-tests/playwright/e2e/plugins/notifications/filterNotificationsBySeverity.spec.ts delete mode 100644 e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsRead.spec.ts delete mode 100644 e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsSaved.spec.ts delete mode 100644 e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsUnRead.spec.ts create mode 100644 e2e-tests/playwright/e2e/plugins/notifications/markNotifications.spec.ts delete mode 100644 e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowRunsDetailsValidations.spec.ts delete mode 100644 e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowRunsValidations.spec.ts delete mode 100644 e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowValidations.spec.ts rename e2e-tests/playwright/e2e/plugins/orchestrator/{greetingWorkflowReRun.spec.ts => workflowAllRunsValidations.spec.ts} (65%) diff --git a/e2e-tests/playwright/e2e/plugins/notifications/filter-notification-severity-critical.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/filter-notification-severity-critical.spec.ts deleted file mode 100644 index 7a2bbae652..0000000000 --- a/e2e-tests/playwright/e2e/plugins/notifications/filter-notification-severity-critical.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { test } from "@playwright/test"; -import { UIhelper } from "../../../utils/ui-helper"; -import { Common } from "../../../utils/common"; -import RhdhNotficationsApi from "../../../support/api/notifications"; -import { Notifications } from "../../../support/api/notifications-api-structures"; -import { RhdhAuthApiHack } from "../../../support/api/rhdh-auth-api-hack"; - -import { NotificationPage } from "../../../support/pages/notifications"; - -test.describe("Filter critical notification tests", () => { - let uiHelper: UIhelper; - let common: Common; - let notificationPage: NotificationPage; - - test.beforeAll(async () => { - test.info().annotations.push({ - type: "component", - description: "integration", - }); - }); - - test.beforeEach(async ({ page }) => { - uiHelper = new UIhelper(page); - common = new Common(page); - notificationPage = new NotificationPage(page); - await common.loginAsKeycloakUser(); - await RhdhAuthApiHack.getToken(page); - }); - - test("Filter notifications by severity - critical", async () => { - const r = (Math.random() + 1).toString(36).substring(7); - const severity = "critical"; - const notificationsApi = await RhdhNotficationsApi.build("test-token"); - // Used broadcast here, but we should use type: entity and entityRef: ["user:/"] - const notification: Notifications = { - recipients: { - type: "broadcast", - entityRef: [""], - }, - payload: { - title: `UI Notification Mark all as read ${severity}-${r}`, - description: `Test UI Notification Mark all as read ${severity}-${r}`, - severity: severity, - topic: `Testing UI Notification Mark all as read ${severity}-${r}`, - }, - }; - await notificationsApi.createNotification(notification); - await uiHelper.openSidebar("Notifications"); - await notificationPage.selectSeverity("Critical"); - await notificationPage.notificationContains( - `UI Notification Mark all as read ${severity}-${r}`, - ); - }); -}); diff --git a/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityHigh.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityHigh.spec.ts deleted file mode 100644 index 210290ab57..0000000000 --- a/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityHigh.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { test } from "@playwright/test"; -import { UIhelper } from "../../../utils/ui-helper"; -import { Common } from "../../../utils/common"; -import RhdhNotficationsApi from "../../../support/api/notifications"; -import { Notifications } from "../../../support/api/notifications-api-structures"; -import { RhdhAuthApiHack } from "../../../support/api/rhdh-auth-api-hack"; -import { Orchestrator } from "../../../support/pages/orchestrator"; -import { NotificationPage } from "../../../support/pages/notifications"; - -test.describe("Filter high notification tests", () => { - let uiHelper: UIhelper; - let common: Common; - let orchestrator: Orchestrator; - let notificationPage: NotificationPage; - let apiToken: string; - - test.beforeEach(async ({ page }) => { - uiHelper = new UIhelper(page); - common = new Common(page); - orchestrator = new Orchestrator(page); - notificationPage = new NotificationPage(page); - await common.loginAsKeycloakUser(); - apiToken = await RhdhAuthApiHack.getToken(page); - console.log(`apiToken -> ${apiToken}`) - }); - - test("Filter notifications by severity - high", async () => { - const r = (Math.random() + 1).toString(36).substring(7); - const severity = "high"; - const notificationsApi = await RhdhNotficationsApi.build("test-token"); - // Used broadcast here, but we should use type: entity and entityRef: ["user:/"] - const notification: Notifications = { - recipients: { - type: "broadcast", - entityRef: [""], - }, - payload: { - title: `UI Notification Mark all as read ${severity}-${r}`, - description: `Test UI Notification Mark all as read ${severity}-${r}`, - severity: severity, - topic: `Testing UI Notification Mark all as read ${severity}-${r}`, - }, - }; - await notificationsApi.createNotification(notification); - await uiHelper.openSidebar("Notifications"); - await notificationPage.selectSeverity("High"); - await notificationPage.notificationContains( - `UI Notification Mark all as read ${severity}-${r}`, - ); - }); -}); diff --git a/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityLow.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityLow.spec.ts deleted file mode 100644 index bd866f1efe..0000000000 --- a/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityLow.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { test } from "@playwright/test"; -import { UIhelper } from "../../../utils/ui-helper"; -import { Common } from "../../../utils/common"; -import RhdhNotficationsApi from "../../../support/api/notifications"; -import { Notifications } from "../../../support/api/notifications-api-structures"; -import { RhdhAuthApiHack } from "../../../support/api/rhdh-auth-api-hack"; -import { Orchestrator } from "../../../support/pages/orchestrator"; -import { NotificationPage } from "../../../support/pages/notifications"; - -test.describe("Filter low notification tests", () => { - let uiHelper: UIhelper; - let common: Common; - let orchestrator: Orchestrator; - let notificationPage: NotificationPage; - let apiToken: string; - - test.beforeEach(async ({ page }) => { - uiHelper = new UIhelper(page); - common = new Common(page); - orchestrator = new Orchestrator(page); - notificationPage = new NotificationPage(page); - await common.loginAsKeycloakUser(); - apiToken = await RhdhAuthApiHack.getToken(page); - console.log(`apiToken -> ${apiToken}`) - }); - - test("Filter notifications by severity - low", async () => { - const r = (Math.random() + 1).toString(36).substring(7); - const severity = "low"; - const notificationsApi = await RhdhNotficationsApi.build("test-token"); - // Used broadcast here, but we should use type: entity and entityRef: ["user:/"] - const notification: Notifications = { - recipients: { - type: "broadcast", - entityRef: [""], - }, - payload: { - title: `UI Notification Mark all as read ${severity}-${r}`, - description: `Test UI Notification Mark all as read ${severity}-${r}`, - severity: severity, - topic: `Testing UI Notification Mark all as read ${severity}-${r}`, - }, - }; - await notificationsApi.createNotification(notification); - await uiHelper.openSidebar("Notifications"); - await notificationPage.selectSeverity("Low"); - await notificationPage.notificationContains( - `UI Notification Mark all as read ${severity}-${r}`, - ); - }); -}); diff --git a/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityNormal.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityNormal.spec.ts deleted file mode 100644 index fe603415bb..0000000000 --- a/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationSeverityNormal.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { test } from "@playwright/test"; -import { UIhelper } from "../../../utils/ui-helper"; -import { Common } from "../../../utils/common"; -import RhdhNotficationsApi from "../../../support/api/notifications"; -import { Notifications } from "../../../support/api/notifications-api-structures"; -import { RhdhAuthApiHack } from "../../../support/api/rhdh-auth-api-hack"; -import { Orchestrator } from "../../../support/pages/orchestrator"; -import { NotificationPage } from "../../../support/pages/notifications"; - -test.describe("Filter normal notification tests", () => { - let uiHelper: UIhelper; - let common: Common; - let orchestrator: Orchestrator; - let notificationPage: NotificationPage; - let apiToken: string; - - test.beforeEach(async ({ page }) => { - uiHelper = new UIhelper(page); - common = new Common(page); - orchestrator = new Orchestrator(page); - notificationPage = new NotificationPage(page); - await common.loginAsKeycloakUser(); - apiToken = await RhdhAuthApiHack.getToken(page); - console.log(`apiToken -> ${apiToken}`) - }); - - test("Filter notifications by severity - normal", async () => { - const r = (Math.random() + 1).toString(36).substring(7); - const severity = "normal"; - const notificationsApi = await RhdhNotficationsApi.build("test-token"); - // Used broadcast here, but we should use type: entity and entityRef: ["user:/"] - const notification: Notifications = { - recipients: { - type: "broadcast", - entityRef: [""], - }, - payload: { - title: `UI Notification Mark all as read ${severity}-${r}`, - description: `Test UI Notification Mark all as read ${severity}-${r}`, - severity: severity, - topic: `Testing UI Notification Mark all as read ${severity}-${r}`, - }, - }; - await notificationsApi.createNotification(notification); - await uiHelper.openSidebar("Notifications"); - await notificationPage.selectSeverity("Normal"); - await notificationPage.notificationContains( - `UI Notification Mark all as read ${severity}-${r}`, - ); - }); -}); diff --git a/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationsBySeverity.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationsBySeverity.spec.ts new file mode 100644 index 0000000000..6b977978d9 --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationsBySeverity.spec.ts @@ -0,0 +1,55 @@ +import { test } from "@playwright/test"; +import { UIhelper } from "../../../utils/ui-helper"; +import { Common } from "../../../utils/common"; +import RhdhNotficationsApi from "../../../support/api/notifications"; +import { Notifications } from "../../../support/api/notifications-api-structures"; +import { RhdhAuthApiHack } from "../../../support/api/rhdh-auth-api-hack"; +import { Orchestrator } from "../../../support/pages/orchestrator"; +import { NotificationPage } from "../../../support/pages/notifications"; + +test.describe("Filter critical notification tests", () => { + let uiHelper: UIhelper; + let common: Common; + let orchestrator: Orchestrator; + let notificationPage: NotificationPage; + let apiToken: string; + + let severities = ["Critical", "High", "Normal", "Low"]; + + test.beforeEach(async ({ page }) => { + uiHelper = new UIhelper(page); + common = new Common(page); + orchestrator = new Orchestrator(page); + notificationPage = new NotificationPage(page); + await common.loginAsKeycloakUser(); + apiToken = await RhdhAuthApiHack.getToken(page); + console.log(`apiToken -> ${apiToken}`) + }); + + for (const severity of severities) { + test(`Filter notifications by severity - ${severity}`, async () => { + const r = (Math.random() + 1).toString(36).substring(7); + const notificationsApi = await RhdhNotficationsApi.build(apiToken); + const notificationTitle = "UI Notification By Severity" + // Used broadcast here, but we should use type: entity and entityRef: ["user:/"] + const notification: Notifications = { + recipients: { + type: "broadcast", + entityRef: [""], + }, + payload: { + title: `${notificationTitle} ${severity}-${r}`, + description: `Test ${notificationTitle} ${severity}-${r}`, + severity: severity, + topic: `Testing ${notificationTitle} ${severity}-${r}`, + }, + }; + await notificationsApi.createNotification(notification); + await uiHelper.openSidebar("Notifications"); + await notificationPage.selectSeverity(severity); + await notificationPage.notificationContains( + `${notificationTitle} ${severity}-${r}`, + ); + }); + } +}); diff --git a/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsRead.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsRead.spec.ts deleted file mode 100644 index 6271b17dc7..0000000000 --- a/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsRead.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { test } from "@playwright/test"; -import { OrchestratorPages } from "../../utils/orchestrator_common/orchestratorPages"; -import { createNotification } from "../../utils/common/api"; - -test("OCP-74287: Mark notification as read @notifications @parallel", async ({ page }) => { - let r = (Math.random() + 1).toString(36).substring(7); - await createNotification( - "entity", - ["user:default/guest"], - `UI Notification Mark as read ${r}`, - `Test UI Notification Mark as read ${r}`, - "normal", - `Testing UI Notification Mark as read ${r}` - ); - const orchestratorPages = new OrchestratorPages(page); - await orchestratorPages.goto(); - await orchestratorPages.loginAsGuest(); - await orchestratorPages.clickNotificationsNavBarItem(); - await orchestratorPages.clickNotificationsPersonalTab(); - await orchestratorPages.notificationTextExists( - `UI Notification Mark as read ${r}` - ); - await orchestratorPages.markNotificationAsRead(`UI Notification Mark as read ${r}`); - await orchestratorPages.viewRead(); - await orchestratorPages.notificationTextExists( - RegExp(`UI Notification Mark as read ${r}.*(a few seconds ago)|(a minute ago)`) - ); -}); diff --git a/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsSaved.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsSaved.spec.ts deleted file mode 100644 index f39ee1023e..0000000000 --- a/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsSaved.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { test } from "@playwright/test"; -import { OrchestratorPages } from "../../utils/orchestrator_common/orchestratorPages"; -import { createNotification } from "../../utils/common/api"; - -test("OCP-74573: Implement filter on Saved - https://issues.redhat.com/browse/FLPATH-1001 @notifications @parallel", async ({ - page, -}) => { - let r = (Math.random() + 1).toString(36).substring(7); - await createNotification( - "entity", - ["user:default/guest"], - `UI Notification save selected ${r}`, - `Test UI Notification save selected ${r}`, - "normal", - `Testing UI Notification save selected ${r}` - ); - const orchestratorPages = new OrchestratorPages(page); - await orchestratorPages.goto(); - await orchestratorPages.loginAsGuest(); - await orchestratorPages.clickNotificationsNavBarItem(); - await orchestratorPages.selectNotification(); - await orchestratorPages.saveSelected(); - await orchestratorPages.viewSaved(); - await orchestratorPages.notificationContains( - RegExp( - `UI Notification save selected ${r}.*(a few seconds ago)|(a minute ago)` - ) - ); -}); diff --git a/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsUnRead.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsUnRead.spec.ts deleted file mode 100644 index 560f63d14a..0000000000 --- a/e2e-tests/playwright/e2e/plugins/notifications/markNotificationAsUnRead.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { test } from "@playwright/test"; -import { OrchestratorPages } from "../../utils/orchestrator_common/orchestratorPages"; -import { createNotification } from "../../utils/common/api"; - -test("OCP-74288: Mark notification as un-read @notifications @parallel", async ({ page }) => { - let r = (Math.random() + 1).toString(36).substring(7); - await createNotification( - "entity", - ["user:default/guest"], - `UI Notification Mark as unread - ${r}`, - `Test UI Notification Mark as unread - ${r}`, - "normal", - `Testing UI Notification Mark as unread - ${r}` - ); - const orchestratorPages = new OrchestratorPages(page); - await orchestratorPages.goto(); - await orchestratorPages.loginAsGuest(); - await orchestratorPages.clickNotificationsNavBarItem(); - await orchestratorPages.clickNotificationsPersonalTab(); - await orchestratorPages.notificationContains( - `UI Notification Mark as unread - ${r}` - ); - await orchestratorPages.markLastNotificationAsRead(); - await orchestratorPages.viewRead(); - await orchestratorPages.notificationContains( - `UI Notification Mark as unread - ${r}` - ); - await orchestratorPages.markLastNotificationAsUnRead(); - await orchestratorPages.viewUnRead(); - await orchestratorPages.notificationContains( - `UI Notification Mark as unread - ${r}` - ); -}); diff --git a/e2e-tests/playwright/e2e/plugins/notifications/markNotifications.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/markNotifications.spec.ts new file mode 100644 index 0000000000..a2f2366356 --- /dev/null +++ b/e2e-tests/playwright/e2e/plugins/notifications/markNotifications.spec.ts @@ -0,0 +1,109 @@ +import { test } from "@playwright/test"; +import { UIhelper } from "../../../utils/ui-helper"; +import { Common } from "../../../utils/common"; +import RhdhNotificationsApi from "../../../support/api/notifications"; +import { Notifications } from "../../../support/api/notifications-api-structures"; +import { RhdhAuthApiHack } from "../../../support/api/rhdh-auth-api-hack"; +import { Orchestrator } from "../../../support/pages/orchestrator"; +import { NotificationPage } from "../../../support/pages/notifications"; + +test.describe("Mark notification tests", () => { + let uiHelper: UIhelper; + let common: Common; + let orchestrator: Orchestrator; + let notificationPage: NotificationPage; + let apiToken: string; + + test.beforeEach(async ({ page }) => { + uiHelper = new UIhelper(page); + common = new Common(page); + orchestrator = new Orchestrator(page); + notificationPage = new NotificationPage(page); + await common.loginAsKeycloakUser(); + apiToken = await RhdhAuthApiHack.getToken(page); + console.log(`apiToken -> ${apiToken}`) + }); + + test("Mark notification as read", async () => { + const r = (Math.random() + 1).toString(36).substring(7); + const notificationsApi = await RhdhNotificationsApi.build(apiToken); + const notificationTitle = `UI Notification Mark as read ${r}` + const notification: Notifications = { + recipients: { + type: "broadcast", + entityRef: [""], + }, + payload: { + title: notificationTitle, + description: `Test ${notificationTitle}`, + severity: "normal", + topic: `Testing ${notificationTitle}`, + }, + }; + await notificationsApi.createNotification(notification); + await uiHelper.openSidebar("Notifications"); + await notificationPage.notificationTextExists(notificationTitle); + await notificationPage.markNotificationAsRead(notificationTitle); + await notificationPage.viewRead(); + await notificationPage.notificationTextExists( + RegExp(`${notificationTitle}.*(a few seconds ago)|(a minute ago)`) + ); + }); + + test("Mark notification as unread", async () => { + const r = (Math.random() + 1).toString(36).substring(7); + const notificationsApi = await RhdhNotificationsApi.build(apiToken); + const notificationTitle = `UI Notification Mark as unread ${r}` + const notification: Notifications = { + recipients: { + type: "broadcast", + entityRef: [""], + }, + payload: { + title: notificationTitle, + description: `Test ${notificationTitle}`, + severity: "normal", + topic: `Testing ${notificationTitle}`, + }, + }; + await notificationsApi.createNotification(notification); + await uiHelper.openSidebar("Notifications"); + await notificationPage.notificationTextExists(notificationTitle); + await notificationPage.markNotificationAsRead(notificationTitle); + await notificationPage.viewRead(); + await notificationPage.notificationTextExists( + RegExp(`${notificationTitle}.*(a few seconds ago)|(a minute ago)`) + ); + await notificationPage.markLastNotificationAsUnRead(); + await notificationPage.viewUnRead(); + await notificationPage.notificationTextExists( + RegExp(`${notificationTitle}.*(a few seconds ago)|(a minute ago)`) + ); + }); + + test("Mark notification as saved", async () => { + const r = (Math.random() + 1).toString(36).substring(7); + const notificationsApi = await RhdhNotificationsApi.build(apiToken); + const notificationTitle = `UI Notification Mark as saved ${r}` + const notification: Notifications = { + recipients: { + type: "broadcast", + entityRef: [""], + }, + payload: { + title: notificationTitle, + description: `Test ${notificationTitle}`, + severity: "normal", + topic: `Testing ${notificationTitle}`, + }, + }; + await notificationsApi.createNotification(notification); + await uiHelper.openSidebar("Notifications"); + await notificationPage.selectNotification(); + await notificationPage.saveSelected(); + await notificationPage.viewSaved(); + await notificationPage.notificationTextExists( + RegExp(`${notificationTitle}.*(a few seconds ago)|(a minute ago)`) + ); + }); +}); \ No newline at end of file diff --git a/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflow.spec.ts b/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflow.spec.ts index c51dd9ea84..6adfc29a6d 100644 --- a/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflow.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflow.spec.ts @@ -3,20 +3,31 @@ import { UIhelper } from "../../../utils/ui-helper"; import { Common } from "../../../utils/common"; import { Orchestrator } from "../../../support/pages/orchestrator"; -test.describe("Greeting workflow tests", () => { +test.describe("Orchestrator greeting workflow tests", () => { let uiHelper: UIhelper; let common: Common; let orchestrator: Orchestrator; test.beforeEach(async ({ page }) => { uiHelper = new UIhelper(page); + common = new Common(page); orchestrator = new Orchestrator(page); await common.loginAsKeycloakUser(); }); - test("Orchestrator greeting workflow execution", async () => { + test("Greeting workflow execution and workflow tab validation", async () => { await uiHelper.openSidebar("Orchestrator"); await orchestrator.selectGreetingWorkflowItem(); await orchestrator.runGreetingWorkflow(); + await uiHelper.openSidebar("Orchestrator"); + await orchestrator.validateGreetingWorkflow(); + }); + + test("Greeting workflow run details validation", async () => { + await uiHelper.openSidebar("Orchestrator"); + await orchestrator.selectGreetingWorkflowItem(); + await orchestrator.runGreetingWorkflow(); + await orchestrator.reRunGreetingWorkflow(); + await orchestrator.validateWorkflowRunsDetails(); }); }); diff --git a/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowRunsDetailsValidations.spec.ts b/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowRunsDetailsValidations.spec.ts deleted file mode 100644 index 879ee1733e..0000000000 --- a/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowRunsDetailsValidations.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { test } from "@playwright/test"; -import { UIhelper } from "../../../utils/ui-helper"; -import { Common } from "../../../utils/common"; -import { Orchestrator } from "../../../support/pages/orchestrator"; - -test.describe("Greeting workflow Run Details tests", () => { - let uiHelper: UIhelper; - let common: Common; - let orchestrator: Orchestrator; - - test.beforeEach(async ({ page }) => { - uiHelper = new UIhelper(page); - orchestrator = new Orchestrator(page); - await common.loginAsKeycloakUser(); - }); - - test("Orchestrator Workflow Run Details Validations", async () => { - await uiHelper.openSidebar("Orchestrator"); - await orchestrator.selectGreetingWorkflowItem(); - await orchestrator.runGreetingWorkflow(); - await orchestrator.reRunGreetingWorkflow(); - await orchestrator.validateWorkflowRunsDetails(); - }); -}); diff --git a/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowRunsValidations.spec.ts b/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowRunsValidations.spec.ts deleted file mode 100644 index 580ecddb69..0000000000 --- a/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowRunsValidations.spec.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { test } from "@playwright/test"; -import { UIhelper } from "../../../utils/ui-helper"; -import { Common } from "../../../utils/common"; -import { Orchestrator } from "../../../support/pages/orchestrator"; - -test.describe("Greeting workflow Run Details tests", () => { - let uiHelper: UIhelper; - let common: Common; - let orchestrator: Orchestrator; - - test.beforeEach(async ({ page }) => { - uiHelper = new UIhelper(page); - orchestrator = new Orchestrator(page); - await common.loginAsKeycloakUser(); - }); - - test("OCP-71620: Orchestrator Workflow Runs Validations @greeting @parallel", async ({ page }) => { - await page.goto("/"); - await CommonPageTestUtils.getNavBarItem(page, "Orchestrator").click(); - if (`${process.env.MILESTONE}` == '3') { - await OrchestratorPageTestUtils.workflowRunsTab(page).click(); - } - else { - await OrchestratorPageTestUtils.allRunsTab(page).click(); - } - let headerName = "All runs" - if (`${process.env.MILESTONE}` == '3') { - headerName = "Workflow Runs" - const workflowRunsHeader = page.getByRole("heading", { - name: headerName, - }); - await expect(workflowRunsHeader).toBeVisible(); - await expect(workflowRunsHeader).toHaveText(headerName); - } - await expect( - OrchestratorPageTestUtils.workflowInstanceTableCell(page, 0, 0) - ).toBeVisible(); - await expect(page.getByTestId("select").first()).toHaveAttribute( - "aria-label", - "Status" - ); - await page.getByTestId("select").first().click(); - await expect(page.getByRole("option", { name: "All" })).toHaveText("All"); - await expect(page.getByRole("option", { name: "Active" })).toHaveText( - "Active" - ); - await expect(page.getByRole("option", { name: "Error" })).toHaveText("Error"); - await expect(page.getByRole("option", { name: "Completed" })).toHaveText( - "Completed" - ); - await expect(page.getByRole("option", { name: "Aborted" })).toHaveText( - "Aborted" - ); - await expect(page.getByRole("option", { name: "Suspended" })).toHaveText( - "Suspended" - ); - await page.getByRole("option", { name: "All" }).click(); - await expect(CommonPageTestUtils.getColumnWithText(page, "ID")).toBeVisible(); - let name = "" - let status = "" - if (`${process.env.MILESTONE}` == '3') { - name = "Name" - } else { - name = "Workflow name" - } - if (`${process.env.MILESTONE}` >= '6') { - status = "Run Status" - } else { - status = "Status" - } - await expect( - CommonPageTestUtils.getColumnWithText(page, name) - ).toBeVisible(); - await expect( - CommonPageTestUtils.getColumnWithText(page, status) - ).toBeVisible(); - await expect( - CommonPageTestUtils.getColumnWithText(page, "Category") - ).toBeVisible(); - await expect( - CommonPageTestUtils.getColumnWithText(page, "Started") - ).toBeVisible(); - await expect( - CommonPageTestUtils.getColumnWithText(page, "Duration") - ).toBeVisible(); - }); -}); diff --git a/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowValidations.spec.ts b/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowValidations.spec.ts deleted file mode 100644 index a882dd6fda..0000000000 --- a/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowValidations.spec.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { test, expect } from "@playwright/test"; -import OrchestratorPageTestUtils from "../../utils/orchestrator/TestUtils/OrchestratorPageLocators"; -import CommonPageTestUtils from "../../utils/orchestrator_common/Common"; -import LoginPageTestUtils from "../../utils/orchestrator_common/Login"; -import Workflows from "../../utils/orchestrator_common/Workflows"; - -export enum WorkflowNames { - MOVE2KUBE = "Move2Kube workflow", - GREETING_WORKFLOW = "Greeting workflow", - MTA_ANALYSIS = "MTA Analysis", -} - -test("OCP-71621: Orchestrator Workflow Validations @greeting @parallel", async ({ page }) => { - await page.goto("/"); - await LoginPageTestUtils.signInAsGuest(page).click(); - await CommonPageTestUtils.getNavBarItem(page, "Orchestrator").click(); - await OrchestratorPageTestUtils.workflowsTab(page).click(); - const workflowHeader = page.getByRole("heading", { name: "Workflows" }); - await expect(workflowHeader).toBeVisible(); - await expect(workflowHeader).toHaveText("Workflows"); - await expect(Workflows.workflowsTable(page)).toBeVisible(); - if (`${process.env.MILESTONE}` >= "6") { - expect( - await CommonPageTestUtils.getInputByAriaLabel(page, "Search") - ).toHaveAttribute("placeholder", "Search"); - } else { expect( - await CommonPageTestUtils.getInputByAriaLabel(page, "Search") - ).toHaveAttribute("placeholder", "Filter"); - } - await expect( - CommonPageTestUtils.getColumnWithText(page, "Name") - ).toBeVisible(); - await expect( - CommonPageTestUtils.getColumnWithText(page, "Category") - ).toBeVisible(); - if (`${process.env.MILESTONE}` >= "6") { - await expect( - CommonPageTestUtils.getColumnWithText(page, "Workflow status") - ).toBeVisible(); - } - await expect( - CommonPageTestUtils.getColumnWithText(page, "Last run") - ).toBeVisible(); - await expect( - CommonPageTestUtils.getColumnWithText(page, "Last run status") - ).toBeVisible(); - if (`${process.env.MILESTONE}` == "3") { - await expect(CommonPageTestUtils.getColumnWithText(page, "Avg. duration")).toBeVisible(); - } - await expect( - CommonPageTestUtils.getColumnWithText(page, "Actions") - ).toBeVisible(); - const workFlowRow = page.locator( - `tr:has-text("${WorkflowNames.GREETING_WORKFLOW}")` - ); - await expect(workFlowRow.locator("td").nth(0)).toHaveText( - "Greeting workflow" - ); - await expect(workFlowRow.locator("td").nth(1)).toHaveText("Infrastructure"); - // await expect(workFlowRow.locator('td').nth(2)).toHaveText(RegExp("(\d{1,2})/(\d{1,2})/(\d{2,4}), (\d{1,2}):(\d{1,2}):(\d{1,2}) (AM|PM)")) - if (`${process.env.MILESTONE}` >= "6") { - await expect(workFlowRow.locator("td").nth(2)).toHaveText("Available"); - await expect(workFlowRow.locator("td").nth(3)).toHaveText(/^\d{1,2}\/\d{1,2}\/\d{4}, \d{1,2}:\d{1,2}:\d{1,2} (AM|PM)$/); - await expect(workFlowRow.locator("td").nth(4)).toHaveText("Completed"); - } else { - await expect(workFlowRow.locator("td").nth(3)).toHaveText("Completed"); - } - if (`${process.env.MILESTONE}` == "3") { - await expect(workFlowRow.locator("td").nth(4)).toHaveText("a few seconds"); - await expect(workFlowRow.locator("td").nth(5)).toHaveText( - "YAML based greeting workflow" - ); - } else if (`${process.env.MILESTONE}` >= "6") { - await expect(workFlowRow.locator("td").nth(5)).toHaveText( - "YAML based greeting workflow" - ); - } else { - await expect(workFlowRow.locator("td").nth(4)).toHaveText( - "YAML based greeting workflow" - ); - } - if (`${process.env.MILESTONE}` == "3") { - await expect( - workFlowRow.getByRole("button", { name: "Execute" }) - ).toBeVisible(); - await expect(workFlowRow.getByRole("button", { name: "View" })).toBeVisible(); - } - else { - await expect(workFlowRow.getByRole('button', { name: 'Run', exact: true }).first()).toBeVisible(); - await expect(workFlowRow.getByRole('button', { name: 'View runs' }).first()).toBeVisible(); - if (`${process.env.MILESTONE}` >= "6") await expect(workFlowRow.getByRole('button', { name: 'View input schema' }).first()).toBeVisible(); - } -}); diff --git a/e2e-tests/playwright/e2e/plugins/orchestrator/userOnBoardingWorkflow.spec.ts b/e2e-tests/playwright/e2e/plugins/orchestrator/userOnBoardingWorkflow.spec.ts index 35840e533f..c1eb5b2fd0 100644 --- a/e2e-tests/playwright/e2e/plugins/orchestrator/userOnBoardingWorkflow.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/orchestrator/userOnBoardingWorkflow.spec.ts @@ -1,15 +1,26 @@ import { test } from "@playwright/test"; -import { OrchestratorPages } from "../../utils/orchestrator_common/orchestratorPages"; +import { UIhelper } from "../../../utils/ui-helper"; +import { Common } from "../../../utils/common"; +import { Orchestrator } from "../../../support/pages/orchestrator"; -test("OCP-xxxxx: Orchestrator user on-boarding workflow execution @onboarding @parallel", async ({ - page, -}) => { - const orchestratorPages = new OrchestratorPages(page); - await orchestratorPages.goto(); - await orchestratorPages.loginAsGuest(); - await orchestratorPages.clickOrchestratorNavBarItem(); - await orchestratorPages.selectUserOnboardingWorkflowItem(); - await orchestratorPages.runUserOnboardingWorkflow(); - await orchestratorPages.clickNotificationsNavBarItem(); - await orchestratorPages.notificationContains(/Onboarding user.*completed/); +test.describe("Orchestrator greeting workflow tests", () => { + let uiHelper: UIhelper; + let common: Common; + let orchestrator: Orchestrator; + + test.beforeEach(async ({ page }) => { + uiHelper = new UIhelper(page); + common = new Common(page); + orchestrator = new Orchestrator(page); + await common.loginAsKeycloakUser(); + }); + + test("Greeting workflow execution and workflow tab validation", async () => { + await uiHelper.openSidebar("Orchestrator"); + await orchestrator.selectGreetingWorkflowItem(); + await orchestrator.selectUserOnboardingWorkflowItem(); + await orchestrator.runUserOnboardingWorkflow(); + await uiHelper.openSidebar("Notifications"); + await orchestrator.notificationContains(/Onboarding user.*completed/); + }); }); diff --git a/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowReRun.spec.ts b/e2e-tests/playwright/e2e/plugins/orchestrator/workflowAllRunsValidations.spec.ts similarity index 65% rename from e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowReRun.spec.ts rename to e2e-tests/playwright/e2e/plugins/orchestrator/workflowAllRunsValidations.spec.ts index d25018b367..9280d42f83 100644 --- a/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflowReRun.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/orchestrator/workflowAllRunsValidations.spec.ts @@ -3,21 +3,20 @@ import { UIhelper } from "../../../utils/ui-helper"; import { Common } from "../../../utils/common"; import { Orchestrator } from "../../../support/pages/orchestrator"; -test.describe("Greeting workflow tests", () => { +test.describe("Orchestrator Workflow Runs tests", () => { let uiHelper: UIhelper; let common: Common; let orchestrator: Orchestrator; test.beforeEach(async ({ page }) => { uiHelper = new UIhelper(page); + common = new Common(page); orchestrator = new Orchestrator(page); await common.loginAsKeycloakUser(); }); - test("Orchestrator greeting workflow re-run execution", async () => { + test("Workflow All Runs Validation", async () => { await uiHelper.openSidebar("Orchestrator"); - await orchestrator.selectGreetingWorkflowItem(); - await orchestrator.runGreetingWorkflow(); - await orchestrator.reRunGreetingWorkflow(); + await orchestrator.validateWorkflowAllRuns(); }); }); diff --git a/e2e-tests/playwright/support/pages/notifications.ts b/e2e-tests/playwright/support/pages/notifications.ts index b08948383a..11b565777b 100644 --- a/e2e-tests/playwright/support/pages/notifications.ts +++ b/e2e-tests/playwright/support/pages/notifications.ts @@ -12,6 +12,12 @@ export class NotificationPage { async clickNotificationsNavBarItem() { await this.uiHelper.openSidebar("Notifications"); + await expect( + this.page.getByRole("table").filter({ hasText: "Rows per page" }) + ).toBeVisible(); + await expect( + this.page.getByTestId("loading-indicator").getByRole("img") + ).toHaveCount(0); } async notificationContains(text: string | RegExp) { @@ -119,7 +125,7 @@ export class NotificationPage { async viewRead() { await this.page.getByLabel("View").click(); - if (`${process.env.MILESTONE}` == "5") { + if (`${process.env.MILESTONE}` >= "5") { await this.page .getByRole("option", { name: "Read notifications", exact: true }) .click(); @@ -133,7 +139,7 @@ export class NotificationPage { async viewUnRead() { await this.page.getByLabel("View").click(); - if (`${process.env.MILESTONE}` == "5") { + if (`${process.env.MILESTONE}` >= "5") { await this.page .getByRole("option", { name: "Unread notifications", exact: true }) .click(); diff --git a/e2e-tests/playwright/support/pages/orchestrator.ts b/e2e-tests/playwright/support/pages/orchestrator.ts index 88f8f30f33..2bf4788b46 100644 --- a/e2e-tests/playwright/support/pages/orchestrator.ts +++ b/e2e-tests/playwright/support/pages/orchestrator.ts @@ -47,13 +47,13 @@ export class Orchestrator { await runButton.click(); if (`${process.env.MILESTONE}` == "2") { await this.page.locator("#root_language").click(); - await this.page.getByRole("option", { name: language}).click(); + await this.page.getByRole("option", { name: language }).click(); } else { await this.page.getByLabel("Language").click(); await this.page.getByRole("option", { name: "English" }).click(); } - await this.page.getByRole('button', { name: "Next"}).click(); - await this.page.getByRole('button', { name: "Run"}).click(); + await this.page.getByRole('button', { name: "Next" }).click(); + await this.page.getByRole('button', { name: "Run" }).click(); await expect(this.page.getByText(`${status}`, { exact: true })).toBeVisible( { timeout: 600000, @@ -69,19 +69,19 @@ export class Orchestrator { await this.page.getByText("Run again").click(); } else { await expect( - this.page.getByRole('button', { name: "Rerun"}) + this.page.getByRole('button', { name: "Rerun" }) ).toBeVisible(); - await this.page.getByRole('button', { name: "Rerun"}).click(); + await this.page.getByRole('button', { name: "Rerun" }).click(); } if (`${process.env.MILESTONE}` == "2") { await this.page.locator("#root_language").click(); - await this.page.getByRole("option", { name: language}).click(); + await this.page.getByRole("option", { name: language }).click(); } else { await this.page.getByLabel("Language").click(); await this.page.getByRole("option", { name: "English" }).click(); } - await this.page.getByRole('button', { name: "Next"}).click(); - await this.page.getByRole('button', { name: "Run"}).click(); + await this.page.getByRole('button', { name: "Next" }).click(); + await this.page.getByRole('button', { name: "Run" }).click(); await expect(this.page.getByText(`${status}`, { exact: true })).toBeVisible( { timeout: 600000, @@ -89,6 +89,84 @@ export class Orchestrator { ); } + async validateGreetingWorkflow() { + await this.page.getByRole('tab', { name: 'Workflows' }).click(); + const workflowHeader = this.page.getByRole("heading", { name: "Workflows" }); + await expect(workflowHeader).toBeVisible(); + await expect(workflowHeader).toHaveText("Workflows"); + await expect(Workflows.workflowsTable(this.page)).toBeVisible(); + if (`${process.env.MILESTONE}` >= "6") { + expect( + await this.page.locator(`input[aria-label="Search"]`) + ).toHaveAttribute("placeholder", "Search"); + } else { expect( + await this.page.locator(`input[aria-label="Search"]`) + ).toHaveAttribute("placeholder", "Filter"); + } + await expect( + this.page.getByRole('columnheader', { name: "Name", exact: true}) + ).toBeVisible(); + await expect( + this.page.getByRole('columnheader', { name: "Category", exact: true}) + ).toBeVisible(); + if (`${process.env.MILESTONE}` >= "6") { + await expect( + this.page.getByRole('columnheader', { name: "Workflow status", exact: true}) + ).toBeVisible(); + } + await expect( + this.page.getByRole('columnheader', { name: "Last run", exact: true}) + ).toBeVisible(); + await expect( + this.page.getByRole('columnheader', { name: "Last run status", exact: true}) + ).toBeVisible(); + if (`${process.env.MILESTONE}` == "3") { + await expect(this.page.getByRole('columnheader', { name: "Avg. duration", exact: true})).toBeVisible(); + } + await expect( + this.page.getByRole('columnheader', { name: "Actions", exact: true}) + ).toBeVisible(); + const workFlowRow = this.page.locator( + `tr:has-text("Greeting workflow")` + ); + await expect(workFlowRow.locator("td").nth(0)).toHaveText( + "Greeting workflow" + ); + await expect(workFlowRow.locator("td").nth(1)).toHaveText("Infrastructure"); + if (`${process.env.MILESTONE}` >= "6") { + await expect(workFlowRow.locator("td").nth(2)).toHaveText("Available"); + await expect(workFlowRow.locator("td").nth(3)).toHaveText(/^\d{1,2}\/\d{1,2}\/\d{4}, \d{1,2}:\d{1,2}:\d{1,2} (AM|PM)$/); + await expect(workFlowRow.locator("td").nth(4)).toHaveText("Completed"); + } else { + await expect(workFlowRow.locator("td").nth(3)).toHaveText("Completed"); + } + if (`${process.env.MILESTONE}` == "3") { + await expect(workFlowRow.locator("td").nth(4)).toHaveText("a few seconds"); + await expect(workFlowRow.locator("td").nth(5)).toHaveText( + "YAML based greeting workflow" + ); + } else if (`${process.env.MILESTONE}` >= "6") { + await expect(workFlowRow.locator("td").nth(5)).toHaveText( + "YAML based greeting workflow" + ); + } else { + await expect(workFlowRow.locator("td").nth(4)).toHaveText( + "YAML based greeting workflow" + ); + } + if (`${process.env.MILESTONE}` == "3") { + await expect( + workFlowRow.getByRole("button", { name: "Execute" }) + ).toBeVisible(); + await expect(workFlowRow.getByRole("button", { name: "View" })).toBeVisible(); + } + else { + await expect(workFlowRow.getByRole('button', { name: 'Run', exact: true }).first()).toBeVisible(); + await expect(workFlowRow.getByRole('button', { name: 'View runs' }).first()).toBeVisible(); + if (`${process.env.MILESTONE}` >= "6") await expect(workFlowRow.getByRole('button', { name: 'View input schema' }).first()).toBeVisible(); + } + } + async validateWorkflowRunsDetails() { await expect(this.page.getByText("Details")).toBeVisible(); await expect(this.page.getByText("Results")).toBeVisible(); @@ -113,8 +191,53 @@ export class Orchestrator { } } - async validateWorkflowRuns() { + async validateWorkflowAllRuns() { + if (`${process.env.MILESTONE}` == '3') { + await this.page.getByRole('tab', { name: 'workflow runs' }).click(); + } + else { + await this.page.getByRole('tab', { name: 'all runs' }).click(); + } + let headerName = "All runs" + if (`${process.env.MILESTONE}` == '3') { + headerName = "Workflow Runs" + const workflowRunsHeader = this.page.getByRole("heading", { + name: headerName, + }); + await expect(workflowRunsHeader).toBeVisible(); + await expect(workflowRunsHeader).toHaveText(headerName); + } + await expect( + this.page.locator('tbody').getByRole('row').nth(0).getByRole('cell').nth(0) + ).toBeVisible(); + await expect(this.page.getByTestId("select").first()).toHaveAttribute( + "aria-label", + "Status" + ); + await this.page.getByTestId("select").first().click(); + + const statuses = ["All", "Active", "Error", "Completed", "Aborted", "Suspended"] + for (const status of statuses) { + await expect(this.page.getByRole("option", { name: status })).toHaveText(status); + } + await this.page.getByRole("option", { name: "All" }).click(); + let name = "" + let status = "" + if (`${process.env.MILESTONE}` == '3') { + name = "Name" + } else { + name = "Workflow name" + } + if (`${process.env.MILESTONE}` >= '6') { + status = "Run Status" + } else { + status = "Status" + } + const column_headers = ["ID", name, status, "Category", "Started", "Duration"] + for (const column_header of column_headers) { + await expect(this.page.getByRole('columnheader', { name: column_header, exact: true })).toBeVisible(); + } } async getPageUrl() { diff --git a/e2e-tests/playwright/support/pages/workflows.ts b/e2e-tests/playwright/support/pages/workflows.ts index 2f929d1d83..dfe0bf9952 100644 --- a/e2e-tests/playwright/support/pages/workflows.ts +++ b/e2e-tests/playwright/support/pages/workflows.ts @@ -3,7 +3,7 @@ import { Page } from "@playwright/test"; const workflowsTable = (page: Page) => page .locator("#root div") - .filter({ hasText: "WorkflowsNameCategoryLast" }) + .filter({ hasText: "Workflows" }) .nth(2); const WORKFLOWS = { From 516d22aa06d1a7807fa40682514e8cb17fbd9941 Mon Sep 17 00:00:00 2001 From: Thomas Stetson Date: Wed, 16 Jul 2025 16:54:39 -0400 Subject: [PATCH 03/12] refactor(e2e-tests): update user onboarding workflow tests and streamline notification handling --- .../userOnBoardingWorkflow.spec.ts | 12 +- .../playwright/support/pages/notifications.ts | 20 +- .../playwright/support/pages/orchestrator.ts | 211 +++++------------- 3 files changed, 72 insertions(+), 171 deletions(-) diff --git a/e2e-tests/playwright/e2e/plugins/orchestrator/userOnBoardingWorkflow.spec.ts b/e2e-tests/playwright/e2e/plugins/orchestrator/userOnBoardingWorkflow.spec.ts index c1eb5b2fd0..6616797582 100644 --- a/e2e-tests/playwright/e2e/plugins/orchestrator/userOnBoardingWorkflow.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/orchestrator/userOnBoardingWorkflow.spec.ts @@ -2,25 +2,27 @@ import { test } from "@playwright/test"; import { UIhelper } from "../../../utils/ui-helper"; import { Common } from "../../../utils/common"; import { Orchestrator } from "../../../support/pages/orchestrator"; +import { NotificationPage } from "../../../support/pages/notifications"; -test.describe("Orchestrator greeting workflow tests", () => { +test.describe("Orchestrator user onboarding workflow tests", () => { let uiHelper: UIhelper; let common: Common; let orchestrator: Orchestrator; + let notificationPage: NotificationPage; test.beforeEach(async ({ page }) => { uiHelper = new UIhelper(page); common = new Common(page); orchestrator = new Orchestrator(page); + notificationPage = new NotificationPage(page); await common.loginAsKeycloakUser(); }); - test("Greeting workflow execution and workflow tab validation", async () => { + test("User onboarding workflow execution and notification validation", async () => { await uiHelper.openSidebar("Orchestrator"); - await orchestrator.selectGreetingWorkflowItem(); await orchestrator.selectUserOnboardingWorkflowItem(); await orchestrator.runUserOnboardingWorkflow(); - await uiHelper.openSidebar("Notifications"); - await orchestrator.notificationContains(/Onboarding user.*completed/); + await notificationPage.clickNotificationsNavBarItem(); + await notificationPage.notificationContains(/Onboarding user.*completed/); }); }); diff --git a/e2e-tests/playwright/support/pages/notifications.ts b/e2e-tests/playwright/support/pages/notifications.ts index 11b565777b..d7325ae861 100644 --- a/e2e-tests/playwright/support/pages/notifications.ts +++ b/e2e-tests/playwright/support/pages/notifications.ts @@ -125,13 +125,9 @@ export class NotificationPage { async viewRead() { await this.page.getByLabel("View").click(); - if (`${process.env.MILESTONE}` >= "5") { - await this.page - .getByRole("option", { name: "Read notifications", exact: true }) - .click(); - } else { - await this.page.getByRole("option", { name: "Marked as read" }).click(); - } + await this.page + .getByRole("option", { name: "Read notifications", exact: true }) + .click(); await expect( this.page.getByTestId("loading-indicator").getByRole("img"), ).toHaveCount(0); @@ -139,13 +135,9 @@ export class NotificationPage { async viewUnRead() { await this.page.getByLabel("View").click(); - if (`${process.env.MILESTONE}` >= "5") { - await this.page - .getByRole("option", { name: "Unread notifications", exact: true }) - .click(); - } else { - await this.page.getByRole("option", { name: "New only" }).click(); - } + await this.page + .getByRole("option", { name: "Unread notifications", exact: true }) + .click(); await expect( this.page.getByTestId("loading-indicator").getByRole("img"), ).toHaveCount(0); diff --git a/e2e-tests/playwright/support/pages/orchestrator.ts b/e2e-tests/playwright/support/pages/orchestrator.ts index 2bf4788b46..c61d5ca630 100644 --- a/e2e-tests/playwright/support/pages/orchestrator.ts +++ b/e2e-tests/playwright/support/pages/orchestrator.ts @@ -1,14 +1,11 @@ import { expect, type Page } from "@playwright/test"; -import { UIhelper } from "../../utils/ui-helper"; -import Workflows from "./workflows"; +import Workflows from "./Workflows"; export class Orchestrator { private readonly page: Page; - private readonly uiHelper: UIhelper; constructor(page: Page) { - this.page = page; - this.uiHelper = new UIhelper(page); + this.page = page; } async openWorkflowAlert() { @@ -45,13 +42,8 @@ export class Orchestrator { const runButton = this.page.getByRole('button', { name: "Run" }); await expect(runButton).toBeVisible(); await runButton.click(); - if (`${process.env.MILESTONE}` == "2") { - await this.page.locator("#root_language").click(); - await this.page.getByRole("option", { name: language }).click(); - } else { - await this.page.getByLabel("Language").click(); - await this.page.getByRole("option", { name: "English" }).click(); - } + await this.page.getByLabel("Language").click(); + await this.page.getByRole("option", { name: language }).click(); await this.page.getByRole('button', { name: "Next" }).click(); await this.page.getByRole('button', { name: "Run" }).click(); await expect(this.page.getByText(`${status}`, { exact: true })).toBeVisible( @@ -62,24 +54,12 @@ export class Orchestrator { } async reRunGreetingWorkflow(language = "English", status = "Completed") { - if (`${process.env.MILESTONE}` >= "6") { - await expect( - this.page.getByText("Run again") - ).toBeVisible(); - await this.page.getByText("Run again").click(); - } else { - await expect( - this.page.getByRole('button', { name: "Rerun" }) - ).toBeVisible(); - await this.page.getByRole('button', { name: "Rerun" }).click(); - } - if (`${process.env.MILESTONE}` == "2") { - await this.page.locator("#root_language").click(); - await this.page.getByRole("option", { name: language }).click(); - } else { - await this.page.getByLabel("Language").click(); - await this.page.getByRole("option", { name: "English" }).click(); - } + await expect( + this.page.getByText("Run again") + ).toBeVisible(); + await this.page.getByText("Run again").click(); + await this.page.getByLabel("Language").click(); + await this.page.getByRole("option", { name: language }).click(); await this.page.getByRole('button', { name: "Next" }).click(); await this.page.getByRole('button', { name: "Run" }).click(); await expect(this.page.getByText(`${status}`, { exact: true })).toBeVisible( @@ -95,34 +75,25 @@ export class Orchestrator { await expect(workflowHeader).toBeVisible(); await expect(workflowHeader).toHaveText("Workflows"); await expect(Workflows.workflowsTable(this.page)).toBeVisible(); - if (`${process.env.MILESTONE}` >= "6") { - expect( + expect( await this.page.locator(`input[aria-label="Search"]`) ).toHaveAttribute("placeholder", "Search"); - } else { expect( - await this.page.locator(`input[aria-label="Search"]`) - ).toHaveAttribute("placeholder", "Filter"); - } await expect( this.page.getByRole('columnheader', { name: "Name", exact: true}) ).toBeVisible(); await expect( this.page.getByRole('columnheader', { name: "Category", exact: true}) ).toBeVisible(); - if (`${process.env.MILESTONE}` >= "6") { - await expect( - this.page.getByRole('columnheader', { name: "Workflow status", exact: true}) - ).toBeVisible(); - } + await expect( + this.page.getByRole('columnheader', { name: "Workflow status", exact: true}) + ).toBeVisible(); + await expect( this.page.getByRole('columnheader', { name: "Last run", exact: true}) ).toBeVisible(); await expect( this.page.getByRole('columnheader', { name: "Last run status", exact: true}) ).toBeVisible(); - if (`${process.env.MILESTONE}` == "3") { - await expect(this.page.getByRole('columnheader', { name: "Avg. duration", exact: true})).toBeVisible(); - } await expect( this.page.getByRole('columnheader', { name: "Actions", exact: true}) ).toBeVisible(); @@ -133,80 +104,55 @@ export class Orchestrator { "Greeting workflow" ); await expect(workFlowRow.locator("td").nth(1)).toHaveText("Infrastructure"); - if (`${process.env.MILESTONE}` >= "6") { - await expect(workFlowRow.locator("td").nth(2)).toHaveText("Available"); - await expect(workFlowRow.locator("td").nth(3)).toHaveText(/^\d{1,2}\/\d{1,2}\/\d{4}, \d{1,2}:\d{1,2}:\d{1,2} (AM|PM)$/); - await expect(workFlowRow.locator("td").nth(4)).toHaveText("Completed"); - } else { - await expect(workFlowRow.locator("td").nth(3)).toHaveText("Completed"); - } - if (`${process.env.MILESTONE}` == "3") { - await expect(workFlowRow.locator("td").nth(4)).toHaveText("a few seconds"); - await expect(workFlowRow.locator("td").nth(5)).toHaveText( - "YAML based greeting workflow" - ); - } else if (`${process.env.MILESTONE}` >= "6") { - await expect(workFlowRow.locator("td").nth(5)).toHaveText( - "YAML based greeting workflow" - ); - } else { - await expect(workFlowRow.locator("td").nth(4)).toHaveText( - "YAML based greeting workflow" - ); - } - if (`${process.env.MILESTONE}` == "3") { - await expect( - workFlowRow.getByRole("button", { name: "Execute" }) - ).toBeVisible(); - await expect(workFlowRow.getByRole("button", { name: "View" })).toBeVisible(); - } - else { - await expect(workFlowRow.getByRole('button', { name: 'Run', exact: true }).first()).toBeVisible(); - await expect(workFlowRow.getByRole('button', { name: 'View runs' }).first()).toBeVisible(); - if (`${process.env.MILESTONE}` >= "6") await expect(workFlowRow.getByRole('button', { name: 'View input schema' }).first()).toBeVisible(); + await expect(workFlowRow.locator("td").nth(2)).toHaveText("Available"); + await expect(workFlowRow.locator("td").nth(3)).toHaveText(/^\d{1,2}\/\d{1,2}\/\d{4}, \d{1,2}:\d{1,2}:\d{1,2} (AM|PM)$/); + await expect(workFlowRow.locator("td").nth(4)).toHaveText("Completed"); + await expect(workFlowRow.locator("td").nth(5)).toHaveText( + "YAML based greeting workflow" + ); + await expect(workFlowRow.getByRole('button', { name: 'Run', exact: true }).first()).toBeVisible(); + await expect(workFlowRow.getByRole('button', { name: 'View runs' }).first()).toBeVisible(); + await expect(workFlowRow.getByRole('button', { name: 'View input schema' }).first()).toBeVisible(); + + } + + async runUserOnboardingWorkflow( + userId = `user:default/${process.env.GH_USER_ID}`, + iterationNo = 10, + nameOfUser = process.env.GH_USER_ID, + recipients = [`user:default/${process.env.GH_USER_ID}`], + status = "Completed" + ) { + const runButton = this.page.getByRole('button', { name: "Run" }); + await expect(runButton).toBeVisible(); + await runButton.click(); + await this.page.locator('#root_userId').fill(String(userId)); + await this.page.locator('#root_iterationNum').fill(String(iterationNo)); + await this.page.locator('#root_username').fill(nameOfUser); + await this.page.locator('#root_recipients_0').fill(recipients[0]); + await this.page.getByRole('button', { name: "Next" }).click(); + await this.page.getByRole('button', { name: "Run" }).click(); + await expect(this.page.getByText(`${status}`, { exact: true })).toBeVisible( + { + timeout: 600000, } + ); } async validateWorkflowRunsDetails() { await expect(this.page.getByText("Details")).toBeVisible(); await expect(this.page.getByText("Results")).toBeVisible(); - if (`${process.env.MILESTONE}` == '3') { - await expect(this.page.getByText("Workflow definition")).toBeVisible(); - } await expect(this.page.getByText("Workflow progress")).toBeVisible(); - let workFlowStatusRegex: RegExp - if (`${process.env.MILESTONE}` >= "6") { - workFlowStatusRegex = /^Completed$/ - } else { - workFlowStatusRegex = /^Status Completed$/ - } await expect( this.page .locator("div") - .filter({ hasText: workFlowStatusRegex }) + .filter({ hasText: "Completed" }) .first() ).toBeVisible(); - if (`${process.env.MILESTONE}` == '3') { - await expect(this.page.locator('[data-testid="kogito-iframe"]')).toBeVisible(); - } } async validateWorkflowAllRuns() { - if (`${process.env.MILESTONE}` == '3') { - await this.page.getByRole('tab', { name: 'workflow runs' }).click(); - } - else { - await this.page.getByRole('tab', { name: 'all runs' }).click(); - } - let headerName = "All runs" - if (`${process.env.MILESTONE}` == '3') { - headerName = "Workflow Runs" - const workflowRunsHeader = this.page.getByRole("heading", { - name: headerName, - }); - await expect(workflowRunsHeader).toBeVisible(); - await expect(workflowRunsHeader).toHaveText(headerName); - } + await this.page.getByRole('tab', { name: 'all runs' }).click(); await expect( this.page.locator('tbody').getByRole('row').nth(0).getByRole('cell').nth(0) ).toBeVisible(); @@ -216,25 +162,13 @@ export class Orchestrator { ); await this.page.getByTestId("select").first().click(); - const statuses = ["All", "Active", "Error", "Completed", "Aborted", "Suspended"] + const statuses = ["All", "Running", "Failed", "Completed", "Aborted", "Suspended"] for (const status of statuses) { await expect(this.page.getByRole("option", { name: status })).toHaveText(status); } await this.page.getByRole("option", { name: "All" }).click(); - let name = "" - let status = "" - if (`${process.env.MILESTONE}` == '3') { - name = "Name" - } else { - name = "Workflow name" - } - if (`${process.env.MILESTONE}` >= '6') { - status = "Run Status" - } else { - status = "Status" - } - const column_headers = ["ID", name, status, "Category", "Started", "Duration"] + const column_headers = ["ID", "Workflow name", "Run Status", "Category", "Started", "Duration"] for (const column_header of column_headers) { await expect(this.page.getByRole('columnheader', { name: column_header, exact: true })).toBeVisible(); } @@ -265,42 +199,15 @@ export class Orchestrator { this.page.getByRole("button", { name: "Abort" }), ).toBeEnabled(); await this.page.getByRole("button", { name: "Abort" }).click(); - if (`${process.env.MILESTONE}` == "3") { - await expect( - this.page.getByRole("heading", { name: "Abort workflow", exact: true }), - ).toBeVisible(); - await expect( - this.page - .locator("div") - .filter({ - hasText: - /^Are you sure you want to abort this workflow instance\?$/, - }) - .first(), - ).toBeVisible(); - await this.page.getByRole("button", { name: "Ok" }).click(); - } else { - await expect( - this.page - .getByRole("dialog") - .locator("div") - .filter({ hasText: "Are you sure you want to" }) - .nth(2), - ).toBeVisible(); - await this.page.getByRole("button", { name: "Abort" }).click(); - } + await expect( + this.page + .getByRole("dialog") + .locator("div") + .filter({ hasText: "Are you sure you want to" }) + .nth(2), + ).toBeVisible(); + await this.page.getByRole("button", { name: "Abort" }).click(); await expect(this.page.getByText("Status Aborted")).toBeVisible(); - if (`${process.env.MILESTONE}` == "3") { - await expect( - this.page - .locator("b") - .filter({ - hasText: - /[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/, - }) - .first(), - ).toBeVisible(); - } } async validateErrorPopup() { From 769991d06483bc20fe09ab0ab6b8a9d6646c211a Mon Sep 17 00:00:00 2001 From: Thomas Stetson Date: Thu, 31 Jul 2025 13:30:07 -0400 Subject: [PATCH 04/12] refactor(e2e): remove user onboarding workflow tests and related methods from orchestrator --- .../userOnBoardingWorkflow.spec.ts | 28 --------------- .../playwright/support/pages/orchestrator.ts | 34 ------------------- 2 files changed, 62 deletions(-) delete mode 100644 e2e-tests/playwright/e2e/plugins/orchestrator/userOnBoardingWorkflow.spec.ts diff --git a/e2e-tests/playwright/e2e/plugins/orchestrator/userOnBoardingWorkflow.spec.ts b/e2e-tests/playwright/e2e/plugins/orchestrator/userOnBoardingWorkflow.spec.ts deleted file mode 100644 index 6616797582..0000000000 --- a/e2e-tests/playwright/e2e/plugins/orchestrator/userOnBoardingWorkflow.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { test } from "@playwright/test"; -import { UIhelper } from "../../../utils/ui-helper"; -import { Common } from "../../../utils/common"; -import { Orchestrator } from "../../../support/pages/orchestrator"; -import { NotificationPage } from "../../../support/pages/notifications"; - -test.describe("Orchestrator user onboarding workflow tests", () => { - let uiHelper: UIhelper; - let common: Common; - let orchestrator: Orchestrator; - let notificationPage: NotificationPage; - - test.beforeEach(async ({ page }) => { - uiHelper = new UIhelper(page); - common = new Common(page); - orchestrator = new Orchestrator(page); - notificationPage = new NotificationPage(page); - await common.loginAsKeycloakUser(); - }); - - test("User onboarding workflow execution and notification validation", async () => { - await uiHelper.openSidebar("Orchestrator"); - await orchestrator.selectUserOnboardingWorkflowItem(); - await orchestrator.runUserOnboardingWorkflow(); - await notificationPage.clickNotificationsNavBarItem(); - await notificationPage.notificationContains(/Onboarding user.*completed/); - }); -}); diff --git a/e2e-tests/playwright/support/pages/orchestrator.ts b/e2e-tests/playwright/support/pages/orchestrator.ts index c61d5ca630..62fc6375d6 100644 --- a/e2e-tests/playwright/support/pages/orchestrator.ts +++ b/e2e-tests/playwright/support/pages/orchestrator.ts @@ -17,17 +17,6 @@ export class Orchestrator { async closeWorkflowAlert() { await this.page.getByRole("alert").getByRole("button").nth(2).click(); } - - async selectUserOnboardingWorkflowItem() { - const workflowHeader = this.page.getByRole("heading", { - name: "Workflows", - }); - await expect(workflowHeader).toBeVisible(); - await expect(workflowHeader).toHaveText("Workflows"); - await expect(Workflows.workflowsTable(this.page)).toBeVisible(); - await this.page.getByRole("link", { name: "User Onboarding" }).click(); - } - async selectGreetingWorkflowItem() { const workflowHeader = this.page.getByRole("heading", { name: "Workflows", @@ -116,29 +105,6 @@ export class Orchestrator { } - async runUserOnboardingWorkflow( - userId = `user:default/${process.env.GH_USER_ID}`, - iterationNo = 10, - nameOfUser = process.env.GH_USER_ID, - recipients = [`user:default/${process.env.GH_USER_ID}`], - status = "Completed" - ) { - const runButton = this.page.getByRole('button', { name: "Run" }); - await expect(runButton).toBeVisible(); - await runButton.click(); - await this.page.locator('#root_userId').fill(String(userId)); - await this.page.locator('#root_iterationNum').fill(String(iterationNo)); - await this.page.locator('#root_username').fill(nameOfUser); - await this.page.locator('#root_recipients_0').fill(recipients[0]); - await this.page.getByRole('button', { name: "Next" }).click(); - await this.page.getByRole('button', { name: "Run" }).click(); - await expect(this.page.getByText(`${status}`, { exact: true })).toBeVisible( - { - timeout: 600000, - } - ); - } - async validateWorkflowRunsDetails() { await expect(this.page.getByText("Details")).toBeVisible(); await expect(this.page.getByText("Results")).toBeVisible(); From d5a856e6b033912cae94e73dfbffc5ba4c4c1bee Mon Sep 17 00:00:00 2001 From: Thomas Stetson Date: Thu, 7 Aug 2025 14:16:30 -0400 Subject: [PATCH 05/12] add sleep command for 2-hour delay to facilitate debugging --- .ibm/pipelines/openshift-ci-tests.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.ibm/pipelines/openshift-ci-tests.sh b/.ibm/pipelines/openshift-ci-tests.sh index f30caf70dd..94401b9fa9 100755 --- a/.ibm/pipelines/openshift-ci-tests.sh +++ b/.ibm/pipelines/openshift-ci-tests.sh @@ -136,3 +136,7 @@ main() { } main + +# Sleep for 2 hours to allow for debugging/investigation +echo "Sleeping for 2 hours before script completion..." +sleep 7200 \ No newline at end of file From 9f1a69f2a222f13dd8eeef646cf4988752fc6b1c Mon Sep 17 00:00:00 2001 From: Thomas Stetson Date: Thu, 7 Aug 2025 16:56:22 -0400 Subject: [PATCH 06/12] move sleep into main --- .ibm/pipelines/openshift-ci-tests.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.ibm/pipelines/openshift-ci-tests.sh b/.ibm/pipelines/openshift-ci-tests.sh index 94401b9fa9..aa30c2105f 100755 --- a/.ibm/pipelines/openshift-ci-tests.sh +++ b/.ibm/pipelines/openshift-ci-tests.sh @@ -132,11 +132,12 @@ main() { esac echo "Main script completed with result: ${OVERALL_RESULT}" + + # Sleep for 2 hours to allow for debugging/investigation + echo "Sleeping for 2 hours before script completion..." + sleep 7200 + exit "${OVERALL_RESULT}" } -main - -# Sleep for 2 hours to allow for debugging/investigation -echo "Sleeping for 2 hours before script completion..." -sleep 7200 \ No newline at end of file +main \ No newline at end of file From e28b9b8ec7dd10c9ea09fec94c8accba04066c5b Mon Sep 17 00:00:00 2001 From: Thomas Stetson Date: Tue, 9 Sep 2025 11:07:50 -0400 Subject: [PATCH 07/12] refactor(e2e-tests): linter issues --- ...c.ts => filter-notifications-by-severity.spec.ts} | 0 ...ifications.spec.ts => mark-notifications.spec.ts} | 3 --- ...ingWorkflow.spec.ts => greeting-workflow.spec.ts} | 0 ...spec.ts => workflow-all-runs-validations.spec.ts} | 0 e2e-tests/playwright/support/pages/orchestrator.ts | 12 ++++++------ 5 files changed, 6 insertions(+), 9 deletions(-) rename e2e-tests/playwright/e2e/plugins/notifications/{filterNotificationsBySeverity.spec.ts => filter-notifications-by-severity.spec.ts} (100%) rename e2e-tests/playwright/e2e/plugins/notifications/{markNotifications.spec.ts => mark-notifications.spec.ts} (96%) rename e2e-tests/playwright/e2e/plugins/orchestrator/{greetingWorkflow.spec.ts => greeting-workflow.spec.ts} (100%) rename e2e-tests/playwright/e2e/plugins/orchestrator/{workflowAllRunsValidations.spec.ts => workflow-all-runs-validations.spec.ts} (100%) diff --git a/e2e-tests/playwright/e2e/plugins/notifications/filterNotificationsBySeverity.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts similarity index 100% rename from e2e-tests/playwright/e2e/plugins/notifications/filterNotificationsBySeverity.spec.ts rename to e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts diff --git a/e2e-tests/playwright/e2e/plugins/notifications/markNotifications.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/mark-notifications.spec.ts similarity index 96% rename from e2e-tests/playwright/e2e/plugins/notifications/markNotifications.spec.ts rename to e2e-tests/playwright/e2e/plugins/notifications/mark-notifications.spec.ts index a2f2366356..290605a0ad 100644 --- a/e2e-tests/playwright/e2e/plugins/notifications/markNotifications.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/notifications/mark-notifications.spec.ts @@ -4,20 +4,17 @@ import { Common } from "../../../utils/common"; import RhdhNotificationsApi from "../../../support/api/notifications"; import { Notifications } from "../../../support/api/notifications-api-structures"; import { RhdhAuthApiHack } from "../../../support/api/rhdh-auth-api-hack"; -import { Orchestrator } from "../../../support/pages/orchestrator"; import { NotificationPage } from "../../../support/pages/notifications"; test.describe("Mark notification tests", () => { let uiHelper: UIhelper; let common: Common; - let orchestrator: Orchestrator; let notificationPage: NotificationPage; let apiToken: string; test.beforeEach(async ({ page }) => { uiHelper = new UIhelper(page); common = new Common(page); - orchestrator = new Orchestrator(page); notificationPage = new NotificationPage(page); await common.loginAsKeycloakUser(); apiToken = await RhdhAuthApiHack.getToken(page); diff --git a/e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflow.spec.ts b/e2e-tests/playwright/e2e/plugins/orchestrator/greeting-workflow.spec.ts similarity index 100% rename from e2e-tests/playwright/e2e/plugins/orchestrator/greetingWorkflow.spec.ts rename to e2e-tests/playwright/e2e/plugins/orchestrator/greeting-workflow.spec.ts diff --git a/e2e-tests/playwright/e2e/plugins/orchestrator/workflowAllRunsValidations.spec.ts b/e2e-tests/playwright/e2e/plugins/orchestrator/workflow-all-runs-validations.spec.ts similarity index 100% rename from e2e-tests/playwright/e2e/plugins/orchestrator/workflowAllRunsValidations.spec.ts rename to e2e-tests/playwright/e2e/plugins/orchestrator/workflow-all-runs-validations.spec.ts diff --git a/e2e-tests/playwright/support/pages/orchestrator.ts b/e2e-tests/playwright/support/pages/orchestrator.ts index 62fc6375d6..60addb66e0 100644 --- a/e2e-tests/playwright/support/pages/orchestrator.ts +++ b/e2e-tests/playwright/support/pages/orchestrator.ts @@ -1,5 +1,5 @@ import { expect, type Page } from "@playwright/test"; -import Workflows from "./Workflows"; +import Workflows from "./workflows"; export class Orchestrator { private readonly page: Page; @@ -64,8 +64,8 @@ export class Orchestrator { await expect(workflowHeader).toBeVisible(); await expect(workflowHeader).toHaveText("Workflows"); await expect(Workflows.workflowsTable(this.page)).toBeVisible(); - expect( - await this.page.locator(`input[aria-label="Search"]`) + await expect( + this.page.locator(`input[aria-label="Search"]`) ).toHaveAttribute("placeholder", "Search"); await expect( this.page.getByRole('columnheader', { name: "Name", exact: true}) @@ -134,9 +134,9 @@ export class Orchestrator { } await this.page.getByRole("option", { name: "All" }).click(); - const column_headers = ["ID", "Workflow name", "Run Status", "Category", "Started", "Duration"] - for (const column_header of column_headers) { - await expect(this.page.getByRole('columnheader', { name: column_header, exact: true })).toBeVisible(); + const columnHeaders = ["ID", "Workflow name", "Run Status", "Category", "Started", "Duration"] + for (const columnHeader of columnHeaders) { + await expect(this.page.getByRole('columnheader', { name: columnHeader, exact: true })).toBeVisible(); } } From 87b412c0923cf97e1514c44afe11c4721a7d5f67 Mon Sep 17 00:00:00 2001 From: Thomas Stetson Date: Tue, 9 Sep 2025 11:10:35 -0400 Subject: [PATCH 08/12] refactor(e2e-tests): couple more linter issues --- .../notifications/filter-notifications-by-severity.spec.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts index 6b977978d9..6524341259 100644 --- a/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts @@ -4,22 +4,19 @@ import { Common } from "../../../utils/common"; import RhdhNotficationsApi from "../../../support/api/notifications"; import { Notifications } from "../../../support/api/notifications-api-structures"; import { RhdhAuthApiHack } from "../../../support/api/rhdh-auth-api-hack"; -import { Orchestrator } from "../../../support/pages/orchestrator"; import { NotificationPage } from "../../../support/pages/notifications"; test.describe("Filter critical notification tests", () => { let uiHelper: UIhelper; let common: Common; - let orchestrator: Orchestrator; let notificationPage: NotificationPage; let apiToken: string; - let severities = ["Critical", "High", "Normal", "Low"]; + const severities = ["Critical", "High", "Normal", "Low"]; test.beforeEach(async ({ page }) => { uiHelper = new UIhelper(page); common = new Common(page); - orchestrator = new Orchestrator(page); notificationPage = new NotificationPage(page); await common.loginAsKeycloakUser(); apiToken = await RhdhAuthApiHack.getToken(page); From c5a34ce356409e9e404fac566c17e5cde70d7e8f Mon Sep 17 00:00:00 2001 From: Thomas Stetson Date: Tue, 9 Sep 2025 11:31:04 -0400 Subject: [PATCH 09/12] refactor(e2e-tests): fix formatting issues --- .../filter-notifications-by-severity.spec.ts | 4 +- .../notifications/mark-notifications.spec.ts | 18 +- .../playwright/support/pages/notifications.ts | 6 +- .../playwright/support/pages/orchestrator.ts | 176 +++++++++++------- .../playwright/support/pages/workflows.ts | 5 +- 5 files changed, 120 insertions(+), 89 deletions(-) diff --git a/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts index 6524341259..6ed56e08ab 100644 --- a/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts @@ -20,14 +20,14 @@ test.describe("Filter critical notification tests", () => { notificationPage = new NotificationPage(page); await common.loginAsKeycloakUser(); apiToken = await RhdhAuthApiHack.getToken(page); - console.log(`apiToken -> ${apiToken}`) + console.log(`apiToken -> ${apiToken}`); }); for (const severity of severities) { test(`Filter notifications by severity - ${severity}`, async () => { const r = (Math.random() + 1).toString(36).substring(7); const notificationsApi = await RhdhNotficationsApi.build(apiToken); - const notificationTitle = "UI Notification By Severity" + const notificationTitle = "UI Notification By Severity"; // Used broadcast here, but we should use type: entity and entityRef: ["user:/"] const notification: Notifications = { recipients: { diff --git a/e2e-tests/playwright/e2e/plugins/notifications/mark-notifications.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/mark-notifications.spec.ts index 290605a0ad..3d3ca1ec1a 100644 --- a/e2e-tests/playwright/e2e/plugins/notifications/mark-notifications.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/notifications/mark-notifications.spec.ts @@ -18,13 +18,13 @@ test.describe("Mark notification tests", () => { notificationPage = new NotificationPage(page); await common.loginAsKeycloakUser(); apiToken = await RhdhAuthApiHack.getToken(page); - console.log(`apiToken -> ${apiToken}`) + console.log(`apiToken -> ${apiToken}`); }); test("Mark notification as read", async () => { const r = (Math.random() + 1).toString(36).substring(7); const notificationsApi = await RhdhNotificationsApi.build(apiToken); - const notificationTitle = `UI Notification Mark as read ${r}` + const notificationTitle = `UI Notification Mark as read ${r}`; const notification: Notifications = { recipients: { type: "broadcast", @@ -43,14 +43,14 @@ test.describe("Mark notification tests", () => { await notificationPage.markNotificationAsRead(notificationTitle); await notificationPage.viewRead(); await notificationPage.notificationTextExists( - RegExp(`${notificationTitle}.*(a few seconds ago)|(a minute ago)`) + RegExp(`${notificationTitle}.*(a few seconds ago)|(a minute ago)`), ); }); test("Mark notification as unread", async () => { const r = (Math.random() + 1).toString(36).substring(7); const notificationsApi = await RhdhNotificationsApi.build(apiToken); - const notificationTitle = `UI Notification Mark as unread ${r}` + const notificationTitle = `UI Notification Mark as unread ${r}`; const notification: Notifications = { recipients: { type: "broadcast", @@ -69,19 +69,19 @@ test.describe("Mark notification tests", () => { await notificationPage.markNotificationAsRead(notificationTitle); await notificationPage.viewRead(); await notificationPage.notificationTextExists( - RegExp(`${notificationTitle}.*(a few seconds ago)|(a minute ago)`) + RegExp(`${notificationTitle}.*(a few seconds ago)|(a minute ago)`), ); await notificationPage.markLastNotificationAsUnRead(); await notificationPage.viewUnRead(); await notificationPage.notificationTextExists( - RegExp(`${notificationTitle}.*(a few seconds ago)|(a minute ago)`) + RegExp(`${notificationTitle}.*(a few seconds ago)|(a minute ago)`), ); }); test("Mark notification as saved", async () => { const r = (Math.random() + 1).toString(36).substring(7); const notificationsApi = await RhdhNotificationsApi.build(apiToken); - const notificationTitle = `UI Notification Mark as saved ${r}` + const notificationTitle = `UI Notification Mark as saved ${r}`; const notification: Notifications = { recipients: { type: "broadcast", @@ -100,7 +100,7 @@ test.describe("Mark notification tests", () => { await notificationPage.saveSelected(); await notificationPage.viewSaved(); await notificationPage.notificationTextExists( - RegExp(`${notificationTitle}.*(a few seconds ago)|(a minute ago)`) + RegExp(`${notificationTitle}.*(a few seconds ago)|(a minute ago)`), ); }); -}); \ No newline at end of file +}); diff --git a/e2e-tests/playwright/support/pages/notifications.ts b/e2e-tests/playwright/support/pages/notifications.ts index d7325ae861..3c7578e7df 100644 --- a/e2e-tests/playwright/support/pages/notifications.ts +++ b/e2e-tests/playwright/support/pages/notifications.ts @@ -13,10 +13,10 @@ export class NotificationPage { async clickNotificationsNavBarItem() { await this.uiHelper.openSidebar("Notifications"); await expect( - this.page.getByRole("table").filter({ hasText: "Rows per page" }) + this.page.getByRole("table").filter({ hasText: "Rows per page" }), ).toBeVisible(); await expect( - this.page.getByTestId("loading-indicator").getByRole("img") + this.page.getByTestId("loading-indicator").getByRole("img"), ).toHaveCount(0); } @@ -127,7 +127,7 @@ export class NotificationPage { await this.page.getByLabel("View").click(); await this.page .getByRole("option", { name: "Read notifications", exact: true }) - .click(); + .click(); await expect( this.page.getByTestId("loading-indicator").getByRole("img"), ).toHaveCount(0); diff --git a/e2e-tests/playwright/support/pages/orchestrator.ts b/e2e-tests/playwright/support/pages/orchestrator.ts index 60addb66e0..93b695d8e2 100644 --- a/e2e-tests/playwright/support/pages/orchestrator.ts +++ b/e2e-tests/playwright/support/pages/orchestrator.ts @@ -5,7 +5,7 @@ export class Orchestrator { private readonly page: Page; constructor(page: Page) { - this.page = page; + this.page = page; } async openWorkflowAlert() { @@ -28,81 +28,92 @@ export class Orchestrator { } async runGreetingWorkflow(language = "English", status = "Completed") { - const runButton = this.page.getByRole('button', { name: "Run" }); + const runButton = this.page.getByRole("button", { name: "Run" }); await expect(runButton).toBeVisible(); await runButton.click(); await this.page.getByLabel("Language").click(); - await this.page.getByRole("option", { name: language }).click(); - await this.page.getByRole('button', { name: "Next" }).click(); - await this.page.getByRole('button', { name: "Run" }).click(); + await this.page.getByRole("option", { name: language }).click(); + await this.page.getByRole("button", { name: "Next" }).click(); + await this.page.getByRole("button", { name: "Run" }).click(); await expect(this.page.getByText(`${status}`, { exact: true })).toBeVisible( { timeout: 600000, - } + }, ); } async reRunGreetingWorkflow(language = "English", status = "Completed") { - await expect( - this.page.getByText("Run again") - ).toBeVisible(); + await expect(this.page.getByText("Run again")).toBeVisible(); await this.page.getByText("Run again").click(); await this.page.getByLabel("Language").click(); - await this.page.getByRole("option", { name: language }).click(); - await this.page.getByRole('button', { name: "Next" }).click(); - await this.page.getByRole('button', { name: "Run" }).click(); + await this.page.getByRole("option", { name: language }).click(); + await this.page.getByRole("button", { name: "Next" }).click(); + await this.page.getByRole("button", { name: "Run" }).click(); await expect(this.page.getByText(`${status}`, { exact: true })).toBeVisible( { timeout: 600000, - } + }, ); } async validateGreetingWorkflow() { - await this.page.getByRole('tab', { name: 'Workflows' }).click(); - const workflowHeader = this.page.getByRole("heading", { name: "Workflows" }); - await expect(workflowHeader).toBeVisible(); - await expect(workflowHeader).toHaveText("Workflows"); - await expect(Workflows.workflowsTable(this.page)).toBeVisible(); - await expect( - this.page.locator(`input[aria-label="Search"]`) - ).toHaveAttribute("placeholder", "Search"); - await expect( - this.page.getByRole('columnheader', { name: "Name", exact: true}) - ).toBeVisible(); - await expect( - this.page.getByRole('columnheader', { name: "Category", exact: true}) - ).toBeVisible(); - await expect( - this.page.getByRole('columnheader', { name: "Workflow status", exact: true}) - ).toBeVisible(); - - await expect( - this.page.getByRole('columnheader', { name: "Last run", exact: true}) - ).toBeVisible(); - await expect( - this.page.getByRole('columnheader', { name: "Last run status", exact: true}) - ).toBeVisible(); - await expect( - this.page.getByRole('columnheader', { name: "Actions", exact: true}) - ).toBeVisible(); - const workFlowRow = this.page.locator( - `tr:has-text("Greeting workflow")` - ); - await expect(workFlowRow.locator("td").nth(0)).toHaveText( - "Greeting workflow" - ); - await expect(workFlowRow.locator("td").nth(1)).toHaveText("Infrastructure"); - await expect(workFlowRow.locator("td").nth(2)).toHaveText("Available"); - await expect(workFlowRow.locator("td").nth(3)).toHaveText(/^\d{1,2}\/\d{1,2}\/\d{4}, \d{1,2}:\d{1,2}:\d{1,2} (AM|PM)$/); - await expect(workFlowRow.locator("td").nth(4)).toHaveText("Completed"); - await expect(workFlowRow.locator("td").nth(5)).toHaveText( - "YAML based greeting workflow" - ); - await expect(workFlowRow.getByRole('button', { name: 'Run', exact: true }).first()).toBeVisible(); - await expect(workFlowRow.getByRole('button', { name: 'View runs' }).first()).toBeVisible(); - await expect(workFlowRow.getByRole('button', { name: 'View input schema' }).first()).toBeVisible(); - + await this.page.getByRole("tab", { name: "Workflows" }).click(); + const workflowHeader = this.page.getByRole("heading", { + name: "Workflows", + }); + await expect(workflowHeader).toBeVisible(); + await expect(workflowHeader).toHaveText("Workflows"); + await expect(Workflows.workflowsTable(this.page)).toBeVisible(); + await expect( + this.page.locator(`input[aria-label="Search"]`), + ).toHaveAttribute("placeholder", "Search"); + await expect( + this.page.getByRole("columnheader", { name: "Name", exact: true }), + ).toBeVisible(); + await expect( + this.page.getByRole("columnheader", { name: "Category", exact: true }), + ).toBeVisible(); + await expect( + this.page.getByRole("columnheader", { + name: "Workflow status", + exact: true, + }), + ).toBeVisible(); + + await expect( + this.page.getByRole("columnheader", { name: "Last run", exact: true }), + ).toBeVisible(); + await expect( + this.page.getByRole("columnheader", { + name: "Last run status", + exact: true, + }), + ).toBeVisible(); + await expect( + this.page.getByRole("columnheader", { name: "Actions", exact: true }), + ).toBeVisible(); + const workFlowRow = this.page.locator(`tr:has-text("Greeting workflow")`); + await expect(workFlowRow.locator("td").nth(0)).toHaveText( + "Greeting workflow", + ); + await expect(workFlowRow.locator("td").nth(1)).toHaveText("Infrastructure"); + await expect(workFlowRow.locator("td").nth(2)).toHaveText("Available"); + await expect(workFlowRow.locator("td").nth(3)).toHaveText( + /^\d{1,2}\/\d{1,2}\/\d{4}, \d{1,2}:\d{1,2}:\d{1,2} (AM|PM)$/, + ); + await expect(workFlowRow.locator("td").nth(4)).toHaveText("Completed"); + await expect(workFlowRow.locator("td").nth(5)).toHaveText( + "YAML based greeting workflow", + ); + await expect( + workFlowRow.getByRole("button", { name: "Run", exact: true }).first(), + ).toBeVisible(); + await expect( + workFlowRow.getByRole("button", { name: "View runs" }).first(), + ).toBeVisible(); + await expect( + workFlowRow.getByRole("button", { name: "View input schema" }).first(), + ).toBeVisible(); } async validateWorkflowRunsDetails() { @@ -110,33 +121,56 @@ export class Orchestrator { await expect(this.page.getByText("Results")).toBeVisible(); await expect(this.page.getByText("Workflow progress")).toBeVisible(); await expect( - this.page - .locator("div") - .filter({ hasText: "Completed" }) - .first() + this.page.locator("div").filter({ hasText: "Completed" }).first(), ).toBeVisible(); } async validateWorkflowAllRuns() { - await this.page.getByRole('tab', { name: 'all runs' }).click(); + await this.page.getByRole("tab", { name: "all runs" }).click(); await expect( - this.page.locator('tbody').getByRole('row').nth(0).getByRole('cell').nth(0) + this.page + .locator("tbody") + .getByRole("row") + .nth(0) + .getByRole("cell") + .nth(0), ).toBeVisible(); await expect(this.page.getByTestId("select").first()).toHaveAttribute( "aria-label", - "Status" + "Status", ); await this.page.getByTestId("select").first().click(); - - const statuses = ["All", "Running", "Failed", "Completed", "Aborted", "Suspended"] + + const statuses = [ + "All", + "Running", + "Failed", + "Completed", + "Aborted", + "Suspended", + ]; for (const status of statuses) { - await expect(this.page.getByRole("option", { name: status })).toHaveText(status); + await expect(this.page.getByRole("option", { name: status })).toHaveText( + status, + ); } await this.page.getByRole("option", { name: "All" }).click(); - const columnHeaders = ["ID", "Workflow name", "Run Status", "Category", "Started", "Duration"] - for (const columnHeader of columnHeaders) { - await expect(this.page.getByRole('columnheader', { name: columnHeader, exact: true })).toBeVisible(); + const columnHeaders = [ + "ID", + "Workflow name", + "Run Status", + "Category", + "Started", + "Duration", + ]; + for (const columnHeader of columnHeaders) { + await expect( + this.page.getByRole("columnheader", { + name: columnHeader, + exact: true, + }), + ).toBeVisible(); } } @@ -172,7 +206,7 @@ export class Orchestrator { .filter({ hasText: "Are you sure you want to" }) .nth(2), ).toBeVisible(); - await this.page.getByRole("button", { name: "Abort" }).click(); + await this.page.getByRole("button", { name: "Abort" }).click(); await expect(this.page.getByText("Status Aborted")).toBeVisible(); } diff --git a/e2e-tests/playwright/support/pages/workflows.ts b/e2e-tests/playwright/support/pages/workflows.ts index dfe0bf9952..33e0ba12e7 100644 --- a/e2e-tests/playwright/support/pages/workflows.ts +++ b/e2e-tests/playwright/support/pages/workflows.ts @@ -1,10 +1,7 @@ import { Page } from "@playwright/test"; const workflowsTable = (page: Page) => - page - .locator("#root div") - .filter({ hasText: "Workflows" }) - .nth(2); + page.locator("#root div").filter({ hasText: "Workflows" }).nth(2); const WORKFLOWS = { workflowsTable, From c3ce85391384037d7f80ec52e5d473642be0d432 Mon Sep 17 00:00:00 2001 From: Thomas Stetson Date: Tue, 9 Sep 2025 14:26:49 -0400 Subject: [PATCH 10/12] remove sleep --- .ibm/pipelines/openshift-ci-tests.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.ibm/pipelines/openshift-ci-tests.sh b/.ibm/pipelines/openshift-ci-tests.sh index aa30c2105f..9df0637e34 100755 --- a/.ibm/pipelines/openshift-ci-tests.sh +++ b/.ibm/pipelines/openshift-ci-tests.sh @@ -132,11 +132,6 @@ main() { esac echo "Main script completed with result: ${OVERALL_RESULT}" - - # Sleep for 2 hours to allow for debugging/investigation - echo "Sleeping for 2 hours before script completion..." - sleep 7200 - exit "${OVERALL_RESULT}" } From bc263c405cda83960443630a8916d372623f4e9a Mon Sep 17 00:00:00 2001 From: Thomas Stetson Date: Tue, 9 Sep 2025 14:54:45 -0400 Subject: [PATCH 11/12] replace API token retrieval with test-token --- .../notifications/filter-notifications-by-severity.spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts index 6ed56e08ab..50ae3704fd 100644 --- a/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts @@ -3,7 +3,6 @@ import { UIhelper } from "../../../utils/ui-helper"; import { Common } from "../../../utils/common"; import RhdhNotficationsApi from "../../../support/api/notifications"; import { Notifications } from "../../../support/api/notifications-api-structures"; -import { RhdhAuthApiHack } from "../../../support/api/rhdh-auth-api-hack"; import { NotificationPage } from "../../../support/pages/notifications"; test.describe("Filter critical notification tests", () => { @@ -19,7 +18,7 @@ test.describe("Filter critical notification tests", () => { common = new Common(page); notificationPage = new NotificationPage(page); await common.loginAsKeycloakUser(); - apiToken = await RhdhAuthApiHack.getToken(page); + apiToken = "test-token" console.log(`apiToken -> ${apiToken}`); }); From c7a012996343768a36783ce6c99f7372f253bf2a Mon Sep 17 00:00:00 2001 From: Thomas Stetson Date: Wed, 10 Sep 2025 11:18:38 -0400 Subject: [PATCH 12/12] refactor(e2e-tests): streamline notification tests, attempt to fix issue with mark notifications tests --- .../filter-notifications-by-severity.spec.ts | 4 +- .../notifications/mark-notifications.spec.ts | 58 +++++++++---------- .../playwright/support/pages/notifications.ts | 4 -- 3 files changed, 29 insertions(+), 37 deletions(-) diff --git a/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts index 50ae3704fd..cebb2bd616 100644 --- a/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/notifications/filter-notifications-by-severity.spec.ts @@ -18,8 +18,7 @@ test.describe("Filter critical notification tests", () => { common = new Common(page); notificationPage = new NotificationPage(page); await common.loginAsKeycloakUser(); - apiToken = "test-token" - console.log(`apiToken -> ${apiToken}`); + apiToken = "test-token"; }); for (const severity of severities) { @@ -27,7 +26,6 @@ test.describe("Filter critical notification tests", () => { const r = (Math.random() + 1).toString(36).substring(7); const notificationsApi = await RhdhNotficationsApi.build(apiToken); const notificationTitle = "UI Notification By Severity"; - // Used broadcast here, but we should use type: entity and entityRef: ["user:/"] const notification: Notifications = { recipients: { type: "broadcast", diff --git a/e2e-tests/playwright/e2e/plugins/notifications/mark-notifications.spec.ts b/e2e-tests/playwright/e2e/plugins/notifications/mark-notifications.spec.ts index 3d3ca1ec1a..7d4cee189a 100644 --- a/e2e-tests/playwright/e2e/plugins/notifications/mark-notifications.spec.ts +++ b/e2e-tests/playwright/e2e/plugins/notifications/mark-notifications.spec.ts @@ -3,7 +3,6 @@ import { UIhelper } from "../../../utils/ui-helper"; import { Common } from "../../../utils/common"; import RhdhNotificationsApi from "../../../support/api/notifications"; import { Notifications } from "../../../support/api/notifications-api-structures"; -import { RhdhAuthApiHack } from "../../../support/api/rhdh-auth-api-hack"; import { NotificationPage } from "../../../support/pages/notifications"; test.describe("Mark notification tests", () => { @@ -17,81 +16,80 @@ test.describe("Mark notification tests", () => { common = new Common(page); notificationPage = new NotificationPage(page); await common.loginAsKeycloakUser(); - apiToken = await RhdhAuthApiHack.getToken(page); - console.log(`apiToken -> ${apiToken}`); + apiToken = "test-token"; }); test("Mark notification as read", async () => { const r = (Math.random() + 1).toString(36).substring(7); const notificationsApi = await RhdhNotificationsApi.build(apiToken); - const notificationTitle = `UI Notification Mark as read ${r}`; + const notificationTitle = `UI Notification Mark as read`; const notification: Notifications = { recipients: { type: "broadcast", entityRef: [""], }, payload: { - title: notificationTitle, - description: `Test ${notificationTitle}`, - severity: "normal", - topic: `Testing ${notificationTitle}`, + title: `${notificationTitle}-${r}`, + description: `Test ${notificationTitle}-${r}`, + severity: "Normal", + topic: `Testing ${notificationTitle}-${r}`, }, }; await notificationsApi.createNotification(notification); await uiHelper.openSidebar("Notifications"); - await notificationPage.notificationTextExists(notificationTitle); - await notificationPage.markNotificationAsRead(notificationTitle); + await notificationPage.notificationContains(`${notificationTitle}-${r}`); + await notificationPage.markNotificationAsRead(`${notificationTitle}-${r}`); await notificationPage.viewRead(); - await notificationPage.notificationTextExists( - RegExp(`${notificationTitle}.*(a few seconds ago)|(a minute ago)`), + await notificationPage.notificationContains( + RegExp(`${notificationTitle}-${r}.*(a few seconds ago)|(a minute ago)`), ); }); test("Mark notification as unread", async () => { const r = (Math.random() + 1).toString(36).substring(7); const notificationsApi = await RhdhNotificationsApi.build(apiToken); - const notificationTitle = `UI Notification Mark as unread ${r}`; + const notificationTitle = `UI Notification Mark as unread`; const notification: Notifications = { recipients: { type: "broadcast", entityRef: [""], }, payload: { - title: notificationTitle, - description: `Test ${notificationTitle}`, - severity: "normal", - topic: `Testing ${notificationTitle}`, + title: `${notificationTitle}-${r}`, + description: `Test ${notificationTitle}-${r}`, + severity: "Normal", + topic: `Testing ${notificationTitle}-${r}`, }, }; await notificationsApi.createNotification(notification); await uiHelper.openSidebar("Notifications"); - await notificationPage.notificationTextExists(notificationTitle); - await notificationPage.markNotificationAsRead(notificationTitle); + await notificationPage.notificationContains(`${notificationTitle}-${r}`); + await notificationPage.markNotificationAsRead(`${notificationTitle}-${r}`); await notificationPage.viewRead(); - await notificationPage.notificationTextExists( - RegExp(`${notificationTitle}.*(a few seconds ago)|(a minute ago)`), + await notificationPage.notificationContains( + RegExp(`${notificationTitle}-${r}.*(a few seconds ago)|(a minute ago)`), ); await notificationPage.markLastNotificationAsUnRead(); await notificationPage.viewUnRead(); - await notificationPage.notificationTextExists( - RegExp(`${notificationTitle}.*(a few seconds ago)|(a minute ago)`), + await notificationPage.notificationContains( + RegExp(`${notificationTitle}-${r}.*(a few seconds ago)|(a minute ago)`), ); }); test("Mark notification as saved", async () => { const r = (Math.random() + 1).toString(36).substring(7); const notificationsApi = await RhdhNotificationsApi.build(apiToken); - const notificationTitle = `UI Notification Mark as saved ${r}`; + const notificationTitle = `UI Notification Mark as saved`; const notification: Notifications = { recipients: { type: "broadcast", entityRef: [""], }, payload: { - title: notificationTitle, - description: `Test ${notificationTitle}`, - severity: "normal", - topic: `Testing ${notificationTitle}`, + title: `${notificationTitle}-${r}`, + description: `Test ${notificationTitle}-${r}`, + severity: "Normal", + topic: `Testing ${notificationTitle}-${r}`, }, }; await notificationsApi.createNotification(notification); @@ -99,8 +97,8 @@ test.describe("Mark notification tests", () => { await notificationPage.selectNotification(); await notificationPage.saveSelected(); await notificationPage.viewSaved(); - await notificationPage.notificationTextExists( - RegExp(`${notificationTitle}.*(a few seconds ago)|(a minute ago)`), + await notificationPage.notificationContains( + RegExp(`${notificationTitle}-${r}.*(a few seconds ago)|(a minute ago)`), ); }); }); diff --git a/e2e-tests/playwright/support/pages/notifications.ts b/e2e-tests/playwright/support/pages/notifications.ts index 3c7578e7df..35a299f2e8 100644 --- a/e2e-tests/playwright/support/pages/notifications.ts +++ b/e2e-tests/playwright/support/pages/notifications.ts @@ -31,10 +31,6 @@ export class NotificationPage { await expect(row).toHaveCount(1); } - async notificationTextExists(text: string | RegExp) { - await expect(this.page.getByRole("cell", { name: text })).toHaveCount(1); - } - async clickNotificationHeadingLink(text: string | RegExp) { await this.page .getByRole("cell", { name: text, exact: true })