import { describe, expect, test } from "bun:test"; import { Window } from "happy-dom"; import { createContextProvider, historySignal, mountPortal, transition, createTimeline, urlSignal, } from "../src/index.ts"; describe("advanced reactive primitives", () => { test("supports bounded undo and redo", () => { const value = historySignal(0, { limit: 2 }); value.set(1); value.set(2); value.set(3); expect(value.undo()).toBe(true); expect(value.get()).toBe(2); expect(value.undo()).toBe(true); expect(value.get()).toBe(1); expect(value.undo()).toBe(false); expect(value.redo()).toBe(true); expect(value.get()).toBe(2); }); test("synchronizes URL state through an explicit adapter", () => { let changed = ""; const value = urlSignal("all", { url: new URL("https://example.test/?filter=open"), parameter: "filter", parse: (input) => input ?? "all", replace: (url) => { changed = url.href; }, }); expect(value.get()).toBe("open"); value.set("closed"); expect(changed).toBe("https://example.test/?filter=closed"); }); test("provides scoped context and cleans up portals and transitions", async () => { const context = createContextProvider("default"); expect(context.provide("tenant", () => context.use())).toBe("tenant"); expect(context.use()).toBe("default"); const window = new Window(); const target = window.document.createElement("div"); const cleanup = mountPortal("hello", target as unknown as Element); expect(target.textContent).toBe("hello"); cleanup(); expect(target.textContent).toBe(""); await transition(() => target.setAttribute("ready", ""), { target: target as unknown as Element, className: "enter", }); expect(target.classList.contains("enter")).toBe(false); }); }); test("animation timelines sequence bounded forward and reverse steps", async () => { let clock = 0; const values: number[] = []; const timeline = createTimeline( [{ durationMs: 10, update: (progress) => values.push(progress) }], { now: () => clock, frame(callback) { clock += 5; queueMicrotask(callback); }, }, ); await timeline.play(); expect(values.at(-1)).toBe(1); values.length = 0; clock = 0; await timeline.play({ reverse: true }); expect(values[0]).toBe(1); expect(values.at(-1)).toBe(0); expect(timeline.running).toBeFalse(); });