import { expect, test } from "bun:test"; import { readFileSync } from "node:fs"; import { Window, type HTMLInputElement, type HTMLElement } from "happy-dom"; test("selecting delivery radios sends once, ignores stale responses, and never sends for local methods", async () => { const window = new Window({ url: "https://sso.example.test/mfa" }); try { window.sessionStorage.setItem( "wrnexus.auth.mfa", JSON.stringify({ mfaToken: "synthetic-test-challenge" }), ); window.document.body.innerHTML = `
${["totp", "recovery-code", "email-otp", "sms-otp"].map((value) => ``).join("")}

`; const requests: Array<{ method: string; resolve: (response: Response) => void }> = []; window.fetch = ((_: unknown, init: RequestInit) => new Promise((resolve) => { requests.push({ method: JSON.parse(String(init.body)).method, resolve }); })) as any; window.eval(readFileSync(new URL("../assets/client/auth.js", import.meta.url), "utf8")); (window as any).WRNexusAuth.mount(window.document); const form = window.document.querySelector("form")!; const field = (name: string) => form.querySelector(`[name=${name}]`)!; const choose = (value: string) => { const radio = form.querySelector(`[name=method][value="${value}"]`)!; radio.checked = true; radio.dispatchEvent(new window.Event("change", { bubbles: true })); }; choose("recovery-code"); expect(requests).toHaveLength(0); field("recoveryCode").value = "abc123-def456"; form.dispatchEvent(new window.Event("submit", { bubbles: true, cancelable: true })); expect(field("code").value).toBe("abc123-def456"); choose("email-otp"); choose("email-otp"); expect(requests.map((x) => x.method)).toEqual(["email-otp"]); choose("sms-otp"); expect(requests.map((x) => x.method)).toEqual(["email-otp", "sms-otp"]); requests[0]!.resolve(Response.json({ id: "email-challenge" })); await new Promise((resolve) => setTimeout(resolve, 0)); expect(field("challengeId").value).toBe(""); requests[1]!.resolve(Response.json({ id: "sms-challenge" })); await new Promise((resolve) => setTimeout(resolve, 0)); expect(field("challengeId").value).toBe("sms-challenge"); choose("totp"); expect(field("challengeId").value).toBe(""); expect(requests).toHaveLength(2); field("otpCode").value = "123456"; form.dispatchEvent(new window.Event("submit", { bubbles: true, cancelable: true })); expect(field("code").value).toBe("123456"); } finally { await window.happyDOM.close(); } }); test("MFA radios preserve allowed methods and restore continuation without reviving expired challenges", async () => { const window = new Window({ url: "https://sso.example.test/mfa" }); try { window.sessionStorage.setItem( "wrnexus.auth.mfa", JSON.stringify({ mfaToken: "synthetic-test-challenge", requires: { mfa: ["totp"] }, }), ); window.document.body.innerHTML = `

`; window.eval(readFileSync(new URL("../assets/client/auth.js", import.meta.url), "utf8")); (window as any).WRNexusAuth.mount(window.document); const form = window.document.querySelector("form")!; const token = form.querySelector('[name="mfaToken"]')!; expect(token.value).toBe("synthetic-test-challenge"); expect(form.querySelector('input[value="email-otp"]')!.disabled).toBe(true); token.value = ""; // Hydration reapplies the component's initial empty prop. form.dispatchEvent(new window.Event("submit", { bubbles: true, cancelable: true })); expect(token.value).toBe("synthetic-test-challenge"); form.dispatchEvent( new window.CustomEvent("wrn:error", { bubbles: true, detail: { code: "mfa-invalid" } }), ); expect(token.value).toBe("synthetic-test-challenge"); form.dispatchEvent( new window.CustomEvent("wrn:error", { bubbles: true, detail: { code: "mfa-transaction-expired" }, }), ); expect(window.sessionStorage.getItem("wrnexus.auth.mfa")).toBeNull(); expect(window.document.querySelector("[data-auth-mfa-restart]")!.hidden).toBe( false, ); const retry = new window.Event("submit", { bubbles: true, cancelable: true }); form.dispatchEvent(retry); expect(retry.defaultPrevented).toBe(true); expect(token.value).toBe(""); } finally { await window.happyDOM.close(); } });