import { test, expect, beforeEach } from "bun:test";
import { Window } from "happy-dom";
import { NAV_RUNTIME } from "../src/nav-runtime.ts";
let win: any;
let fetchCalls: { url: string; opts: any }[];
let nextHtml: string;
function install(bodyHtml: string): void {
win = new Window({ url: "https://example.test/" });
win.document.body.innerHTML = bodyHtml;
fetchCalls = [];
nextHtml = "";
const g = globalThis as any;
g.window = win;
g.document = win.document;
g.history = win.history;
g.location = win.location;
g.DOMParser = win.DOMParser;
g.CustomEvent = win.CustomEvent;
g.Event = win.Event;
g.fetch = win.fetch = (url: string, opts: any) => {
fetchCalls.push({ url, opts });
return Promise.resolve({
redirected: false,
url,
headers: {
get: (k: string) =>
k.toLowerCase() === "content-type" ? "text/html; charset=utf-8" : null,
},
text: () => Promise.resolve(nextHtml),
});
};
(0, eval)(NAV_RUNTIME);
}
const flush = () => new Promise((r) => setTimeout(r, 0));
beforeEach(() => {
const g = globalThis as any;
for (const k of [
"window",
"document",
"history",
"location",
"DOMParser",
"CustomEvent",
"Event",
"fetch",
]) {
delete g[k];
}
});
test("intercepts an internal link click and swaps #app in place", async () => {
install(`
`);
nextHtml =
`About ` +
`
About page `;
win.document.getElementById("lnk").click();
await flush();
expect(fetchCalls.length).toBe(1);
expect(fetchCalls[0]!.url).toContain("/about");
expect(fetchCalls[0]!.opts.headers["x-wrnexus-nav"]).toBe("1");
expect(win.document.getElementById("app").innerHTML).toContain("About page");
expect(win.document.title).toBe("About");
});
test("prefetches focused routes once and reuses the document during navigation", async () => {
install(``);
nextHtml =
`About ` +
`
Prefetched `;
const link = win.document.getElementById("prefetch");
link.dispatchEvent(new win.FocusEvent("focusin", { bubbles: true }));
await flush();
expect(fetchCalls).toHaveLength(1);
expect(fetchCalls[0]?.opts.headers["x-wrnexus-prefetch"]).toBe("1");
link.click();
await flush();
expect(fetchCalls).toHaveLength(1);
expect(win.document.querySelector("h1")?.textContent).toBe("Prefetched");
});
test("does not prefetch links merely passing under the pointer while scrolling", async () => {
install(``);
const link = win.document.getElementById("prefetch");
link.dispatchEvent(new win.PointerEvent("pointerover", { bubbles: true }));
await flush();
expect(fetchCalls).toHaveLength(0);
link.dispatchEvent(new win.PointerEvent("pointerdown", { bubbles: true }));
await flush();
expect(fetchCalls).toHaveLength(1);
expect(fetchCalls[0]?.opts.headers["x-wrnexus-prefetch"]).toBe("1");
});
test("ignores cross-origin links (full navigation)", async () => {
install(``);
win.document.getElementById("lnk").click();
await flush();
expect(fetchCalls.length).toBe(0);
});
test("ignores modified clicks so new-tab still works", async () => {
install(``);
const MouseEventConstructor = (win as unknown as { MouseEvent: typeof MouseEvent }).MouseEvent;
win.document.getElementById("lnk").dispatchEvent(
new MouseEventConstructor("click", {
bubbles: true,
cancelable: true,
button: 0,
metaKey: true,
}),
);
await flush();
expect(fetchCalls.length).toBe(0);
});
test("exposes programmatic navigation", async () => {
install(`
Home `);
nextHtml = `Dash
Dashboard `;
expect(typeof win.__wrnexusNavigate).toBe("function");
win.__wrnexusNavigate("/dashboard");
await flush();
expect(fetchCalls.length).toBe(1);
expect(win.document.getElementById("app").innerHTML).toContain("Dashboard");
});
test("rebinds theme controls after swapping the page", async () => {
install(``);
let boundRoot: unknown;
win.wrnTheme = { bind: (root: unknown) => (boundRoot = root) };
nextHtml = `Theme
`;
win.document.getElementById("lnk").click();
await flush();
expect(boundRoot).toBe(win.document);
expect(win.document.querySelector("[data-wrn-theme-toggle]")).not.toBeNull();
});
test("synchronizes and rebinds i18n data during client navigation", async () => {
install(``);
let boundRoot: unknown;
const translate = () => "translated";
const setLanguage = () => true;
win.__wrnI18n = { lang: "en", messages: { old: "Old" }, t: translate, set: setLanguage };
win.__wrnLang = { bind: (root: unknown) => (boundRoot = root) };
nextHtml =
`` +
`` +
``;
win.document.getElementById("lnk").click();
await flush();
expect(win.__wrnI18n.lang).toBe("mr");
expect(win.__wrnI18n.messages.home.title).toBe("नवीन");
expect(win.__wrnI18n.t).toBe(translate);
expect(win.__wrnI18n.set).toBe(setLanguage);
expect(boundRoot).toBe(win.document.getElementById("app"));
});
test("preserves same-language translations when an incoming navigation catalog is partial", async () => {
install(``);
win.__wrnI18n = {
lang: "en",
messages: { navigation: { home: "Home" }, footer: { contact: "Contact" } },
fallbackMessages: {},
};
win.__wrnLang = {
bind: (root: ParentNode) => {
root.querySelectorAll("[data-t]").forEach((node) => {
const parts = String(node.getAttribute("data-t") || "").split(".");
let value: any = win.__wrnI18n.messages;
for (const part of parts) value = value?.[part];
node.textContent = typeof value === "string" ? value : node.getAttribute("data-t");
});
},
};
nextHtml =
`` +
`` +
``;
win.document.getElementById("lnk").click();
await flush();
expect(win.__wrnI18n.messages.navigation.home).toBe("Home");
expect(win.__wrnI18n.messages.footer.contact).toBe("Contact");
expect(win.document.querySelector("[data-t]")?.textContent).toBe("Home");
});
test("unmounts and remounts package runtimes during client navigation", async () => {
install(
``,
);
let mounts = 0;
let unmounts = 0;
win.__wrnexusRuntimes = {
captcha: {
mount: () => mounts++,
unmount: () => unmounts++,
},
};
nextHtml = ``;
win.document.getElementById("lnk").click();
await flush();
expect(unmounts).toBe(1);
expect(mounts).toBe(1);
expect(win.document.getElementById("app").textContent).toContain("New");
});
test("synchronizes page styles during client navigation and preserves the current nonce", async () => {
install(
`` +
`` +
``,
);
nextHtml =
`` +
`` +
`
About `;
win.document.getElementById("lnk").click();
await flush();
expect(win.document.querySelector('style[data-wrnexus-style-id="home"]')).toBeNull();
const style = win.document.querySelector('style[data-wrnexus-style-id="about"]');
expect(style).not.toBeNull();
expect(style.textContent).toContain("color:blue");
expect(style.getAttribute("nonce")).toBe("current-nonce");
});
test("restores declared form state but never preserves sensitive fields", async () => {
install(
` ` +
`
`,
);
nextHtml = `Next
`;
win.__wrnexusNavigate("/next");
await flush();
nextHtml =
`` +
`
`;
win.__wrnexusNavigate("/");
await flush();
expect(win.document.querySelector('[name="query"]').value).toBe("draft");
expect(win.document.querySelector('[name="password"]').value).toBe("");
});
test("KeepAlive preserves the same live DOM instance across routes", async () => {
install(
`
`,
);
const original = win.document.querySelector("[data-wrn-keepalive]");
original.runtimeState = { count: 7 };
nextHtml = `
`;
win.__wrnexusNavigate("/next");
await flush();
const restored = win.document.querySelector("[data-wrn-keepalive]");
expect(restored).toBe(original);
expect(restored.runtimeState).toEqual({ count: 7 });
expect(restored.querySelector("input").value).toBe("live");
});