// The request flow every form shares: a refusal becomes a problem, a failure // to send becomes a problem, a success parses its reply. import assert from "node:assert/strict"; import { test } from "node:test"; import { submitOutcome } from "../src/ui/submit/submit.ts"; const problem = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/problem+json" }, }); test("an ok reply is done, with nothing parsed by default", async () => { const out = await submitOutcome( async () => new Response(null, { status: 204 }), ); assert.deepEqual(out, { status: "done", value: undefined }); }); test("an ok reply is parsed into the done value", async () => { const out = await submitOutcome( async () => new Response(JSON.stringify({ node_id: 7 })), { parse: async (r) => (await r.json()).node_id }, ); assert.deepEqual(out, { status: "done", value: 7 }); }); test("a refusal carries the server's problem", async () => { const out = await submitOutcome(async () => problem(409, { title: "conflict", detail: "already linked" }), ); assert.equal(out.status, "error"); assert.equal(out.problem.detail, "already linked"); }); test("refine can reword a refusal by status", async () => { const out = await submitOutcome(async () => problem(404, {}), { refine: (r, p) => r.status === 404 && !p.detail ? { ...p, detail: "no such agent" } : p, }); assert.equal(out.status, "error"); assert.equal(out.problem.detail, "no such agent"); }); test("a request that throws is an error with the message", async () => { const out = await submitOutcome(async () => { throw new Error("offline"); }); assert.equal(out.status, "error"); assert.match(out.problem.detail, /offline/); }); test("a parse that throws is an error, not a done", async () => { const out = await submitOutcome(async () => new Response("not json"), { parse: (r) => r.json(), }); assert.equal(out.status, "error"); });