Merge remote-tracking branch 'origin/v2/main' into v2/sdk-v2-migration-1624
# Conflicts: # clients/web/src/components/groups/ResourceLink/ResourceLink.tsx # clients/web/src/components/groups/ToolResultPanel/ToolResultPanel.stories.tsx # clients/web/src/components/screens/ToolsScreen/ToolsScreen.tsx
This commit is contained in:
commit
39362dd5ff
1 changed files with 591 additions and 197 deletions
|
|
@ -7,6 +7,7 @@ import {
|
|||
waitFor,
|
||||
} from "../../../test/renderWithMantine";
|
||||
import { ToolResultPanel } from "./ToolResultPanel";
|
||||
import { resultHasResourceLinks } from "./toolResultUtils";
|
||||
|
||||
const okResult: CallToolResult = {
|
||||
content: [{ type: "text", text: "ok" }],
|
||||
|
|
@ -41,7 +42,7 @@ describe("ToolResultPanel", () => {
|
|||
expect(screen.getByText("No results yet")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders a resource_link block as an expandable ResourceLink", async () => {
|
||||
it("groups resource_link blocks in a scrollable Resource Links box", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onReadResource = vi.fn().mockResolvedValue({
|
||||
contents: [{ uri: "demo://r/1", text: "linked body" }],
|
||||
|
|
@ -60,6 +61,10 @@ describe("ToolResultPanel", () => {
|
|||
/>,
|
||||
);
|
||||
expect(screen.getByText("ok")).toBeInTheDocument();
|
||||
// The link sits inside a grouped, labeled box.
|
||||
expect(
|
||||
screen.getByRole("heading", { name: "Resource Links" }),
|
||||
).toBeInTheDocument();
|
||||
await user.click(
|
||||
screen.getByRole("button", { name: "Expand resource demo://r/1" }),
|
||||
);
|
||||
|
|
@ -69,6 +74,41 @@ describe("ToolResultPanel", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("collapses consecutive resource_link blocks into a single box", () => {
|
||||
const result: CallToolResult = {
|
||||
content: [
|
||||
{ type: "text", text: "intro" },
|
||||
{ type: "resource_link", uri: "demo://r/1", name: "One" },
|
||||
{ type: "resource_link", uri: "demo://r/2", name: "Two" },
|
||||
{ type: "resource_link", uri: "demo://r/3", name: "Three" },
|
||||
],
|
||||
};
|
||||
renderWithMantine(<ToolResultPanel result={result} onClear={() => {}} />);
|
||||
// One shared "Resource Links" heading for the whole run of links.
|
||||
expect(
|
||||
screen.getAllByRole("heading", { name: "Resource Links" }),
|
||||
).toHaveLength(1);
|
||||
expect(screen.getByText("One")).toBeInTheDocument();
|
||||
expect(screen.getByText("Two")).toBeInTheDocument();
|
||||
expect(screen.getByText("Three")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders a separate Resource Links box per non-adjacent run", () => {
|
||||
const result: CallToolResult = {
|
||||
content: [
|
||||
{ type: "resource_link", uri: "demo://r/1", name: "One" },
|
||||
{ type: "text", text: "divider" },
|
||||
{ type: "resource_link", uri: "demo://r/2", name: "Two" },
|
||||
],
|
||||
};
|
||||
renderWithMantine(<ToolResultPanel result={result} onClear={() => {}} />);
|
||||
// The text block between the two links splits them into two boxes.
|
||||
expect(
|
||||
screen.getAllByRole("heading", { name: "Resource Links" }),
|
||||
).toHaveLength(2);
|
||||
expect(screen.getByText("divider")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("invokes onClear when the close button is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onClear = vi.fn();
|
||||
|
|
@ -76,4 +116,36 @@ describe("ToolResultPanel", () => {
|
|||
await user.click(screen.getByRole("button", { name: "Close results" }));
|
||||
expect(onClear).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
describe("resultHasResourceLinks", () => {
|
||||
it("is true only for a non-error result containing a resource_link", () => {
|
||||
expect(
|
||||
resultHasResourceLinks({
|
||||
content: [
|
||||
{ type: "text", text: "ok" },
|
||||
{ type: "resource_link", uri: "demo://r/1", name: "Linked" },
|
||||
],
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("is false for a text-only result", () => {
|
||||
expect(resultHasResourceLinks(okResult)).toBe(false);
|
||||
});
|
||||
|
||||
it("is false for an empty result", () => {
|
||||
expect(resultHasResourceLinks(emptyResult)).toBe(false);
|
||||
});
|
||||
|
||||
it("is false when the result is an error, even with a resource_link", () => {
|
||||
expect(
|
||||
resultHasResourceLinks({
|
||||
isError: true,
|
||||
content: [
|
||||
{ type: "resource_link", uri: "demo://r/1", name: "Linked" },
|
||||
],
|
||||
}),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue