106 lines
4.0 KiB
TypeScript
106 lines
4.0 KiB
TypeScript
// The approval queue's markup: the EXACT stored proposed arguments are rendered (never a
|
|
// narration), the Art. 50 disclosure rides with them, and the controls are named for a keyboard.
|
|
|
|
import { buildRegistry } from "@conusai-kit/core"
|
|
import { createI18n } from "@conusai-kit/i18n"
|
|
import { TransportProvider } from "@conusai-kit/ui"
|
|
import { renderToStaticMarkup } from "react-dom/server"
|
|
import { describe, expect, it } from "vitest"
|
|
import { appModules } from "../../index.js"
|
|
import type { ApprovalView } from "../contract/index.js"
|
|
import { ApprovalQueue } from "../ui/approval-queue.js"
|
|
import { RunControls } from "../ui/run-controls.js"
|
|
|
|
const catalogues = buildRegistry([...appModules]).catalogues
|
|
const i18n = createI18n("en", { en: { agent: catalogues.en?.agent ?? {} } })
|
|
const t = (key: string, values?: Record<string, string | number>) => i18n.message(`agent.${key}`, values)
|
|
const failed = "That did not work: {detail}"
|
|
const slugs = { org: "acme", workspace: "ws", project: "p" }
|
|
|
|
const inProvider = (node: React.ReactNode) =>
|
|
renderToStaticMarkup(
|
|
<TransportProvider call={async () => undefined} describe={() => "x"}>
|
|
{node}
|
|
</TransportProvider>,
|
|
)
|
|
|
|
const approval: ApprovalView = {
|
|
runId: "r1",
|
|
stepKey: "read",
|
|
goal: "Review the contract",
|
|
tool: "orders.create",
|
|
arguments: { total: 4200, currency: "EUR" },
|
|
reviewUrl: "https://console/tool-approvals?action=act-1",
|
|
createdAt: "2026-10-23T00:00:00.000Z",
|
|
}
|
|
|
|
const labels = {
|
|
heading: t("approvals.heading"),
|
|
empty: t("approvals.empty"),
|
|
proposed: t("approvals.proposed"),
|
|
review: t("approvals.review"),
|
|
approve: t("approvals.approve"),
|
|
reject: t("approvals.reject"),
|
|
decided: t("approvals.decided"),
|
|
disclosure: t("approvals.disclosure"),
|
|
failed,
|
|
}
|
|
|
|
describe("the approval queue", () => {
|
|
const html = inProvider(<ApprovalQueue slugs={slugs} approvals={[approval]} labels={labels} />)
|
|
|
|
it("renders the exact stored proposed arguments, pretty-printed, with the disclosure", () => {
|
|
// The exact stored arguments, HTML-escaped as any rendered text is.
|
|
expect(html).toContain(""total": 4200")
|
|
expect(html).toContain(""currency": "EUR"")
|
|
expect(html).toContain(t("approvals.disclosure"))
|
|
expect(html).toContain(t("approvals.proposed"))
|
|
})
|
|
|
|
it("names the decision controls and links the console review", () => {
|
|
expect(html).toContain(`>${t("approvals.approve")}<`)
|
|
expect(html).toContain(`>${t("approvals.reject")}<`)
|
|
expect(html).toContain('href="https://console/tool-approvals?action=act-1"')
|
|
})
|
|
|
|
it("announces an empty queue", () => {
|
|
const empty = inProvider(<ApprovalQueue slugs={slugs} approvals={[]} labels={labels} />)
|
|
expect(empty).toContain(t("approvals.empty"))
|
|
})
|
|
})
|
|
|
|
describe("the run controls", () => {
|
|
const labels = {
|
|
cancel: t("runs.cancel"),
|
|
cancelling: t("runs.cancelling"),
|
|
refresh: t("runs.refresh"),
|
|
failedHint: t("recovery.failedHint"),
|
|
cancelledHint: t("recovery.cancelledHint"),
|
|
uncertainHint: t("recovery.uncertainHint"),
|
|
budgetExhausted: t("recovery.budgetExhausted"),
|
|
approvalUnavailable: t("recovery.approvalUnavailable"),
|
|
toolError: t("recovery.toolError"),
|
|
planRefused: t("recovery.planRefused"),
|
|
internal: t("recovery.internal"),
|
|
}
|
|
|
|
it("offers cancel on an active run and recovery copy on an uncertain one", () => {
|
|
const active = inProvider(
|
|
<RunControls slugs={slugs} run={{ runId: "r1", status: "running", failureCode: null }} labels={labels} />,
|
|
)
|
|
expect(active).toContain(`>${t("runs.cancel")}<`)
|
|
const uncertain = inProvider(
|
|
<RunControls slugs={slugs} run={{ runId: "r1", status: "uncertain", failureCode: null }} labels={labels} />,
|
|
)
|
|
expect(uncertain).toContain(
|
|
t("recovery.uncertainHint")
|
|
.replaceAll("&", "&")
|
|
.replaceAll("<", "<")
|
|
.replaceAll(">", ">")
|
|
.replaceAll(String.fromCharCode(34), """)
|
|
.replaceAll(String.fromCharCode(39), "'"),
|
|
)
|
|
expect(uncertain).not.toContain(`>${t("runs.cancel")}<`)
|
|
})
|
|
})
|