import { assert } from "chai"; import { init, h, thunk, VNode } from "../../src/index"; const patch = init([]); describe("thunk", function () { let elm: any, vnode0: any; beforeEach(function () { elm = vnode0 = document.createElement("div"); }); it("returns vnode with data and render function", function () { function numberInSpan(n: number) { return h("span", `Number is ${n}`); } const vnode = thunk("span", "num", numberInSpan, [22]); assert.deepEqual(vnode.sel, "span"); assert.deepEqual(vnode.data.key, "num"); assert.deepEqual(vnode.data.args, [22]); }); it("calls render function once on data change", function () { let called = 0; function numberInSpan(n: number) { called++; return h("span", { key: "num" }, `Number is ${n}`); } const vnode1 = h("div", [thunk("span", "num", numberInSpan, [1])]); const vnode2 = h("div", [thunk("span", "num", numberInSpan, [2])]); patch(vnode0, vnode1); assert.strictEqual(called, 1); patch(vnode1, vnode2); assert.strictEqual(called, 2); }); it("does not call render function on data unchanged", function () { let called = 0; function numberInSpan(n: number) { called++; return h("span", { key: "num" }, `Number is ${n}`); } const vnode1 = h("div", [thunk("span", "num", numberInSpan, [1])]); const vnode2 = h("div", [thunk("span", "num", numberInSpan, [1])]); patch(vnode0, vnode1); assert.strictEqual(called, 1); patch(vnode1, vnode2); assert.strictEqual(called, 1); }); it("calls render function once on data-length change", function () { let called = 0; function numberInSpan(n: number) { called++; return h("span", { key: "num" }, `Number is ${n}`); } const vnode1 = h("div", [thunk("span", "num", numberInSpan, [1])]); const vnode2 = h("div", [thunk("span", "num", numberInSpan, [1, 2])]); patch(vnode0, vnode1); assert.strictEqual(called, 1); patch(vnode1, vnode2); assert.strictEqual(called, 2); }); it("calls render function once on function change", function () { let called = 0; function numberInSpan(n: number) { called++; return h("span", { key: "num" }, `Number is ${n}`); } function numberInSpan2(n: number) { called++; return h("span", { key: "num" }, `Number really is ${n}`); } const vnode1 = h("div", [thunk("span", "num", numberInSpan, [1])]); const vnode2 = h("div", [thunk("span", "num", numberInSpan2, [1])]); patch(vnode0, vnode1); assert.strictEqual(called, 1); patch(vnode1, vnode2); assert.strictEqual(called, 2); }); it("renders correctly", function () { let called = 0; function numberInSpan(n: number) { called++; return h("span", { key: "num" }, `Number is ${n}`); } const vnode1 = h("div", [thunk("span", "num", numberInSpan, [1])]); const vnode2 = h("div", [thunk("span", "num", numberInSpan, [1])]); const vnode3 = h("div", [thunk("span", "num", numberInSpan, [2])]); elm = patch(vnode0, vnode1).elm; assert.strictEqual(elm.firstChild.tagName.toLowerCase(), "span"); assert.strictEqual(elm.firstChild.innerHTML, "Number is 1"); elm = patch(vnode1, vnode2).elm; assert.strictEqual(elm.firstChild.tagName.toLowerCase(), "span"); assert.strictEqual(elm.firstChild.innerHTML, "Number is 1"); elm = patch(vnode2, vnode3).elm; assert.strictEqual(elm.firstChild.tagName.toLowerCase(), "span"); assert.strictEqual(elm.firstChild.innerHTML, "Number is 2"); assert.strictEqual(called, 2); }); it("supports leaving out the `key` argument", function () { function vnodeFn(s: string) { return h("span.number", "Hello " + s); } const vnode1 = thunk("span.number", vnodeFn, ["World!"]); elm = patch(vnode0, vnode1).elm; assert.strictEqual(elm.innerText, "Hello World!"); }); it("renders correctly when root", function () { let called = 0; function numberInSpan(n: number) { called++; return h("span", { key: "num" }, `Number is ${n}`); } const vnode1 = thunk("span", "num", numberInSpan, [1]); const vnode2 = thunk("span", "num", numberInSpan, [1]); const vnode3 = thunk("span", "num", numberInSpan, [2]); elm = patch(vnode0, vnode1).elm; assert.strictEqual(elm.tagName.toLowerCase(), "span"); assert.strictEqual(elm.innerHTML, "Number is 1"); elm = patch(vnode1, vnode2).elm; assert.strictEqual(elm.tagName.toLowerCase(), "span"); assert.strictEqual(elm.innerHTML, "Number is 1"); elm = patch(vnode2, vnode3).elm; assert.strictEqual(elm.tagName.toLowerCase(), "span"); assert.strictEqual(elm.innerHTML, "Number is 2"); assert.strictEqual(called, 2); }); it("can be replaced and removed", function () { function numberInSpan(n: number) { return h("span", { key: "num" }, `Number is ${n}`); } function oddEven(n: number): VNode { const prefix = n % 2 === 0 ? "Even" : "Odd"; return h("div", { key: oddEven as any }, `${prefix}: ${n}`); } const vnode1 = h("div", [thunk("span", "num", numberInSpan, [1])]); const vnode2 = h("div", [thunk("div", "oddEven", oddEven, [4])]); elm = patch(vnode0, vnode1).elm; assert.strictEqual(elm.firstChild.tagName.toLowerCase(), "span"); assert.strictEqual(elm.firstChild.innerHTML, "Number is 1"); elm = patch(vnode1, vnode2).elm; assert.strictEqual(elm.firstChild.tagName.toLowerCase(), "div"); assert.strictEqual(elm.firstChild.innerHTML, "Even: 4"); }); it("can be replaced and removed when root", function () { function numberInSpan(n: number) { return h("span", { key: "num" }, `Number is ${n}`); } function oddEven(n: number): VNode { const prefix = n % 2 === 0 ? "Even" : "Odd"; return h("div", { key: oddEven as any }, `${prefix}: ${n}`); } const vnode1 = thunk("span", "num", numberInSpan, [1]); const vnode2 = thunk("div", "oddEven", oddEven, [4]); elm = patch(vnode0, vnode1).elm; assert.strictEqual(elm.tagName.toLowerCase(), "span"); assert.strictEqual(elm.innerHTML, "Number is 1"); elm = patch(vnode1, vnode2).elm; assert.strictEqual(elm.tagName.toLowerCase(), "div"); assert.strictEqual(elm.innerHTML, "Even: 4"); }); it("invokes destroy hook on thunks", function () { let called = 0; function destroyHook() { called++; } function numberInSpan(n: number) { return h( "span", { key: "num", hook: { destroy: destroyHook } }, `Number is ${n}` ); } const vnode1 = h("div", [ h("div", "Foo"), thunk("span", "num", numberInSpan, [1]), h("div", "Foo"), ]); const vnode2 = h("div", [h("div", "Foo"), h("div", "Foo")]); patch(vnode0, vnode1); patch(vnode1, vnode2); assert.strictEqual(called, 1); }); it("invokes remove hook on thunks", function () { let called = 0; function hook() { called++; } function numberInSpan(n: number) { return h( "span", { key: "num", hook: { remove: hook } }, `Number is ${n}` ); } const vnode1 = h("div", [ h("div", "Foo"), thunk("span", "num", numberInSpan, [1]), h("div", "Foo"), ]); const vnode2 = h("div", [h("div", "Foo"), h("div", "Foo")]); patch(vnode0, vnode1); patch(vnode1, vnode2); assert.strictEqual(called, 1); }); });