test: cover the structured-output cap and the list_items showcase end to end
Two gaps from the Copilot review on #1975: - The `Large` story asserted nothing, so dropping the section's `mah` (or applying it to the wrong element) would still pass CI. Its play function now measures the real ScrollArea viewport: the visible height stays at the cap and the overflow is scrollable rather than clipped. - Nothing exercised the `list_items` fixture, so a preset-registration typo would leave the documented showcase broken with a green suite. A new integration test loads `structured-output-http.json` through loadConfig/resolveConfig — covering the file, the registry entry, and the fixture — connects, calls the tool, and asserts both the summary block and the nested structuredContent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SW1p8E2uiyyLx4RKwrwSrt
This commit is contained in:
parent
6f47eaa143
commit
8049b4f7c8
2 changed files with 68 additions and 0 deletions
|
|
@ -59,10 +59,29 @@ export const Flat: Story = {
|
|||
},
|
||||
};
|
||||
|
||||
// A payload taller than the section's cap scrolls inside it rather than
|
||||
// pushing the rest of the result panel down. Asserted on the real geometry,
|
||||
// so dropping `mah` (or moving it to the wrong element) fails here.
|
||||
export const Large: Story = {
|
||||
args: {
|
||||
structuredContent: large,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
await waitFor(() =>
|
||||
expect(canvasElement.textContent).toContain('"Row 60"'),
|
||||
);
|
||||
const viewport = canvasElement.querySelector(
|
||||
".mantine-ScrollArea-viewport",
|
||||
);
|
||||
if (!(viewport instanceof HTMLElement)) {
|
||||
throw new Error("scroll viewport not found");
|
||||
}
|
||||
// Capped: the visible height stays at the section's `mah`, well under the
|
||||
// payload's natural height…
|
||||
expect(viewport.clientHeight).toBeLessThanOrEqual(400);
|
||||
// …and the overflow is reachable by scrolling rather than clipped away.
|
||||
expect(viewport.scrollHeight).toBeGreaterThan(viewport.clientHeight);
|
||||
},
|
||||
};
|
||||
|
||||
// Collapsing hides the payload; expanding brings it back.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
||||
import { resolve } from "node:path";
|
||||
import * as z from "zod/v4";
|
||||
import { InspectorClient } from "@inspector/core/mcp/inspectorClient.js";
|
||||
import {
|
||||
|
|
@ -45,6 +46,8 @@ import {
|
|||
createAddResourceTool,
|
||||
createAddToolTool,
|
||||
createAddPromptTool,
|
||||
loadConfig,
|
||||
resolveConfig,
|
||||
} from "@modelcontextprotocol/inspector-test-server";
|
||||
import type {
|
||||
MessageEntry,
|
||||
|
|
@ -1214,6 +1217,52 @@ describe("InspectorClient", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("Structured output showcase config (#1908)", () => {
|
||||
// Drives the shipped `structured-output-http.json` end to end — the file
|
||||
// itself, the `list_items` preset registration, and the fixture's nested
|
||||
// payload. A typo in any of the three fails here rather than leaving the
|
||||
// documented showcase quietly broken.
|
||||
const showcaseConfigPath = resolve(
|
||||
import.meta.dirname,
|
||||
"../../../../../../test-servers/configs/structured-output-http.json",
|
||||
);
|
||||
|
||||
it("serves list_items with a summary block and a nested structuredContent", async () => {
|
||||
server = createTestServerHttp(
|
||||
resolveConfig(loadConfig(showcaseConfigPath)),
|
||||
);
|
||||
await server.start();
|
||||
|
||||
client = new InspectorClient(
|
||||
{ type: "streamable-http", url: server.url },
|
||||
{ environment: { transport: createTransportNode } },
|
||||
);
|
||||
await client.connect();
|
||||
|
||||
const tool = await getTool(client, "list_items");
|
||||
expect(tool.outputSchema).toBeDefined();
|
||||
|
||||
const result = await client.callTool(tool, {});
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
// The text block only summarizes — the payload is the structured half.
|
||||
const content = result.result!.content as Array<{
|
||||
type: string;
|
||||
text?: string;
|
||||
}>;
|
||||
expect(content[0].type).toBe("text");
|
||||
expect(content[0].text).toBe("Found 2 items.");
|
||||
|
||||
expect(result.result!.structuredContent).toEqual({
|
||||
items: [
|
||||
{ id: 1, name: "Item A", tags: ["foo", "bar"] },
|
||||
{ id: 2, name: "Item B", tags: ["baz"] },
|
||||
],
|
||||
total: 2,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Default metadata (server-wide _meta)", () => {
|
||||
function metaOf(req: { message: unknown }): Record<string, unknown> {
|
||||
const params = (req.message as { params?: { _meta?: unknown } }).params;
|
||||
|
|
|
|||
Loading…
Reference in a new issue