/**
* PROPOSED: joinPasteChip flows not covered by test/dom.test.mjs —
* keyboard apply/collapse, the paste-intent timer, the #g= capture group,
* the host-boundary security check (evil-ardegazu.ro must NOT navigate),
* subdomain navigation, and the secret length bounds.
* Runs against the committed dist/ on the repo's own fake DOM.
*/
import test from "node:test";
import assert from "node:assert/strict";
import { installFakeDom } from "./helpers/fake-dom.mjs";
const env = installFakeDom();
const { joinPasteChip } = await import("../dist/join.js");
const { enText } = await import("../dist/index.js");
const tick = (ms = 0) => new Promise((r) => setTimeout(r, ms));
const SECRET = "C".repeat(43);
test("joinPasteChip: keyboard, paste timer, #g=, host boundary, length bounds", async () => {
joinPasteChip();
const chip = env.document.querySelector(".soc-joinchip");
chip.dispatch("click");
let input = chip.querySelector("input");
assert.ok(input, "expanded");
// Enter applies: bare secret -> same-origin hash + reload
input.value = SECRET;
input.dispatch("keydown", { key: "Enter" });
assert.equal(env.location.hash, SECRET, "Enter applied the secret");
assert.equal(env.location.reloaded, 1);
// Escape collapses back to the label span; a later click re-expands
input.dispatch("keydown", { key: "Escape" });
assert.equal(chip.querySelector("input"), null, "collapsed");
assert.equal(chip.querySelector("span").textContent, enText("social.paste.chip"), "label restored");
chip.dispatch("click");
input = chip.querySelector("input");
assert.ok(input, "re-expanded after collapse");
// paste = intent: apply fires ~50ms after the paste event
input.value = "D".repeat(43);
input.dispatch("paste");
assert.equal(env.location.hash, SECRET, "not yet applied at t=0");
await tick(80);
assert.equal(env.location.hash, "D".repeat(43), "applied after the 50ms intent timer");
assert.equal(env.location.reloaded, 2);
// #g= form: the capture group strips the g= prefix (same-host link)
input.value = `https://game1.ardegazu.ro/#g=${SECRET}`;
input.dispatch("keydown", { key: "Enter" });
assert.equal(env.location.hash, SECRET, "g= prefix stripped, secret only");
assert.equal(env.location.reloaded, 3);
const hrefBefore = env.location.href;
// SECURITY: evil-ardegazu.ro is NOT a suite host — must not navigate
input.value = `https://evil-ardegazu.ro/#${"E".repeat(43)}`;
input.dispatch("keydown", { key: "Enter" });
assert.equal(env.location.href, hrefBefore, "no navigation to a non-suite host");
assert.equal(env.location.hash, "E".repeat(43), "treated as this-app secret instead");
assert.equal(env.location.reloaded, 4);
// a real subdomain of ardegazu.ro DOES navigate
const sub = `https://x.ardegazu.ro/#${"F".repeat(43)}`;
input.value = sub;
input.dispatch("keydown", { key: "Enter" });
assert.equal(env.location.href, sub, "subdomain suite host navigates");
// length bounds: 21 chars is junk...
input.value = "G".repeat(21);
input.dispatch("keydown", { key: "Enter" });
assert.equal(input.value, "", "short secret rejected");
assert.equal(input.placeholder, enText("social.paste.error"));
// ...and 65 chars is junk too (the secret must be a WHOLE token)
input.value = "H".repeat(65);
input.dispatch("keydown", { key: "Enter" });
assert.equal(input.value, "", "overlong secret rejected");
// 22 chars is the smallest accepted secret
input.value = "J".repeat(22);
input.dispatch("keydown", { key: "Enter" });
assert.equal(env.location.hash, "J".repeat(22), "22-char secret accepted");
// empty input is a silent no-op (no error placeholder churn)
input.value = " ";
input.placeholder = "sentinel";
input.dispatch("keydown", { key: "Enter" });
assert.equal(input.placeholder, "sentinel", "whitespace-only input ignored");
});