import { defineComponent, ref, watch } from "vue"; import { screen, fireEvent, waitFor } from "@testing-library/vue"; import { z } from "zod"; import { describe, it, expect } from "vitest"; import { useFrontendTool } from "../use-frontend-tool"; import { useCopilotKit } from "../../providers/useCopilotKit"; import type { VueFrontendTool } from "../../types"; import type { CopilotKitCoreVue } from "../../lib/vue-core"; import { renderWithCopilotKit } from "../../__tests__/utils/test-helpers"; /** * Component that captures the copilotkit core ref for test assertions. */ const CoreCapture = defineComponent({ props: { onCore: { type: Function as () => (core: CopilotKitCoreVue) => void, required: true, }, }, setup(props) { const { copilotkit } = useCopilotKit(); watch( copilotkit, (core) => { if (core) { props.onCore(core); } }, { immediate: true }, ); return {}; }, template: `
`, }); describe("useFrontendTool available flag", () => { it("registers tool with available: false on the core", async () => { let coreRef: CopilotKitCoreVue | null = null; const ToolComponent = defineComponent({ setup() { const tool: VueFrontendTool<{ msg: string }> = { name: "disabledTool", description: "A disabled tool", available: false, parameters: z.object({ msg: z.string() }), handler: async () => ({ result: "ok" }), }; useFrontendTool(tool); return {}; }, template: `
`, }); const Host = defineComponent({ components: { ToolComponent, CoreCapture }, setup() { return { setCore: (core: CopilotKitCoreVue) => { coreRef = core; }, }; }, template: `
`, }); const ui = renderWithCopilotKit({ children: Host, }); await waitFor(() => { expect(coreRef).not.toBeNull(); const tool = coreRef!.tools.find( (entry) => entry.name === "disabledTool", ); expect(tool).toBeDefined(); expect(tool!.available).toBe(false); }); ui.unmount(); }); it("registers tool with available: true on the core", async () => { let coreRef: CopilotKitCoreVue | null = null; const ToolComponent = defineComponent({ setup() { const tool: VueFrontendTool<{ msg: string }> = { name: "enabledTool", description: "An enabled tool", available: true, parameters: z.object({ msg: z.string() }), handler: async () => ({ result: "ok" }), }; useFrontendTool(tool); return {}; }, template: `
`, }); const Host = defineComponent({ components: { ToolComponent, CoreCapture }, setup() { return { setCore: (core: CopilotKitCoreVue) => { coreRef = core; }, }; }, template: `
`, }); const ui = renderWithCopilotKit({ children: Host, }); await waitFor(() => { expect(coreRef).not.toBeNull(); const tool = coreRef!.tools.find((entry) => entry.name === "enabledTool"); expect(tool).toBeDefined(); expect(tool!.available).toBe(true); }); ui.unmount(); }); it("re-registers tool when available toggles between true and false", async () => { let coreRef: CopilotKitCoreVue | null = null; const ToolWithToggle = defineComponent({ setup() { const isEnabled = ref(true); const tool: VueFrontendTool<{ data: string }> = { name: "toggleTool", description: "A toggleable tool", get available() { return isEnabled.value; }, parameters: z.object({ data: z.string() }), handler: async () => ({ ok: true }), }; useFrontendTool(tool, [isEnabled]); return { isEnabled, toggle: () => { isEnabled.value = !isEnabled.value; }, }; }, template: ` `, }); const Host = defineComponent({ components: { ToolWithToggle, CoreCapture }, setup() { return { setCore: (core: CopilotKitCoreVue) => { coreRef = core; }, }; }, template: `
`, }); const ui = renderWithCopilotKit({ children: Host, }); // Tool should be registered as enabled initially await waitFor(() => { expect(coreRef).not.toBeNull(); const tool = coreRef!.tools.find((entry) => entry.name === "toggleTool"); expect(tool).toBeDefined(); expect(tool!.available).toBe(true); }); // Toggle to disabled await fireEvent.click(screen.getByTestId("toggle-btn")); // Tool should be re-registered as disabled await waitFor(() => { const tool = coreRef!.tools.find((entry) => entry.name === "toggleTool"); expect(tool).toBeDefined(); expect(tool!.available).toBe(false); }); // Toggle back to enabled await fireEvent.click(screen.getByTestId("toggle-btn")); await waitFor(() => { const tool = coreRef!.tools.find((entry) => entry.name === "toggleTool"); expect(tool).toBeDefined(); expect(tool!.available).toBe(true); }); ui.unmount(); }); });