From 91e7ce272ad064faad5b152b56b6ceb63f961cd6 Mon Sep 17 00:00:00 2001 From: Ahmed Awan Date: Tue, 27 Aug 2024 19:38:20 -0500 Subject: [PATCH] fix/adjust jests for errors shown in `WorkflowInvocationOverview` --- .../WorkflowInvocationOverview.test.js | 73 +++++++++++++++++++ .../WorkflowInvocationOverview.vue | 1 + .../WorkflowInvocationState.test.ts | 61 +++++----------- client/src/composables/useWorkflowInstance.ts | 1 - 4 files changed, 94 insertions(+), 42 deletions(-) diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationOverview.test.js b/client/src/components/WorkflowInvocationState/WorkflowInvocationOverview.test.js index f4e069fb3cc7..ab154ae17813 100644 --- a/client/src/components/WorkflowInvocationState/WorkflowInvocationOverview.test.js +++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationOverview.test.js @@ -1,5 +1,6 @@ import { createTestingPinia } from "@pinia/testing"; import { shallowMount } from "@vue/test-utils"; +import flushPromises from "flush-promises"; import { getLocalVue } from "tests/jest/helpers"; import invocationData from "../Workflow/test/json/invocation.json"; @@ -7,6 +8,42 @@ import WorkflowInvocationOverview from "./WorkflowInvocationOverview"; const localVue = getLocalVue(); +// Constants +const workflowData = { + id: "workflow-id", + name: "Test Workflow", + version: 0, +}; +const selectors = { + bAlertStub: "balert-stub", +}; +const alertMessages = { + unOwned: "Workflow is neither importable, nor owned by or shared with current user", + nonExistent: "No workflow found for this invocation.", +}; + +// Mock the workflow store to return the expected workflow data given the stored workflow ID +jest.mock("@/stores/workflowStore", () => { + const originalModule = jest.requireActual("@/stores/workflowStore"); + return { + ...originalModule, + useWorkflowStore: () => ({ + ...originalModule.useWorkflowStore(), + getStoredWorkflowByInstanceId: jest.fn().mockImplementation((workflowId) => { + if (["unowned-workflow", "nonexistant-workflow"].includes(workflowId)) { + return undefined; + } + return workflowData; + }), + fetchWorkflowForInstanceId: jest.fn().mockImplementation((workflowId) => { + if (workflowId === "unowned-workflow") { + throw new Error(alertMessages.unOwned); + } + }), + }), + }; +}); + describe("WorkflowInvocationOverview.vue with terminal invocation", () => { let wrapper; let propsData; @@ -61,3 +98,39 @@ describe("WorkflowInvocationOverview.vue with invocation scheduling running", () expect(wrapper.find(".cancel-workflow-scheduling").exists()).toBeTruthy(); }); }); + +describe("WorkflowInvocationOverview.vue for a valid/invalid workflow", () => { + async function loadWrapper(invocationData) { + const propsData = { + invocation: invocationData, + invocationAndJobTerminal: true, + invocationSchedulingTerminal: true, + jobStatesSummary: {}, + }; + const wrapper = shallowMount(WorkflowInvocationOverview, { + propsData, + localVue, + }); + await flushPromises(); + return wrapper; + } + + it("displays the workflow invocation graph for a valid workflow", async () => { + const wrapper = await loadWrapper(invocationData); + expect(wrapper.find("[data-description='workflow invocation graph']").exists()).toBeTruthy(); + }); + + it("displays an alert for an unowned workflow", async () => { + const wrapper = await loadWrapper({ ...invocationData, workflow_id: "unowned-workflow" }); + expect(wrapper.find("[data-description='workflow invocation graph']").exists()).toBeFalsy(); + const alert = wrapper.find(selectors.bAlertStub); + expect(alert.text()).toContain(alertMessages.unOwned); + }); + + it("displays an alert for a nonexistant workflow", async () => { + const wrapper = await loadWrapper({ ...invocationData, workflow_id: "nonexistant-workflow" }); + expect(wrapper.find("[data-description='workflow invocation graph']").exists()).toBeFalsy(); + const alert = wrapper.find(selectors.bAlertStub); + expect(alert.text()).toContain(alertMessages.nonExistent); + }); +}); diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationOverview.vue b/client/src/components/WorkflowInvocationState/WorkflowInvocationOverview.vue index b4145df6cc73..bf04dd5ce80c 100644 --- a/client/src/components/WorkflowInvocationState/WorkflowInvocationOverview.vue +++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationOverview.vue @@ -240,6 +240,7 @@ function onCancel() {
{ }; }); -// Sample workflow -const workflowData = { - id: "workflow-id", - name: "Test Workflow", - version: 0, -}; - -// Mock the workflow store to return the expected workflow data given the stored workflow ID +// Mock the workflow store to return a workflow for `getStoredWorkflowByInstanceId` jest.mock("@/stores/workflowStore", () => { const originalModule = jest.requireActual("@/stores/workflowStore"); return { ...originalModule, useWorkflowStore: () => ({ ...originalModule.useWorkflowStore(), - getStoredWorkflowByInstanceId: jest.fn().mockImplementation(() => workflowData), - fetchWorkflowForInstanceId: jest.fn(), + getStoredWorkflowByInstanceId: jest.fn().mockImplementation(() => { + return { + id: "workflow-id", + name: "Test Workflow", + version: 0, + }; + }), }), }; }); @@ -105,30 +103,18 @@ jest.mock("@/stores/workflowStore", () => { * @param fullPage Whether to render the header as well or just the invocation state tabs * @returns The mounted wrapper */ -async function mountWorkflowInvocationState(invocationId: string, shallow = true, isFullPage = false) { +async function mountWorkflowInvocationState(invocationId: string, isFullPage = false) { const pinia = createTestingPinia(); setActivePinia(pinia); - let wrapper; - if (shallow) { - wrapper = shallowMount(WorkflowInvocationState as object, { - propsData: { - invocationId, - isFullPage, - }, - pinia, - localVue, - }); - } else { - wrapper = mount(WorkflowInvocationState as object, { - propsData: { - invocationId, - isFullPage, - }, - pinia, - localVue, - }); - } + const wrapper = shallowMount(WorkflowInvocationState as object, { + propsData: { + invocationId, + isFullPage, + }, + pinia, + localVue, + }); await flushPromises(); return wrapper; } @@ -193,22 +179,15 @@ describe("WorkflowInvocationState check invocation and job terminal states", () describe("WorkflowInvocationState check 'Report' tab disabled state and header", () => { it("determines that 'Report' tab is disabled for non-terminal invocation", async () => { - const wrapper = await mountWorkflowInvocationState("non-terminal-id", true); + const wrapper = await mountWorkflowInvocationState("non-terminal-id"); const reportTab = wrapper.find(selectors.invocationReportTab); expect(reportTab.attributes("disabled")).toBe("true"); }); it("determines that 'Report' tab is not disabled for terminal invocation", async () => { - const wrapper = await mountWorkflowInvocationState(invocationData.id, true); + const wrapper = await mountWorkflowInvocationState(invocationData.id); const reportTab = wrapper.find(selectors.invocationReportTab); expect(reportTab.attributes("disabled")).toBeUndefined(); }); - it("determines that a header is displayed for the invocation", async () => { - const wrapper = await mountWorkflowInvocationState(invocationData.id, true, true); - const heading = wrapper.find(selectors.fullPageHeading); - expect(heading.exists()).toBe(true); - expect(heading.text()).toContain('Invoked Workflow: "Test Workflow"'); - expect(wrapper.html()).toContain(`Workflow Version: ${workflowData.version + 1}`); - }); }); /** diff --git a/client/src/composables/useWorkflowInstance.ts b/client/src/composables/useWorkflowInstance.ts index 43a9d796a701..53bb6d287dee 100644 --- a/client/src/composables/useWorkflowInstance.ts +++ b/client/src/composables/useWorkflowInstance.ts @@ -16,7 +16,6 @@ export function useWorkflowInstance(workflowId: string) { await workflowStore.fetchWorkflowForInstanceId(workflowId); } catch (e) { error.value = errorMessageAsString(e); - console.error("unable to fetch workflow \n", error.value); } finally { loading.value = false; }