166 lines
5.7 KiB
TypeScript
166 lines
5.7 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { h, createComponent, createRoot } from "../src/core/h.js";
|
|
import { createRoot as createHostRoot } from "../src/host/config.js";
|
|
import type { HostConfig } from "../src/host/config.js";
|
|
import type { Fiber } from "../src/host/fiber.js";
|
|
|
|
function makeHost(): {
|
|
host: HostConfig<string, string, Record<string, unknown>>;
|
|
instances: { tag: string; props: Record<string, unknown> }[];
|
|
texts: string[];
|
|
appends: { parent: string; child: string }[];
|
|
} {
|
|
const instances: { tag: string; props: Record<string, unknown> }[] = [];
|
|
const texts: string[] = [];
|
|
const appends: { parent: string; child: string }[] = [];
|
|
const host: HostConfig<string, string, Record<string, unknown>> = {
|
|
name: "test",
|
|
createRootContext: () => ({}),
|
|
createInstance: (tag, props) => {
|
|
instances.push({ tag, props });
|
|
return tag;
|
|
},
|
|
createTextInstance: (text) => {
|
|
texts.push(text);
|
|
return text;
|
|
},
|
|
appendChild: (parent, child) => {
|
|
appends.push({ parent, child });
|
|
},
|
|
};
|
|
return { host, instances, texts, appends };
|
|
}
|
|
|
|
describe("mountWithFibers: rootFiber after render()", () => {
|
|
it("rootFiber is null before render", () => {
|
|
const { host } = makeHost();
|
|
const root = createHostRoot(host, {});
|
|
expect(root.rootFiber).toBeNull();
|
|
});
|
|
|
|
it("rootFiber is set after render", () => {
|
|
const { host } = makeHost();
|
|
const root = createHostRoot(host, {});
|
|
root.render(h("div", null, "hello"));
|
|
expect(root.rootFiber).not.toBeNull();
|
|
});
|
|
|
|
it("rootFiber has correct children for simple element with text", () => {
|
|
const { host } = makeHost();
|
|
const root = createHostRoot(host, {});
|
|
root.render(h("div", { class: "outer" }, "hello"));
|
|
|
|
const rf = root.rootFiber!;
|
|
expect(rf.tag).toBe("#root");
|
|
expect(rf.parent).toBeNull();
|
|
expect(rf.children.length).toBe(1);
|
|
|
|
const divFiber = rf.children[0]!;
|
|
expect(divFiber.tag).toBe("div");
|
|
expect(divFiber.props.class).toBe("outer");
|
|
expect(divFiber.parent).toBe(rf);
|
|
expect(divFiber.children.length).toBe(1);
|
|
|
|
const textFiber = divFiber.children[0]!;
|
|
expect(textFiber.tag).toBe("#text");
|
|
expect(textFiber.props.text).toBe("hello");
|
|
expect(textFiber.parent).toBe(divFiber);
|
|
expect(textFiber.children.length).toBe(0);
|
|
});
|
|
|
|
it("primitives get text fibers with tag #text", () => {
|
|
const { host } = makeHost();
|
|
const root = createHostRoot(host, {});
|
|
root.render(h("p", null, "world"));
|
|
|
|
const textFiber = root.rootFiber!.children[0]!.children[0]!;
|
|
expect(textFiber.tag).toBe("#text");
|
|
expect(textFiber.instance).toBe("world");
|
|
});
|
|
|
|
it("fiber tree linked with parent and children", () => {
|
|
const { host } = makeHost();
|
|
const root = createHostRoot(host, {});
|
|
root.render(h("div", null, h("span", null, "a"), h("em", null, "b")));
|
|
|
|
const divFiber = root.rootFiber!.children[0]!;
|
|
expect(divFiber.parent).toBe(root.rootFiber);
|
|
expect(divFiber.children.length).toBe(2);
|
|
|
|
const spanFiber = divFiber.children[0]!;
|
|
const emFiber = divFiber.children[1]!;
|
|
expect(spanFiber.parent).toBe(divFiber);
|
|
expect(emFiber.parent).toBe(divFiber);
|
|
expect(spanFiber.tag).toBe("span");
|
|
expect(emFiber.tag).toBe("em");
|
|
});
|
|
|
|
it("function components are transparent — no fiber for component", () => {
|
|
const { host } = makeHost();
|
|
const MyComp = createComponent("MyComp", (props) => h("section", null, props.text as string));
|
|
const root = createHostRoot(host, {});
|
|
root.render(h(MyComp, { text: "hi" }));
|
|
|
|
const rf = root.rootFiber!;
|
|
expect(rf.children.length).toBe(1);
|
|
expect(rf.children[0]!.tag).toBe("section");
|
|
expect(rf.children[0]!.parent).toBe(rf);
|
|
});
|
|
|
|
it("URoot children are mounted into parent fiber (root is transparent)", () => {
|
|
const { host } = makeHost();
|
|
const root = createHostRoot(host, {});
|
|
const uRoot = createRoot("r", h("div", null, "x"), h("span", null, "y"));
|
|
root.render(uRoot);
|
|
|
|
const rf = root.rootFiber!;
|
|
expect(rf.children.length).toBe(2);
|
|
expect(rf.children[0]!.tag).toBe("div");
|
|
expect(rf.children[1]!.tag).toBe("span");
|
|
expect(rf.children[0]!.parent).toBe(rf);
|
|
expect(rf.children[1]!.parent).toBe(rf);
|
|
});
|
|
|
|
it("key is propagated to fiber", () => {
|
|
const { host } = makeHost();
|
|
const root = createHostRoot(host, {});
|
|
root.render(h("li", { key: "a" }, "item"));
|
|
|
|
const liFiber = root.rootFiber!.children[0]!;
|
|
expect(liFiber.key).toBe("a");
|
|
});
|
|
|
|
it("signalDisposers initialized as empty array on each fiber", () => {
|
|
const { host } = makeHost();
|
|
const root = createHostRoot(host, {});
|
|
root.render(h("div", null, "x"));
|
|
|
|
const rf = root.rootFiber!;
|
|
expect(rf.signalDisposers).toEqual([]);
|
|
expect(rf.children[0]!.signalDisposers).toEqual([]);
|
|
expect(rf.children[0]!.children[0]!.signalDisposers).toEqual([]);
|
|
});
|
|
|
|
it("effect initialized as null on each fiber", () => {
|
|
const { host } = makeHost();
|
|
const root = createHostRoot(host, {});
|
|
root.render(h("div", null, "x"));
|
|
|
|
const rf = root.rootFiber!;
|
|
expect(rf.effect).toBeNull();
|
|
expect(rf.children[0]!.effect).toBeNull();
|
|
expect(rf.children[0]!.children[0]!.effect).toBeNull();
|
|
});
|
|
|
|
it("existing mount behavior preserved — hosts receive same createInstance/appendChild calls", () => {
|
|
const { host, instances, texts, appends } = makeHost();
|
|
const root = createHostRoot(host, {});
|
|
root.render(h("div", { class: "outer" }, "hello", h("span", null, "world")));
|
|
|
|
expect(instances.length).toBe(2);
|
|
expect(instances[0]!.tag).toBe("div");
|
|
expect(instances[1]!.tag).toBe("span");
|
|
expect(texts.sort()).toEqual(["hello", "world"]);
|
|
expect(appends.length).toBe(3);
|
|
});
|
|
}); |