-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #18930 from ahmedhamidawan/add_wf_import_btn_to_in…
…vocation_view [24.1] Replace `Edit` button with `Import` button on `WorkflowInvocationHeader`
- Loading branch information
Showing
3 changed files
with
217 additions
and
2 deletions.
There are no files selected for viewing
160 changes: 160 additions & 0 deletions
160
client/src/components/WorkflowInvocationState/WorkflowInvocationHeader.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import { createTestingPinia } from "@pinia/testing"; | ||
import { shallowMount } from "@vue/test-utils"; | ||
import flushPromises from "flush-promises"; | ||
import { getLocalVue } from "tests/jest/helpers"; | ||
|
||
import sampleInvocation from "@/components/Workflow/test/json/invocation.json"; | ||
import { useUserStore } from "@/stores/userStore"; | ||
|
||
import WorkflowInvocationHeader from "./WorkflowInvocationHeader.vue"; | ||
|
||
// Constants | ||
const WORKFLOW_OWNER = "test-user"; | ||
const OTHER_USER = "other-user"; | ||
const UNIMPORTABLE_WORKFLOW_ID = "invalid-workflow-id"; | ||
const UNIMPORTABLE_WORKFLOW_INSTANCE_ID = "invalid-instance-id"; | ||
const SAMPLE_WORKFLOW = { | ||
id: "workflow-id", | ||
name: "workflow-name", | ||
owner: WORKFLOW_OWNER, | ||
version: 1, | ||
}; | ||
const IMPORT_ERROR_MESSAGE = "Failed to import workflow"; | ||
|
||
const SELECTORS = { | ||
INVOKED_WORKFLOW_HEADING: "anonymous-stub[h1='true']", | ||
RETURN_TO_INVOCATIONS_LIST_BUTTON: "bbutton-stub[title='Return to Invocations List']", | ||
ACTIONS_BUTTON_GROUP: "bbuttongroup-stub", | ||
EDIT_WORKFLOW_BUTTON: `bbutton-stub[title='<b>Edit</b><br>${SAMPLE_WORKFLOW.name}']`, | ||
IMPORT_WORKFLOW_BUTTON: "anonymous-stub[title='Import this workflow']", | ||
RUN_WORKFLOW_BUTTON: `anonymous-stub[id='${SAMPLE_WORKFLOW.id}']`, | ||
ALERT_MESSAGE: "balert-stub", | ||
}; | ||
|
||
// Mock the copyWorkflow function for importing a workflow | ||
jest.mock("components/Workflow/workflows.services", () => ({ | ||
copyWorkflow: jest.fn().mockImplementation((workflowId: string) => { | ||
if (workflowId === UNIMPORTABLE_WORKFLOW_ID) { | ||
throw new Error(IMPORT_ERROR_MESSAGE); | ||
} | ||
return SAMPLE_WORKFLOW; | ||
}), | ||
})); | ||
|
||
// Mock the workflow store to return the sample workflow | ||
jest.mock("@/stores/workflowStore", () => { | ||
const originalModule = jest.requireActual("@/stores/workflowStore"); | ||
return { | ||
...originalModule, | ||
useWorkflowStore: () => ({ | ||
...originalModule.useWorkflowStore(), | ||
getStoredWorkflowByInstanceId: jest.fn().mockImplementation((instanceId: string) => { | ||
if (instanceId === UNIMPORTABLE_WORKFLOW_INSTANCE_ID) { | ||
return { ...SAMPLE_WORKFLOW, id: UNIMPORTABLE_WORKFLOW_ID }; | ||
} | ||
return SAMPLE_WORKFLOW; | ||
}), | ||
}), | ||
}; | ||
}); | ||
|
||
const localVue = getLocalVue(); | ||
|
||
/** | ||
* Mounts the WorkflowInvocationHeader component with props/stores adjusted given the parameters | ||
* @param ownsWorkflow Whether the user owns the workflow associated with the invocation | ||
* @param hasReturnBtn Whether the component should have a return to invocations list button | ||
* @param unimportableWorkflow Whether the workflow import should fail | ||
* @returns The wrapper object | ||
*/ | ||
async function mountWorkflowInvocationHeader(ownsWorkflow = true, hasReturnBtn = false, unimportableWorkflow = false) { | ||
const wrapper = shallowMount(WorkflowInvocationHeader as object, { | ||
propsData: { | ||
invocation: { | ||
...sampleInvocation, | ||
workflow_id: !unimportableWorkflow ? sampleInvocation.workflow_id : UNIMPORTABLE_WORKFLOW_INSTANCE_ID, | ||
}, | ||
fromPanel: !hasReturnBtn, | ||
}, | ||
localVue, | ||
pinia: createTestingPinia(), | ||
}); | ||
|
||
const userStore = useUserStore(); | ||
userStore.currentUser = { | ||
id: "1", | ||
email: "[email protected]", | ||
tags_used: [], | ||
isAnonymous: false, | ||
total_disk_usage: 0, | ||
username: ownsWorkflow ? WORKFLOW_OWNER : OTHER_USER, | ||
}; | ||
|
||
return { wrapper }; | ||
} | ||
|
||
describe("WorkflowInvocationHeader renders", () => { | ||
// Included both cases in one test because these are always constant | ||
it("(always) the workflow name in header and run button in actions", async () => { | ||
const { wrapper } = await mountWorkflowInvocationHeader(); | ||
|
||
const heading = wrapper.find(SELECTORS.INVOKED_WORKFLOW_HEADING); | ||
expect(heading.text()).toBe(`Invoked Workflow: "${SAMPLE_WORKFLOW.name}"`); | ||
|
||
const actionsGroup = wrapper.find(SELECTORS.ACTIONS_BUTTON_GROUP); | ||
const runButton = actionsGroup.find(SELECTORS.RUN_WORKFLOW_BUTTON); | ||
expect(runButton.attributes("title")).toContain(SAMPLE_WORKFLOW.name); | ||
}); | ||
|
||
it("return to invocations list button if not from panel", async () => { | ||
const { wrapper } = await mountWorkflowInvocationHeader(false, true); | ||
const returnButton = wrapper.find(SELECTORS.RETURN_TO_INVOCATIONS_LIST_BUTTON); | ||
expect(returnButton.text()).toBe("Invocations List"); | ||
}); | ||
|
||
it("edit button if user owns the workflow", async () => { | ||
const { wrapper } = await mountWorkflowInvocationHeader(); | ||
const actionsGroup = wrapper.find(SELECTORS.ACTIONS_BUTTON_GROUP); | ||
const editButton = actionsGroup.find(SELECTORS.EDIT_WORKFLOW_BUTTON); | ||
expect(editButton.attributes("to")).toBe( | ||
`/workflows/edit?id=${SAMPLE_WORKFLOW.id}&version=${SAMPLE_WORKFLOW.version}` | ||
); | ||
}); | ||
|
||
it("import button instead if user does not own the workflow", async () => { | ||
const { wrapper } = await mountWorkflowInvocationHeader(false); | ||
const actionsGroup = wrapper.find(SELECTORS.ACTIONS_BUTTON_GROUP); | ||
const importButton = actionsGroup.find(SELECTORS.IMPORT_WORKFLOW_BUTTON); | ||
expect(importButton.exists()).toBe(true); | ||
}); | ||
}); | ||
|
||
describe("Importing a workflow in WorkflowInvocationHeader", () => { | ||
it("should show a confirmation dialog when the import is successful", async () => { | ||
const { wrapper } = await mountWorkflowInvocationHeader(false); | ||
const actionsGroup = wrapper.find(SELECTORS.ACTIONS_BUTTON_GROUP); | ||
const importButton = actionsGroup.find(SELECTORS.IMPORT_WORKFLOW_BUTTON); | ||
|
||
// Cannot `.trigger("click")` on `AsyncButton` because it is a stubbed custom component | ||
await importButton.props().action(); | ||
await flushPromises(); | ||
|
||
const alert = wrapper.find(SELECTORS.ALERT_MESSAGE); | ||
expect(alert.attributes("variant")).toBe("info"); | ||
expect(alert.text()).toContain(`Workflow ${SAMPLE_WORKFLOW.name} imported successfully`); | ||
}); | ||
|
||
it("should show an error dialog when the import fails", async () => { | ||
const { wrapper } = await mountWorkflowInvocationHeader(false, false, true); | ||
const actionsGroup = wrapper.find(SELECTORS.ACTIONS_BUTTON_GROUP); | ||
const importButton = actionsGroup.find(SELECTORS.IMPORT_WORKFLOW_BUTTON); | ||
|
||
// Cannot `.trigger("click")` on `AsyncButton` because it is a stubbed custom component | ||
await importButton.props().action(); | ||
await flushPromises(); | ||
|
||
const alert = wrapper.find(SELECTORS.ALERT_MESSAGE); | ||
expect(alert.attributes("variant")).toBe("danger"); | ||
expect(alert.text()).toContain(IMPORT_ERROR_MESSAGE); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters