Merge branch 'v2/main' into v2/chore/2156-use-mcp-apps
This commit is contained in:
commit
d79e96b0f2
12 changed files with 885 additions and 59 deletions
|
|
@ -64,3 +64,14 @@ describe("PromptMessagesDisplay", () => {
|
|||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
// The `data-testid` the headless tab smoke waits on (#2148). It asserts the
|
||||
// messages RENDERED, which `data-get-status="ok"` alone does not — that flips
|
||||
// as soon as `prompts/get` resolves. Pinned here so a rename fails loudly in a
|
||||
// unit test rather than as an opaque timeout in a five-minute gate.
|
||||
describe("automation contract (#2148)", () => {
|
||||
it("exposes a stable data-testid on the panel", () => {
|
||||
renderWithMantine(<PromptMessagesDisplay messages={messages} />);
|
||||
expect(screen.getByTestId("prompt-messages")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -67,7 +67,10 @@ export function PromptMessagesDisplay({
|
|||
onClose,
|
||||
}: PromptMessagesDisplayProps) {
|
||||
return (
|
||||
<PanelStack>
|
||||
// `data-testid` so a driver can wait on the messages having actually
|
||||
// RENDERED, not merely on `prompts/get` having returned (#2148) — see the
|
||||
// ResourcePreviewPanel counterpart for why the RPC status is not enough.
|
||||
<PanelStack data-testid="prompt-messages">
|
||||
<HeaderRow>
|
||||
<HeaderLeft>
|
||||
{onClose && (
|
||||
|
|
|
|||
|
|
@ -566,3 +566,22 @@ describe("ResourcePreviewPanel", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
// The `data-testid` the headless tab smoke waits on (#2148) — see the
|
||||
// PromptMessagesDisplay counterpart: `data-read-status="ok"` flips as soon as
|
||||
// `resources/read` resolves, so only this proves the preview rendered.
|
||||
describe("automation contract (#2148)", () => {
|
||||
it("exposes a stable data-testid on the panel", () => {
|
||||
renderWithMantine(
|
||||
<ResourcePreviewPanel
|
||||
resource={textResource}
|
||||
contents={textContents}
|
||||
isSubscribed={false}
|
||||
onRefresh={vi.fn()}
|
||||
onSubscribe={vi.fn()}
|
||||
onUnsubscribe={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByTestId("resource-preview")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -222,7 +222,12 @@ export function ResourcePreviewPanel({
|
|||
const sourceToggleable = isSourceToggleable(mimeType);
|
||||
|
||||
return (
|
||||
<PanelStack>
|
||||
// `data-testid` so a driver can wait on the preview having actually
|
||||
// RENDERED, not merely on `resources/read` having returned (#2148). The
|
||||
// read status flips as soon as the RPC resolves, so a smoke stopping there
|
||||
// would stay green with this panel removed. A `data-*` attribute is not
|
||||
// part of a component's typed props, so it is passed at the call site.
|
||||
<PanelStack data-testid="resource-preview">
|
||||
<HeaderRow>
|
||||
<HeaderLeft>
|
||||
{onClose && (
|
||||
|
|
|
|||
|
|
@ -85,3 +85,15 @@ describe("StructuredOutputPanel", () => {
|
|||
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(
|
||||
<StructuredOutputPanel structuredContent={structuredContent} />,
|
||||
);
|
||||
expect(screen.getByTestId("structured-output")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -72,7 +72,10 @@ export function StructuredOutputPanel({
|
|||
const [expanded, setExpanded] = useState(defaultExpanded);
|
||||
|
||||
return (
|
||||
<StructuredBox>
|
||||
// `data-testid` so the headless tab smoke (#2148) can assert this section
|
||||
// rendered without matching on its heading copy. A `data-*` attribute is
|
||||
// not part of a component's typed props, so it is passed at the call site.
|
||||
<StructuredBox data-testid="structured-output">
|
||||
<StructuredInner>
|
||||
<HeaderRow>
|
||||
<StructuredHeader>Structured Output</StructuredHeader>
|
||||
|
|
|
|||
|
|
@ -391,3 +391,34 @@ describe("PromptsScreen", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
// See the ToolsScreen counterpart: the `data-*` readiness contract the headless
|
||||
// tab smoke drives (#2148), pinned here so a rename fails loudly.
|
||||
describe("automation contract (#2148)", () => {
|
||||
it("reports the prompt count and an idle get status", () => {
|
||||
renderWithMantine(<PromptsScreen {...baseProps} />);
|
||||
const root = screen.getByTestId("prompts-screen");
|
||||
expect(root).toHaveAttribute(
|
||||
"data-prompt-count",
|
||||
String(promptsWithArgs.length),
|
||||
);
|
||||
expect(root).toHaveAttribute("data-get-status", "idle");
|
||||
});
|
||||
|
||||
it("tracks the get status through to a result", () => {
|
||||
renderWithMantine(
|
||||
<PromptsScreen
|
||||
{...baseProps}
|
||||
getPromptState={{
|
||||
status: "ok",
|
||||
promptName: promptsWithArgs[0].name,
|
||||
result: { messages: [] },
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByTestId("prompts-screen")).toHaveAttribute(
|
||||
"data-get-status",
|
||||
"ok",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -274,7 +274,13 @@ export function PromptsScreen({
|
|||
}
|
||||
|
||||
return (
|
||||
<ScreenLayout>
|
||||
// `data-*` readiness contract for the headless tab smoke (#2148); see
|
||||
// clients/web/README.md#core-tab-automation-contract.
|
||||
<ScreenLayout
|
||||
data-testid="prompts-screen"
|
||||
data-prompt-count={prompts.length}
|
||||
data-get-status={getPromptState?.status ?? "idle"}
|
||||
>
|
||||
<Sidebar>
|
||||
<SidebarCard>
|
||||
<PromptControls
|
||||
|
|
|
|||
|
|
@ -403,3 +403,36 @@ describe("ResourcesScreen", () => {
|
|||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
// See the ToolsScreen counterpart: the `data-*` readiness contract the headless
|
||||
// tab smoke drives (#2148), pinned here so a rename fails loudly.
|
||||
describe("automation contract (#2148)", () => {
|
||||
it("reports the resource and template counts separately", () => {
|
||||
renderWithMantine(<ResourcesScreen {...baseProps} />);
|
||||
const root = screen.getByTestId("resources-screen");
|
||||
// Two attributes rather than one: `resources/list` and
|
||||
// `resources/templates/list` are separate calls and either can fail alone.
|
||||
expect(root).toHaveAttribute(
|
||||
"data-resource-count",
|
||||
String(resources.length),
|
||||
);
|
||||
expect(root).toHaveAttribute(
|
||||
"data-template-count",
|
||||
String(templates.length),
|
||||
);
|
||||
expect(root).toHaveAttribute("data-read-status", "idle");
|
||||
});
|
||||
|
||||
it("tracks the read status through to a result", () => {
|
||||
renderWithMantine(
|
||||
<ResourcesScreen
|
||||
{...baseProps}
|
||||
readState={{ status: "ok", uri: "file:///x", result: okResult }}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByTestId("resources-screen")).toHaveAttribute(
|
||||
"data-read-status",
|
||||
"ok",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -319,7 +319,14 @@ export function ResourcesScreen({
|
|||
}
|
||||
|
||||
return (
|
||||
<ScreenLayout>
|
||||
// `data-*` readiness contract for the headless tab smoke (#2148); see
|
||||
// clients/web/README.md#core-tab-automation-contract.
|
||||
<ScreenLayout
|
||||
data-testid="resources-screen"
|
||||
data-resource-count={resources.length}
|
||||
data-template-count={templates.length}
|
||||
data-read-status={readState?.status ?? "idle"}
|
||||
>
|
||||
<Sidebar>
|
||||
<SidebarCard>
|
||||
<ResourceControls
|
||||
|
|
|
|||
|
|
@ -445,3 +445,41 @@ describe("ToolsScreen", () => {
|
|||
expect(screen.getByText("invalid_header_tool")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
// The `data-*` readiness contract the headless tab smoke drives (#2148). It is
|
||||
// a documented public contract (clients/web/README.md), and a smoke asserting
|
||||
// it fails as an opaque 45s timeout rather than a mismatch — so the attribute
|
||||
// names are pinned here, where a rename fails loudly instead.
|
||||
describe("automation contract (#2148)", () => {
|
||||
it("reports the tool count and an idle call status", () => {
|
||||
renderWithMantine(<ToolsScreen {...baseProps} />);
|
||||
const root = screen.getByTestId("tools-screen");
|
||||
expect(root).toHaveAttribute("data-tool-count", String(tools.length));
|
||||
// Absent call state reads `idle`, not empty: the smoke waits on a value.
|
||||
expect(root).toHaveAttribute("data-call-status", "idle");
|
||||
});
|
||||
|
||||
it("reports an empty list distinctly from a missing one", () => {
|
||||
renderWithMantine(<ToolsScreen {...baseProps} tools={[]} />);
|
||||
expect(screen.getByTestId("tools-screen")).toHaveAttribute(
|
||||
"data-tool-count",
|
||||
"0",
|
||||
);
|
||||
});
|
||||
|
||||
it("tracks the call status through to a result", () => {
|
||||
renderWithMantine(
|
||||
<ToolsScreen
|
||||
{...baseProps}
|
||||
callState={{
|
||||
status: "ok",
|
||||
result: { content: [{ type: "text", text: "done" }] },
|
||||
}}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByTestId("tools-screen")).toHaveAttribute(
|
||||
"data-call-status",
|
||||
"ok",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -198,7 +198,15 @@ export function ToolsScreen({
|
|||
};
|
||||
|
||||
return (
|
||||
<ScreenLayout>
|
||||
// `data-*` readiness contract for the headless tab smoke (#2148); see
|
||||
// clients/web/README.md#core-tab-automation-contract. Attributes are not
|
||||
// part of a component's typed props, so they are passed here rather than
|
||||
// baked into the `ScreenLayout` constant.
|
||||
<ScreenLayout
|
||||
data-testid="tools-screen"
|
||||
data-tool-count={tools.length}
|
||||
data-call-status={callState?.status ?? "idle"}
|
||||
>
|
||||
<Sidebar>
|
||||
<SidebarCard>
|
||||
<ToolControls
|
||||
|
|
|
|||
Loading…
Reference in a new issue