diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.test.ts b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.test.ts index a57261db8110..93b3a22c9b09 100644 --- a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.test.ts +++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.test.ts @@ -51,7 +51,11 @@ const invocationJobsSummaryById = { // Mock the invocation store to return the expected invocation data given the invocation ID jest.mock("@/stores/invocationStore", () => { const originalModule = jest.requireActual("@/stores/invocationStore"); - const mockFetchInvocationForId = jest.fn(); + const mockFetchInvocationForId = jest.fn().mockImplementation((fetchParams) => { + if (fetchParams.id === "error-invocation") { + throw new Error("User does not own specified item."); + } + }); const mockFetchInvocationJobsSummaryForId = jest.fn(); return { ...originalModule, @@ -106,8 +110,8 @@ describe("WorkflowInvocationState check invocation and job terminal states", () const wrapper = await mountWorkflowInvocationState(invocationData.id); expect(isInvocationAndJobTerminal(wrapper)).toBe(true); - // Neither the invocation nor the jobs summary should be fetched for terminal invocations - assertInvocationFetched(0); + // Invocation is fetched once and the jobs summary isn't fetched at all for terminal invocations + assertInvocationFetched(1); assertJobsSummaryFetched(0); }); @@ -115,17 +119,23 @@ describe("WorkflowInvocationState check invocation and job terminal states", () const wrapper = await mountWorkflowInvocationState("not-fetched-invocation"); expect(isInvocationAndJobTerminal(wrapper)).toBe(false); - // Both, the invocation and jobs summary should be fetched once if the invocation is not in the store + // Invocation is fetched once and the jobs summary is then never fetched if the invocation is not in the store assertInvocationFetched(1); - assertJobsSummaryFetched(1); + assertJobsSummaryFetched(0); + + // expect there to be an alert for the missing invocation + const alert = wrapper.find("balert-stub"); + expect(alert.attributes("variant")).toBe("info"); + const span = alert.find("span"); + expect(span.text()).toBe("Invocation not found."); }); it("determines that invocation is not terminal with non-terminal state", async () => { const wrapper = await mountWorkflowInvocationState("non-terminal-id"); expect(isInvocationAndJobTerminal(wrapper)).toBe(false); - // Only the invocation should be fetched for non-terminal invocations - assertInvocationFetched(1); + // Only the invocation is fetched for non-terminal invocations; once for the initial fetch and then for the polling + assertInvocationFetched(2); assertJobsSummaryFetched(0); }); @@ -133,10 +143,24 @@ describe("WorkflowInvocationState check invocation and job terminal states", () const wrapper = await mountWorkflowInvocationState("non-terminal-jobs"); expect(isInvocationAndJobTerminal(wrapper)).toBe(false); - // Only the jobs summary should be fetched, not the invocation since it is in scheduled/terminal state - assertInvocationFetched(0); + // Only the jobs summary should be polled, the invocation is initially fetched only since it is in scheduled/terminal state + assertInvocationFetched(1); assertJobsSummaryFetched(1); }); + + it("determines that errored invocation fetches are handled correctly", async () => { + const wrapper = await mountWorkflowInvocationState("error-invocation"); + expect(isInvocationAndJobTerminal(wrapper)).toBe(false); + + // Invocation is fetched once and the jobs summary isn't fetched at all for errored invocations + assertInvocationFetched(1); + assertJobsSummaryFetched(0); + + // expect there to be an alert for the handled error + const alert = wrapper.find("balert-stub"); + expect(alert.attributes("variant")).toBe("danger"); + expect(alert.text()).toBe("User does not own specified item."); + }); }); describe("WorkflowInvocationState check 'Report' tab disabled state", () => { diff --git a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue index 002ba22ed833..b1e590cd203d 100644 --- a/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue +++ b/client/src/components/WorkflowInvocationState/WorkflowInvocationState.vue @@ -73,7 +73,7 @@ useAnimationFrameResizeObserver(scrollableDiv, ({ clientSize, scrollSize }) => { }); const invocation = computed(() => - !initialLoading.value + !initialLoading.value && !errorMessage.value ? (invocationStore.getInvocationById(props.invocationId) as WorkflowInvocationElementView) : null ); @@ -120,6 +120,8 @@ onMounted(async () => { } } catch (e) { errorMessage.value = errorMessageAsString(e); + } finally { + initialLoading.value = false; } }); @@ -274,10 +276,13 @@ function getWorkflowName() { + + + {{ errorMessage }} - + Invocation not found.