import { createTestingPinia } from "@pinia/testing"; import { getLocalVue } from "@tests/vitest/helpers"; import { mount } from "@vue/test-utils"; import flushPromises from "flush-promises"; import { PiniaVuePlugin, setActivePinia } from "pinia"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { HttpResponse, useServerMock } from "@/api/client/__mocks__"; import { useEntryPointStore } from "@/stores/entryPointStore"; import { useInteractiveToolsStore } from "@/stores/interactiveToolsStore"; import testInteractiveToolsResponse from "./testData/testInteractiveToolsResponse.json"; import InteractiveTools from "./InteractiveTools.vue"; // Mock the rethrowSimple function to prevent test failures vi.mock("@/utils/simple-error", () => ({ rethrowSimple: vi.fn(), errorMessageAsString: vi.fn((error) => error.message), })); const { server, http } = useServerMock(); describe("InteractiveTools/InteractiveTools.vue", () => { const localVue = getLocalVue(); localVue.use(PiniaVuePlugin); let wrapper; let testPinia; let deleteRequests = []; beforeEach(async () => { deleteRequests = []; // Set up the GET response for entry points - use a copy to avoid modifications between tests const testData = JSON.parse(JSON.stringify(testInteractiveToolsResponse)); server.use( http.untyped.get("/api/entry_points", () => { return HttpResponse.json(testData); }), ); testPinia = createTestingPinia({ createSpy: vi.fn, initialState: { entryPointStore: { entryPoints: testData, }, interactiveToolsStore: { messages: [], activeTools: testData, }, }, stubActions: false, }); setActivePinia(testPinia); wrapper = mount(InteractiveTools, { localVue, pinia: testPinia, }); // Wait for any async operations to complete await flushPromises(); }); afterEach(() => { vi.clearAllMocks(); }); it("renders table with interactive tools", async () => { expect(wrapper.find("#g-table-interactive-tool-table").exists()).toBeTruthy(); // Each entry has 2 links: one to open inline (rendered as button) and one to open in new tab (rendered as anchor) expect(wrapper.findAll("td > button.g-link").length).toBe(2); expect(wrapper.findAll("td > a").length).toBe(2); }); it("displays interactive tool information correctly", async () => { const firstTool = testInteractiveToolsResponse[0]; const toolName = wrapper.find(`#link-${firstTool.id}`); expect(toolName.text()).toContain(firstTool.name); // Check if the external link is present const externalLink = wrapper.find(`#external-link-${firstTool.id}`); expect(externalLink.exists()).toBeTruthy(); expect(externalLink.attributes("href")).toBe(firstTool.target); expect(externalLink.attributes("target")).toBe("_blank"); }); it("removes the interactive tool when it is gone from store", async () => { const firstTool = testInteractiveToolsResponse[0]; function checkIfExists(tag, id) { return wrapper.find(tag + id).exists(); } expect(checkIfExists("#link-", firstTool.id)).toBeTruthy(); // drop the tested tool from store const store = useEntryPointStore(); await store.entryPoints.splice(0, 1); expect(checkIfExists("#link-", firstTool.id)).toBeFalsy(); }); it("sends a delete request after the stop button is pressed", async () => { const firstTool = testInteractiveToolsResponse[0]; const toolId = firstTool.id; server.use( http.untyped.delete(/\/api\/entry_points\/.*/, ({ request }) => { deleteRequests.push({ url: request.url }); return HttpResponse.json({ status: "ok", message: "ok" }); }), ); await wrapper.get(`#stop-${toolId}`).trigger("click"); await flushPromises(); expect(deleteRequests.length).toBe(1); expect(deleteRequests[0].url.includes(toolId)).toBeTruthy(); }); it("shows an error message if the tool deletion fails", async () => { const firstTool = testInteractiveToolsResponse[0]; const toolId = firstTool.id; const interactiveToolsStore = useInteractiveToolsStore(); // Set up the network error for DELETE requests server.use( http.untyped.delete(/\/api\/entry_points\/.*/, () => { return HttpResponse.error(); }), ); // Try to stop the tool await wrapper.get(`#stop-${toolId}`).trigger("click"); await flushPromises(); // The error should be in the interactiveToolsStore messages expect(interactiveToolsStore.messages.length).toBeGreaterThan(0); expect(interactiveToolsStore.messages[0]).toMatch(/Network Error|Failed to fetch/); // The error should be displayed in the UI expect(wrapper.get(".alert-danger").text()).toMatch(/Network Error|Failed to fetch/); }); });