import { describe, it, expect, vi } from "vitest";
import userEvent from "@testing-library/user-event";
import { renderWithMantine, screen } from "../../../test/renderWithMantine";
import { StructuredOutputPanel } from "./StructuredOutputPanel";
// The real CodeHighlight dynamic-imports the Prism runtime and each grammar
// (its own behavior is covered in CodeHighlight.test.tsx). Stub it so these
// tests assert on the JSON handed to it, synchronously.
vi.mock("../../elements/CodeHighlight/CodeHighlight", () => ({
CodeHighlight: ({ language, code }: { language: string; code: string }) => (
{code}
),
}));
const structuredContent = {
items: [
{ id: 1, name: "Item A", tags: ["foo", "bar"] },
{ id: 2, name: "Item B", tags: ["baz"] },
],
total: 2,
};
describe("StructuredOutputPanel", () => {
it("renders a labeled section with the payload as pretty-printed JSON", () => {
renderWithMantine(
,
);
expect(
screen.getByRole("heading", { name: "Structured Output" }),
).toBeInTheDocument();
const code = screen.getByTestId("code-highlight");
expect(code).toHaveAttribute("data-language", "json");
expect(code).toHaveTextContent(/"total": 2/);
// Nested values are inspectable field by field, not summarized away.
expect(code).toHaveTextContent(/"name": "Item A"/);
expect(code).toHaveTextContent(/"tags"/);
});
it("starts expanded by default", () => {
renderWithMantine(
,
);
expect(
screen.getByRole("button", { name: "Collapse structured output" }),
).toHaveAttribute("aria-expanded", "true");
});
it("starts collapsed when defaultExpanded is false", () => {
renderWithMantine(
,
);
expect(
screen.getByRole("button", { name: "Expand structured output" }),
).toHaveAttribute("aria-expanded", "false");
});
it("toggles between expanded and collapsed", async () => {
const user = userEvent.setup();
renderWithMantine(
,
);
await user.click(
screen.getByRole("button", { name: "Collapse structured output" }),
);
const toggle = screen.getByRole("button", {
name: "Expand structured output",
});
expect(toggle).toHaveAttribute("aria-expanded", "false");
await user.click(toggle);
expect(
screen.getByRole("button", { name: "Collapse structured output" }),
).toHaveAttribute("aria-expanded", "true");
});
it("renders an empty structured payload rather than nothing", () => {
renderWithMantine();
expect(
screen.getByRole("heading", { name: "Structured Output" }),
).toBeInTheDocument();
expect(screen.getByTestId("code-highlight")).toHaveTextContent("{}");
});
});
// The `data-testid` the headless tab smoke waits on (#2148). Asserting the
// section by its heading copy would make the smoke fail on a rewording, so the
// smoke keys off this attribute — pinned here so a rename fails loudly.
describe("automation contract (#2148)", () => {
it("exposes a stable data-testid on the section", () => {
renderWithMantine(
,
);
expect(screen.getByTestId("structured-output")).toBeInTheDocument();
});
});