feat(frontend): multiselect/artifact-gate/artifact widgets + linkage + no-limbo

This commit is contained in:
2026-06-28 12:36:13 +02:00
parent 2b55e96608
commit be9e761e0a
8 changed files with 389 additions and 1 deletions
+60
View File
@@ -0,0 +1,60 @@
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { ArtifactGateWidget } from "./ArtifactGateWidget";
test("reject opens a freetext and combines the reason", async () => {
const onRespond = vi.fn();
render(
<ArtifactGateWidget
descriptor={{
id: "u1",
widget: "artifact-gate",
artifact: { kind: "cte", content: "SELECT 1" },
options: [
{ id: "approve", label: "Approva" },
{
id: "reject",
label: "Rifiuta",
opens: { id: "u1c", widget: "freetext", title: "Motivazione" },
},
],
}}
onRespond={onRespond}
/>
);
await userEvent.click(screen.getByRole("button", { name: /Rifiuta/ }));
await userEvent.type(screen.getByRole("textbox"), "join sbagliata");
await userEvent.click(screen.getByRole("button", { name: /invia/i }));
expect(onRespond).toHaveBeenCalledWith({
id: "u1",
kind: "artifact-gate",
choices: ["reject"],
text: "join sbagliata",
});
});
test("approve (no opens) responds immediately without child widget", async () => {
const onRespond = vi.fn();
render(
<ArtifactGateWidget
descriptor={{
id: "u1",
widget: "artifact-gate",
artifact: { kind: "cte", content: "SELECT 1" },
options: [
{ id: "approve", label: "Approva" },
{
id: "reject",
label: "Rifiuta",
opens: { id: "u1c", widget: "freetext", title: "Motivazione" },
},
],
}}
onRespond={onRespond}
/>
);
await userEvent.click(screen.getByRole("button", { name: /Approva/ }));
expect(onRespond).toHaveBeenCalledWith({ id: "u1", kind: "artifact-gate", choices: ["approve"] });
// No textbox should appear
expect(screen.queryByRole("textbox")).not.toBeInTheDocument();
});