feat(mcp): add preset setup and capability mentions
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { fireEvent, render, screen, waitFor, within } from "@testing-library/react";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { ThreadComposer } from "@/components/thread/ThreadComposer";
|
||||
import type { CliAppInfo, SlashCommand } from "@/lib/types";
|
||||
import type { CliAppInfo, McpPresetInfo, SlashCommand } from "@/lib/types";
|
||||
|
||||
const COMMANDS: SlashCommand[] = [
|
||||
{
|
||||
@@ -70,6 +70,47 @@ const CLI_APPS: CliAppInfo[] = [
|
||||
skill_installed: false,
|
||||
},
|
||||
];
|
||||
|
||||
const MCP_PRESETS: McpPresetInfo[] = [
|
||||
{
|
||||
name: "browserbase",
|
||||
display_name: "Browserbase",
|
||||
category: "browser",
|
||||
description: "Cloud browser automation",
|
||||
docs_url: "https://docs.browserbase.com",
|
||||
transport: "streamableHttp",
|
||||
requires: "Browserbase API key",
|
||||
note: "",
|
||||
install_supported: true,
|
||||
installed: true,
|
||||
configured: true,
|
||||
available: true,
|
||||
status: "configured",
|
||||
logo_url: "https://example.invalid/browserbase.svg",
|
||||
brand_color: "#111827",
|
||||
required_fields: [],
|
||||
connection_summary: "https://mcp.browserbase.com/mcp",
|
||||
},
|
||||
{
|
||||
name: "figma",
|
||||
display_name: "Figma",
|
||||
category: "design",
|
||||
description: "Design context",
|
||||
docs_url: "https://figma.com",
|
||||
transport: "streamableHttp",
|
||||
requires: "Figma desktop",
|
||||
note: "",
|
||||
install_supported: true,
|
||||
installed: true,
|
||||
configured: false,
|
||||
available: false,
|
||||
status: "missing_credentials",
|
||||
logo_url: null,
|
||||
brand_color: "#F24E1E",
|
||||
required_fields: [],
|
||||
connection_summary: "",
|
||||
},
|
||||
];
|
||||
const ORIGINAL_INNER_HEIGHT = window.innerHeight;
|
||||
|
||||
afterEach(() => {
|
||||
@@ -125,11 +166,14 @@ describe("ThreadComposer", () => {
|
||||
<ThreadComposer
|
||||
onSend={vi.fn()}
|
||||
modelLabel="gpt-4o"
|
||||
modelProvider="openai"
|
||||
modelProviderLabel="OpenAI"
|
||||
placeholder="Type your message..."
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText("gpt-4o")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("composer-model-logo-openai")).toBeInTheDocument();
|
||||
const input = screen.getByPlaceholderText("Type your message...");
|
||||
expect(input.className).toContain("min-h-[50px]");
|
||||
expect(input.parentElement?.parentElement?.className).toContain("max-w-[49.5rem]");
|
||||
@@ -227,7 +271,7 @@ describe("ThreadComposer", () => {
|
||||
const input = screen.getByLabelText("Message input");
|
||||
fireEvent.change(input, { target: { value: "@", selectionStart: 1 } });
|
||||
|
||||
const palette = screen.getByRole("listbox", { name: "CLI Apps" });
|
||||
const palette = screen.getByRole("listbox", { name: "Apps and MCP" });
|
||||
expect(palette).toBeInTheDocument();
|
||||
expect(screen.getByRole("option", { name: /@gimp/i })).toHaveAttribute(
|
||||
"aria-selected",
|
||||
@@ -246,7 +290,7 @@ describe("ThreadComposer", () => {
|
||||
expect(screen.getByTestId("composer-cli-mention-blender")).toHaveTextContent("@blender");
|
||||
expect(screen.queryByTestId("composer-cli-app-tray")).not.toBeInTheDocument();
|
||||
expect(onSend).not.toHaveBeenCalled();
|
||||
expect(screen.queryByRole("listbox", { name: "CLI Apps" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("listbox", { name: "Apps and MCP" })).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Send message" }));
|
||||
|
||||
@@ -282,6 +326,69 @@ describe("ThreadComposer", () => {
|
||||
expect(screen.getByTestId("composer-cli-mention-blender")).toHaveTextContent("@blender");
|
||||
});
|
||||
|
||||
it("shows configured MCP presets in the mention palette and submits metadata", () => {
|
||||
const onSend = vi.fn();
|
||||
render(
|
||||
<ThreadComposer
|
||||
onSend={onSend}
|
||||
placeholder="Type your message..."
|
||||
cliApps={CLI_APPS}
|
||||
mcpPresets={MCP_PRESETS}
|
||||
/>,
|
||||
);
|
||||
|
||||
const input = screen.getByLabelText("Message input");
|
||||
fireEvent.change(input, {
|
||||
target: { value: "use @bro", selectionStart: 8 },
|
||||
});
|
||||
|
||||
expect(screen.getByRole("option", { name: /@browserbase/i })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("option", { name: /@figma/i })).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.keyDown(input, { key: "Tab" });
|
||||
|
||||
expect(input).toHaveValue("use @browserbase ");
|
||||
expect(screen.getByTestId("composer-mcp-mention-browserbase")).toHaveTextContent("@browserbase");
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Send message" }));
|
||||
|
||||
expect(onSend).toHaveBeenCalledWith("use @browserbase", undefined, {
|
||||
mcpPresets: [{
|
||||
name: "browserbase",
|
||||
display_name: "Browserbase",
|
||||
category: "browser",
|
||||
transport: "streamableHttp",
|
||||
status: "configured",
|
||||
configured: true,
|
||||
logo_url: "https://example.invalid/browserbase.svg",
|
||||
brand_color: "#111827",
|
||||
}],
|
||||
});
|
||||
});
|
||||
|
||||
it("shows right-side source badges so users can distinguish CLI apps from MCP servers", () => {
|
||||
render(
|
||||
<ThreadComposer
|
||||
onSend={vi.fn()}
|
||||
placeholder="Type your message..."
|
||||
cliApps={CLI_APPS}
|
||||
mcpPresets={MCP_PRESETS}
|
||||
/>,
|
||||
);
|
||||
|
||||
const input = screen.getByLabelText("Message input");
|
||||
fireEvent.change(input, { target: { value: "@", selectionStart: 1 } });
|
||||
|
||||
expect(screen.queryByText("CLI Apps")).not.toBeInTheDocument();
|
||||
expect(screen.queryByText("MCP servers")).not.toBeInTheDocument();
|
||||
const gimp = screen.getByRole("option", { name: /GIMP @gimp .* CLI/i });
|
||||
const browserbase = screen.getByRole("option", { name: /Browserbase @browserbase .* MCP/i });
|
||||
expect(within(gimp).getByText("CLI")).toBeInTheDocument();
|
||||
expect(within(browserbase).getByText("MCP")).toBeInTheDocument();
|
||||
expect(within(gimp).getByText("@gimp")).toBeInTheDocument();
|
||||
expect(within(browserbase).getByText("@browserbase")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not duplicate the next word separator when completing a CLI app mention", () => {
|
||||
render(
|
||||
<ThreadComposer
|
||||
|
||||
Reference in New Issue
Block a user