80 lines
2.4 KiB
TypeScript
80 lines
2.4 KiB
TypeScript
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();
|
|
});
|