mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
5335fb8fba
part of #194
1107 lines
41 KiB
TypeScript
1107 lines
41 KiB
TypeScript
import { h, patch } from "../src/vdom";
|
|
import { init, addNS } from "../src/vdom/vdom";
|
|
|
|
function map(list, fn) {
|
|
var ret: any[] = [];
|
|
for (var i = 0; i < list.length; ++i) {
|
|
ret[i] = fn(list[i]);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Attributes
|
|
//------------------------------------------------------------------------------
|
|
describe("attributes", function() {
|
|
let elm, vnode0;
|
|
beforeEach(function() {
|
|
elm = document.createElement("div");
|
|
vnode0 = elm;
|
|
});
|
|
|
|
test("have their provided values", function() {
|
|
const vnode1 = h("div", {
|
|
attrs: { href: "/foo", minlength: 1, selected: true, disabled: false }
|
|
});
|
|
elm = patch(vnode0, vnode1).elm;
|
|
|
|
expect(elm.getAttribute("href")).toBe("/foo");
|
|
expect(elm.getAttribute("minlength")).toBe("1");
|
|
expect(elm.hasAttribute("selected")).toBe(true);
|
|
expect(elm.getAttribute("selected")).toBe("");
|
|
expect(elm.hasAttribute("disabled")).toBe(false);
|
|
});
|
|
|
|
test("can be memoized", function() {
|
|
const cachedAttrs = { href: "/foo", minlength: 1, selected: true };
|
|
const vnode1 = h("div", { attrs: cachedAttrs });
|
|
const vnode2 = h("div", { attrs: cachedAttrs });
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.getAttribute("href")).toBe("/foo");
|
|
expect(elm.getAttribute("minlength")).toBe("1");
|
|
expect(elm.getAttribute("selected")).toBe("");
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.getAttribute("href")).toBe("/foo");
|
|
expect(elm.getAttribute("minlength")).toBe("1");
|
|
expect(elm.getAttribute("selected")).toBe("");
|
|
});
|
|
|
|
test("are not omitted when falsy values are provided", function() {
|
|
const vnode1 = h("div", <any>{
|
|
attrs: { href: null, minlength: 0, value: "", title: "undefined" }
|
|
});
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.getAttribute("href")).toBe("null");
|
|
expect(elm.getAttribute("minlength")).toBe("0");
|
|
expect(elm.getAttribute("value")).toBe("");
|
|
expect(elm.getAttribute("title")).toBe("undefined");
|
|
});
|
|
|
|
test("are set correctly when namespaced", function() {
|
|
const vnode1 = h("div", { attrs: { "xlink:href": "#foo" } });
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.getAttributeNS("http://www.w3.org/1999/xlink", "href")).toBe("#foo");
|
|
});
|
|
|
|
test("should not touch class nor id fields", function() {
|
|
elm = document.createElement("div");
|
|
elm.id = "myId";
|
|
elm.className = "myClass";
|
|
vnode0 = elm;
|
|
const vnode1 = h("div#myId.myClass", { attrs: {} }, ["Hello"]);
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.tagName).toBe("DIV");
|
|
expect(elm.id).toBe("myId");
|
|
expect(elm.className).toBe("myClass");
|
|
expect(elm.textContent).toBe("Hello");
|
|
});
|
|
|
|
describe("boolean attribute", function() {
|
|
test("is present and empty string if the value is truthy", function() {
|
|
const vnode1 = h("div", {
|
|
attrs: { required: true, readonly: 1, noresize: "truthy" }
|
|
});
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.hasAttribute("required")).toBe(true);
|
|
expect(elm.getAttribute("required")).toBe("");
|
|
expect(elm.hasAttribute("readonly")).toBe(true);
|
|
expect(elm.getAttribute("readonly")).toBe("1");
|
|
expect(elm.hasAttribute("noresize")).toBe(true);
|
|
expect(elm.getAttribute("noresize")).toBe("truthy");
|
|
});
|
|
|
|
test("is omitted if the value is false", function() {
|
|
const vnode1 = h("div", { attrs: { required: false } });
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.hasAttribute("required")).toBe(false);
|
|
expect(elm.getAttribute("required")).toBe(null);
|
|
});
|
|
|
|
test("is not omitted if the value is falsy but casted to string", function() {
|
|
const vnode1 = h("div", <any>{ attrs: { readonly: 0, noresize: null } });
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.getAttribute("readonly")).toBe("0");
|
|
expect(elm.getAttribute("noresize")).toBe("null");
|
|
});
|
|
});
|
|
|
|
describe("Object.prototype property", function() {
|
|
test("is not considered as a boolean attribute and shouldn't be omitted", function() {
|
|
const vnode1 = h("div", { attrs: { constructor: true } });
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.hasAttribute("constructor")).toBe(true);
|
|
expect(elm.getAttribute("constructor")).toBe("");
|
|
const vnode2 = h("div", { attrs: { constructor: false } });
|
|
elm = patch(vnode0, vnode2).elm;
|
|
expect(elm.hasAttribute("constructor")).toBe(false);
|
|
});
|
|
});
|
|
});
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Hyperscript
|
|
//------------------------------------------------------------------------------
|
|
describe("hyperscript", function() {
|
|
test("can create vnode with proper tag", function() {
|
|
expect(h("div").sel).toBe("div");
|
|
expect(h("a").sel).toBe("a");
|
|
});
|
|
|
|
test("can create vnode with children", function() {
|
|
const vnode = h("div", [h("span#hello"), h("b.world")]);
|
|
expect(vnode.sel).toBe("div");
|
|
expect((<any>vnode).children[0].sel).toBe("span#hello");
|
|
expect((<any>vnode).children[1].sel).toBe("b.world");
|
|
});
|
|
|
|
test("can create vnode with one child vnode", function() {
|
|
const vnode = h("div", h("span#hello"));
|
|
expect(vnode.sel).toBe("div");
|
|
expect((<any>vnode).children[0].sel).toBe("span#hello");
|
|
});
|
|
|
|
test("can create vnode with props and one child vnode", function() {
|
|
const vnode = h("div", {}, h("span#hello"));
|
|
expect(vnode.sel).toBe("div");
|
|
expect((<any>vnode).children[0].sel).toBe("span#hello");
|
|
});
|
|
|
|
test("can create vnode with text content", function() {
|
|
const vnode = h("a", ["I am a string"]);
|
|
expect((<any>vnode).children[0].text).toBe("I am a string");
|
|
});
|
|
|
|
test("can create vnode with text content in string", function() {
|
|
const vnode = h("a", "I am a string");
|
|
expect(vnode.text).toBe("I am a string");
|
|
});
|
|
|
|
test("can create vnode with props and text content in string", function() {
|
|
const vnode = h("a", {}, "I am a string");
|
|
expect(vnode.text).toBe("I am a string");
|
|
});
|
|
|
|
test("can create vnode for comment", function() {
|
|
const vnode = h("!", "test");
|
|
expect(vnode.sel).toBe("!");
|
|
expect(vnode.text).toBe("test");
|
|
});
|
|
});
|
|
|
|
//------------------------------------------------------------------------------
|
|
// VDOM
|
|
//------------------------------------------------------------------------------
|
|
describe("snabbdom", function() {
|
|
let elm: any, vnode0;
|
|
beforeEach(function() {
|
|
elm = document.createElement("div");
|
|
vnode0 = elm;
|
|
});
|
|
|
|
describe("created element", function() {
|
|
test("has tag", function() {
|
|
elm = patch(vnode0, h("div")).elm;
|
|
expect(elm.tagName).toBe("DIV");
|
|
});
|
|
|
|
test("has different tag and id", function() {
|
|
let elm: any = document.createElement("div");
|
|
vnode0.appendChild(elm);
|
|
const vnode1 = h("span#id");
|
|
elm = patch(elm, vnode1).elm;
|
|
expect(elm.tagName).toBe("SPAN");
|
|
expect(elm.id).toBe("id");
|
|
});
|
|
|
|
test("has id", function() {
|
|
elm = patch(vnode0, h("div", [h("div#unique")])).elm;
|
|
expect(elm.firstChild.id).toBe("unique");
|
|
});
|
|
|
|
test("has correct namespace", function() {
|
|
const SVGNamespace = "http://www.w3.org/2000/svg";
|
|
const XHTMLNamespace = "http://www.w3.org/1999/xhtml";
|
|
|
|
elm = patch(vnode0, h("div", [h("div", { ns: SVGNamespace })])).elm;
|
|
expect(elm.firstChild.namespaceURI).toBe(SVGNamespace);
|
|
|
|
// verify that svg tag automatically gets svg namespace
|
|
const vnode = h("svg", [h("foreignObject", [h("div", ["I am HTML embedded in SVG"])])]);
|
|
// need to add namespace manually. it is usually done by the template
|
|
// compiler
|
|
addNS(vnode.data, (vnode as any).children, vnode.sel);
|
|
|
|
elm = patch(vnode0, vnode).elm;
|
|
expect(elm.namespaceURI).toBe(SVGNamespace);
|
|
expect(elm.firstChild.namespaceURI).toBe(SVGNamespace);
|
|
expect(elm.firstChild.firstChild.namespaceURI).toBe(XHTMLNamespace);
|
|
});
|
|
|
|
test("receives classes in selector", function() {
|
|
elm = patch(vnode0, h("div", [h("i.am.a.class")])).elm;
|
|
expect(elm.firstChild.classList.contains("am")).toBeTruthy();
|
|
expect(elm.firstChild.classList.contains("a")).toBeTruthy();
|
|
expect(elm.firstChild.classList.contains("class")).toBeTruthy();
|
|
});
|
|
|
|
test("receives classes in selector when namespaced", function() {
|
|
elm = patch(vnode0, h("svg", [h("g.am.a.class.too")])).elm;
|
|
expect(elm.firstChild.classList.contains("am")).toBeTruthy();
|
|
expect(elm.firstChild.classList.contains("a")).toBeTruthy();
|
|
expect(elm.firstChild.classList.contains("class")).toBeTruthy();
|
|
});
|
|
|
|
test("can create elements with text content", function() {
|
|
elm = patch(vnode0, h("div", ["I am a string"])).elm;
|
|
expect(elm.innerHTML).toBe("I am a string");
|
|
});
|
|
|
|
test("can create elements with span and text content", function() {
|
|
elm = patch(vnode0, h("a", [h("span"), "I am a string"])).elm;
|
|
expect(elm.childNodes[0].tagName).toBe("SPAN");
|
|
expect(elm.childNodes[1].textContent).toBe("I am a string");
|
|
});
|
|
|
|
test("can create elements with props", function() {
|
|
elm = patch(vnode0, h("a", { props: { src: "http://localhost/" } })).elm;
|
|
expect(elm.src).toBe("http://localhost/");
|
|
});
|
|
|
|
test("is a patch of the root element", function() {
|
|
const elmWithIdAndClass = document.createElement("div");
|
|
elmWithIdAndClass.id = "id";
|
|
elmWithIdAndClass.className = "class";
|
|
const vnode1 = h("div#id.class", [h("span", "Hi")]);
|
|
elm = patch(elmWithIdAndClass, vnode1).elm;
|
|
expect(elm).toBe(elmWithIdAndClass);
|
|
expect(elm.tagName).toBe("DIV");
|
|
expect(elm.id).toBe("id");
|
|
expect(elm.className).toBe("class");
|
|
});
|
|
|
|
test("can create comments", function() {
|
|
elm = patch(vnode0, h("!", "test")).elm;
|
|
expect(elm.nodeType).toBe(document.COMMENT_NODE);
|
|
expect(elm.textContent).toBe("test");
|
|
});
|
|
});
|
|
|
|
describe("patching an element", function() {
|
|
test("changes the elements classes", function() {
|
|
const vnode1 = h("i.i.am.horse");
|
|
const vnode2 = h("i.i.am");
|
|
patch(vnode0, vnode1);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.classList.contains("i")).toBeTruthy();
|
|
expect(elm.classList.contains("am")).toBeTruthy();
|
|
expect(!elm.classList.contains("horse")).toBeTruthy();
|
|
});
|
|
|
|
test("removes missing classes", function() {
|
|
const vnode1 = h("i.i.am.horse");
|
|
const vnode2 = h("i.i.am");
|
|
patch(vnode0, vnode1);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.classList.contains("i")).toBeTruthy();
|
|
expect(elm.classList.contains("am")).toBeTruthy();
|
|
expect(!elm.classList.contains("horse")).toBeTruthy();
|
|
});
|
|
|
|
test("changes an elements props", function() {
|
|
const vnode1 = h("a", { props: { src: "http://other/" } });
|
|
const vnode2 = h("a", { props: { src: "http://localhost/" } });
|
|
patch(vnode0, vnode1);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.src).toBe("http://localhost/");
|
|
});
|
|
|
|
test("preserves memoized props", function() {
|
|
const cachedProps = { src: "http://other/" };
|
|
const vnode1 = h("a", { props: cachedProps });
|
|
const vnode2 = h("a", { props: cachedProps });
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.src).toBe("http://other/");
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.src).toBe("http://other/");
|
|
});
|
|
|
|
test("removes an elements props", function() {
|
|
const vnode1 = h("a", { props: { src: "http://other/" } });
|
|
const vnode2 = h("a");
|
|
patch(vnode0, vnode1);
|
|
patch(vnode1, vnode2);
|
|
expect(elm.src).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("updating children with keys", function() {
|
|
function spanNum(n) {
|
|
if (n == null) {
|
|
return n;
|
|
} else if (typeof n === "string") {
|
|
return h("span", {}, n);
|
|
} else {
|
|
return h("span", { key: n }, n.toString());
|
|
}
|
|
}
|
|
|
|
describe("addition of elements", function() {
|
|
test("appends elements", function() {
|
|
const vnode1 = h("span", [1].map(spanNum));
|
|
const vnode2 = h("span", [1, 2, 3].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(1);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.children.length).toBe(3);
|
|
expect(elm.children[1].innerHTML).toBe("2");
|
|
expect(elm.children[2].innerHTML).toBe("3");
|
|
});
|
|
|
|
test("prepends elements", function() {
|
|
const vnode1 = h("span", [4, 5].map(spanNum));
|
|
const vnode2 = h("span", [1, 2, 3, 4, 5].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(2);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["1", "2", "3", "4", "5"]);
|
|
});
|
|
|
|
test("add elements in the middle", function() {
|
|
const vnode1 = h("span", [1, 2, 4, 5].map(spanNum));
|
|
const vnode2 = h("span", [1, 2, 3, 4, 5].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(4);
|
|
expect(elm.children.length).toBe(4);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["1", "2", "3", "4", "5"]);
|
|
});
|
|
|
|
test("add elements at beginning and end", function() {
|
|
const vnode1 = h("span", [2, 3, 4].map(spanNum));
|
|
const vnode2 = h("span", [1, 2, 3, 4, 5].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(3);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["1", "2", "3", "4", "5"]);
|
|
});
|
|
|
|
test("adds children to parent with no children", function() {
|
|
const vnode1 = h("span", { key: "span" });
|
|
const vnode2 = h("span", { key: "span" }, [1, 2, 3].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(0);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["1", "2", "3"]);
|
|
});
|
|
|
|
test("removes all children from parent", function() {
|
|
const vnode1 = h("span", { key: "span" }, [1, 2, 3].map(spanNum));
|
|
const vnode2 = h("span", { key: "span" });
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["1", "2", "3"]);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.children.length).toBe(0);
|
|
});
|
|
|
|
test("update one child with same key but different sel", function() {
|
|
const vnode1 = h("span", { key: "span" }, [1, 2, 3].map(spanNum));
|
|
const vnode2 = h("span", { key: "span" }, [
|
|
spanNum(1),
|
|
h("i", { key: 2 }, "2"),
|
|
spanNum(3)
|
|
]);
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["1", "2", "3"]);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["1", "2", "3"]);
|
|
expect(elm.children.length).toBe(3);
|
|
expect(elm.children[1].tagName).toBe("I");
|
|
});
|
|
});
|
|
|
|
describe("removal of elements", function() {
|
|
test("removes elements from the beginning", function() {
|
|
const vnode1 = h("span", [1, 2, 3, 4, 5].map(spanNum));
|
|
const vnode2 = h("span", [3, 4, 5].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(5);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["3", "4", "5"]);
|
|
});
|
|
|
|
test("removes elements from the end", function() {
|
|
const vnode1 = h("span", [1, 2, 3, 4, 5].map(spanNum));
|
|
const vnode2 = h("span", [1, 2, 3].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(5);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.children.length).toBe(3);
|
|
expect(elm.children[0].innerHTML).toBe("1");
|
|
expect(elm.children[1].innerHTML).toBe("2");
|
|
expect(elm.children[2].innerHTML).toBe("3");
|
|
});
|
|
|
|
test("removes elements from the middle", function() {
|
|
const vnode1 = h("span", [1, 2, 3, 4, 5].map(spanNum));
|
|
const vnode2 = h("span", [1, 2, 4, 5].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(5);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.children.length).toBe(4);
|
|
expect(elm.children[0].innerHTML).toBe("1");
|
|
expect(elm.children[1].innerHTML).toBe("2");
|
|
expect(elm.children[2].innerHTML).toBe("4");
|
|
expect(elm.children[3].innerHTML).toBe("5");
|
|
});
|
|
});
|
|
|
|
describe("element reordering", function() {
|
|
test("moves element forward", function() {
|
|
const vnode1 = h("span", [1, 2, 3, 4].map(spanNum));
|
|
const vnode2 = h("span", [2, 3, 1, 4].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(4);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.children.length).toBe(4);
|
|
expect(elm.children[0].innerHTML).toBe("2");
|
|
expect(elm.children[1].innerHTML).toBe("3");
|
|
expect(elm.children[2].innerHTML).toBe("1");
|
|
expect(elm.children[3].innerHTML).toBe("4");
|
|
});
|
|
|
|
test("moves element to end", function() {
|
|
const vnode1 = h("span", [1, 2, 3].map(spanNum));
|
|
const vnode2 = h("span", [2, 3, 1].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(3);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.children.length).toBe(3);
|
|
expect(elm.children[0].innerHTML).toBe("2");
|
|
expect(elm.children[1].innerHTML).toBe("3");
|
|
expect(elm.children[2].innerHTML).toBe("1");
|
|
});
|
|
|
|
test("moves element backwards", function() {
|
|
const vnode1 = h("span", [1, 2, 3, 4].map(spanNum));
|
|
const vnode2 = h("span", [1, 4, 2, 3].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(4);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.children.length).toBe(4);
|
|
expect(elm.children[0].innerHTML).toBe("1");
|
|
expect(elm.children[1].innerHTML).toBe("4");
|
|
expect(elm.children[2].innerHTML).toBe("2");
|
|
expect(elm.children[3].innerHTML).toBe("3");
|
|
});
|
|
|
|
test("swaps first and last", function() {
|
|
const vnode1 = h("span", [1, 2, 3, 4].map(spanNum));
|
|
const vnode2 = h("span", [4, 2, 3, 1].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(4);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.children.length).toBe(4);
|
|
expect(elm.children[0].innerHTML).toBe("4");
|
|
expect(elm.children[1].innerHTML).toBe("2");
|
|
expect(elm.children[2].innerHTML).toBe("3");
|
|
expect(elm.children[3].innerHTML).toBe("1");
|
|
});
|
|
});
|
|
|
|
describe("combinations of additions, removals and reorderings", function() {
|
|
test("move to left and replace", function() {
|
|
const vnode1 = h("span", [1, 2, 3, 4, 5].map(spanNum));
|
|
const vnode2 = h("span", [4, 1, 2, 3, 6].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(5);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.children.length).toBe(5);
|
|
expect(elm.children[0].innerHTML).toBe("4");
|
|
expect(elm.children[1].innerHTML).toBe("1");
|
|
expect(elm.children[2].innerHTML).toBe("2");
|
|
expect(elm.children[3].innerHTML).toBe("3");
|
|
expect(elm.children[4].innerHTML).toBe("6");
|
|
});
|
|
|
|
test("moves to left and leaves hole", function() {
|
|
const vnode1 = h("span", [1, 4, 5].map(spanNum));
|
|
const vnode2 = h("span", [4, 6].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(3);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["4", "6"]);
|
|
});
|
|
|
|
test("handles moved and set to undefined element ending at the end", function() {
|
|
const vnode1 = h("span", [2, 4, 5].map(spanNum));
|
|
const vnode2 = h("span", [4, 5, 3].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(3);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.children.length).toBe(3);
|
|
expect(elm.children[0].innerHTML).toBe("4");
|
|
expect(elm.children[1].innerHTML).toBe("5");
|
|
expect(elm.children[2].innerHTML).toBe("3");
|
|
});
|
|
|
|
test("moves a key in non-keyed nodes with a size up", function() {
|
|
const vnode1 = h("span", [1, "a", "b", "c"].map(spanNum));
|
|
const vnode2 = h("span", ["d", "a", "b", "c", 1, "e"].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(4);
|
|
expect(elm.textContent).toBe("1abc");
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.childNodes.length).toBe(6);
|
|
expect(elm.textContent).toBe("dabc1e");
|
|
});
|
|
});
|
|
|
|
describe("misc", function() {
|
|
test("reverses elements", function() {
|
|
const vnode1 = h("span", [1, 2, 3, 4, 5, 6, 7, 8].map(spanNum));
|
|
const vnode2 = h("span", [8, 7, 6, 5, 4, 3, 2, 1].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(8);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual([
|
|
"8",
|
|
"7",
|
|
"6",
|
|
"5",
|
|
"4",
|
|
"3",
|
|
"2",
|
|
"1"
|
|
]);
|
|
});
|
|
|
|
test("something", function() {
|
|
const vnode1 = h("span", [0, 1, 2, 3, 4, 5].map(spanNum));
|
|
const vnode2 = h("span", [4, 3, 2, 1, 5, 0].map(spanNum));
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(6);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["4", "3", "2", "1", "5", "0"]);
|
|
});
|
|
|
|
test("supports null/undefined children", function() {
|
|
const vnode1 = h("i", [0, 1, 2, 3, 4, 5].map(spanNum));
|
|
const vnode2 = h(
|
|
"i",
|
|
[null, 2, undefined, null, 1, 0, null, 5, 4, null, 3, undefined].map(spanNum)
|
|
);
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children.length).toBe(6);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["2", "1", "0", "5", "4", "3"]);
|
|
});
|
|
|
|
test("supports all null/undefined children", function() {
|
|
const vnode1 = h("i", [0, 1, 2, 3, 4, 5].map(spanNum));
|
|
const vnode2 = h("i", [null, null, undefined, null, null, undefined]);
|
|
const vnode3 = h("i", [5, 4, 3, 2, 1, 0].map(spanNum));
|
|
patch(vnode0, vnode1);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.children.length).toBe(0);
|
|
elm = patch(vnode2, vnode3).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["5", "4", "3", "2", "1", "0"]);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("updating children without keys", function() {
|
|
test("appends elements", function() {
|
|
const vnode1 = h("div", [h("span", "Hello")]);
|
|
const vnode2 = h("div", [h("span", "Hello"), h("span", "World")]);
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["Hello"]);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["Hello", "World"]);
|
|
});
|
|
|
|
test("handles unmoved text nodes", function() {
|
|
const vnode1 = h("div", ["Text", h("span", "Span")]);
|
|
const vnode2 = h("div", ["Text", h("span", "Span")]);
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.childNodes[0].textContent).toBe("Text");
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.childNodes[0].textContent).toBe("Text");
|
|
});
|
|
|
|
test("handles changing text children", function() {
|
|
const vnode1 = h("div", ["Text", h("span", "Span")]);
|
|
const vnode2 = h("div", ["Text2", h("span", "Span")]);
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.childNodes[0].textContent).toBe("Text");
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.childNodes[0].textContent).toBe("Text2");
|
|
});
|
|
|
|
test("handles unmoved comment nodes", function() {
|
|
const vnode1 = h("div", [h("!", "Text"), h("span", "Span")]);
|
|
const vnode2 = h("div", [h("!", "Text"), h("span", "Span")]);
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.childNodes[0].textContent).toBe("Text");
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.childNodes[0].textContent).toBe("Text");
|
|
});
|
|
|
|
test("handles changing comment text", function() {
|
|
const vnode1 = h("div", [h("!", "Text"), h("span", "Span")]);
|
|
const vnode2 = h("div", [h("!", "Text2"), h("span", "Span")]);
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.childNodes[0].textContent).toBe("Text");
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.childNodes[0].textContent).toBe("Text2");
|
|
});
|
|
|
|
test("handles changing empty comment", function() {
|
|
const vnode1 = h("div", [h("!"), h("span", "Span")]);
|
|
const vnode2 = h("div", [h("!", "Test"), h("span", "Span")]);
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.childNodes[0].textContent).toBe("");
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.childNodes[0].textContent).toBe("Test");
|
|
});
|
|
|
|
test("prepends element", function() {
|
|
const vnode1 = h("div", [h("span", "World")]);
|
|
const vnode2 = h("div", [h("span", "Hello"), h("span", "World")]);
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["World"]);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["Hello", "World"]);
|
|
});
|
|
|
|
test("prepends element of different tag type", function() {
|
|
const vnode1 = h("div", [h("span", "World")]);
|
|
const vnode2 = h("div", [h("div", "Hello"), h("span", "World")]);
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["World"]);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(map(elm.children, c => c.tagName)).toEqual(["DIV", "SPAN"]);
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["Hello", "World"]);
|
|
});
|
|
|
|
test("removes elements", function() {
|
|
const vnode1 = h("div", [h("span", "One"), h("span", "Two"), h("span", "Three")]);
|
|
const vnode2 = h("div", [h("span", "One"), h("span", "Three")]);
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["One", "Two", "Three"]);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["One", "Three"]);
|
|
});
|
|
|
|
test("removes a single text node", function() {
|
|
const vnode1 = h("div", "One");
|
|
const vnode2 = h("div");
|
|
patch(vnode0, vnode1);
|
|
expect(elm.textContent).toBe("One");
|
|
patch(vnode1, vnode2);
|
|
expect(elm.textContent).toBe("");
|
|
});
|
|
|
|
test("removes a single text node when children are updated", function() {
|
|
const vnode1 = h("div", "One");
|
|
const vnode2 = h("div", [h("div", "Two"), h("span", "Three")]);
|
|
patch(vnode0, vnode1);
|
|
expect(elm.textContent).toBe("One");
|
|
patch(vnode1, vnode2);
|
|
expect(map(elm.children, c => c.textContent)).toEqual(["Two", "Three"]);
|
|
});
|
|
|
|
test("removes a text node among other elements", function() {
|
|
const vnode1 = h("div", ["One", h("span", "Two")]);
|
|
const vnode2 = h("div", [h("div", "Three")]);
|
|
patch(vnode0, vnode1);
|
|
expect(map(elm.childNodes, c => c.textContent)).toEqual(["One", "Two"]);
|
|
patch(vnode1, vnode2);
|
|
expect(elm.childNodes.length).toBe(1);
|
|
expect(elm.childNodes[0].tagName).toBe("DIV");
|
|
expect(elm.childNodes[0].textContent).toBe("Three");
|
|
});
|
|
|
|
test("reorders elements", function() {
|
|
const vnode1 = h("div", [h("span", "One"), h("div", "Two"), h("b", "Three")]);
|
|
const vnode2 = h("div", [h("b", "Three"), h("span", "One"), h("div", "Two")]);
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["One", "Two", "Three"]);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(map(elm.children, c => c.tagName)).toEqual(["B", "SPAN", "DIV"]);
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["Three", "One", "Two"]);
|
|
});
|
|
|
|
test("supports null/undefined children", function() {
|
|
const vnode1 = h("i", [null, h("i", "1"), h("i", "2"), null]);
|
|
const vnode2 = h("i", [h("i", "2"), undefined, undefined, h("i", "1"), undefined]);
|
|
const vnode3 = h("i", [null, h("i", "1"), undefined, null, h("i", "2"), undefined, null]);
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["1", "2"]);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["2", "1"]);
|
|
elm = patch(vnode2, vnode3).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["1", "2"]);
|
|
});
|
|
|
|
test("supports all null/undefined children", function() {
|
|
const vnode1 = h("i", [h("i", "1"), h("i", "2")]);
|
|
const vnode2 = h("i", [null, undefined]);
|
|
const vnode3 = h("i", [h("i", "2"), h("i", "1")]);
|
|
patch(vnode0, vnode1);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.children.length).toBe(0);
|
|
elm = patch(vnode2, vnode3).elm;
|
|
expect(map(elm.children, c => c.innerHTML)).toEqual(["2", "1"]);
|
|
});
|
|
});
|
|
|
|
describe("hooks", function() {
|
|
describe("element hooks", function() {
|
|
test("calls `create` listener before inserted into parent but after children", function() {
|
|
const result: any[] = [];
|
|
function cb(empty, vnode) {
|
|
expect(vnode.elm).toBeInstanceOf(Element);
|
|
expect(vnode.elm.children).toHaveLength(2);
|
|
expect(vnode.elm.parentNode).toBeNull();
|
|
result.push(vnode);
|
|
}
|
|
const vnode1 = h("div", [
|
|
h("span", "First sibling"),
|
|
h("div", { hook: { create: cb } }, [h("span", "Child 1"), h("span", "Child 2")]),
|
|
h("span", "Can't touch me")
|
|
]);
|
|
patch(vnode0, vnode1);
|
|
expect(result).toHaveLength(1);
|
|
});
|
|
|
|
test("calls `insert` listener after both parents, siblings and children have been inserted", function() {
|
|
const result: any[] = [];
|
|
function cb(vnode) {
|
|
expect(vnode.elm).toBeInstanceOf(Element);
|
|
expect(vnode.elm.children).toHaveLength(2);
|
|
expect(vnode.elm.parentNode.children).toHaveLength(3);
|
|
result.push(vnode);
|
|
}
|
|
const vnode1 = h("div", [
|
|
h("span", "First sibling"),
|
|
h("div", { hook: { insert: cb } }, [h("span", "Child 1"), h("span", "Child 2")]),
|
|
h("span", "Can touch me")
|
|
]);
|
|
patch(vnode0, vnode1);
|
|
expect(result).toHaveLength(1);
|
|
});
|
|
|
|
test("calls `prepatch` listener", function() {
|
|
const result: any[] = [];
|
|
function cb(oldVnode, vnode) {
|
|
expect(oldVnode).toBe(vnode1.children![1]);
|
|
expect(vnode).toBe(vnode2.children![1]);
|
|
result.push(vnode);
|
|
}
|
|
const vnode1 = h("div", [
|
|
h("span", "First sibling"),
|
|
h("div", { hook: { prepatch: cb } }, [h("span", "Child 1"), h("span", "Child 2")])
|
|
]);
|
|
const vnode2 = h("div", [
|
|
h("span", "First sibling"),
|
|
h("div", { hook: { prepatch: cb } }, [h("span", "Child 1"), h("span", "Child 2")])
|
|
]);
|
|
patch(vnode0, vnode1);
|
|
patch(vnode1, vnode2);
|
|
expect(result).toHaveLength(1);
|
|
});
|
|
|
|
test("calls `postpatch` after `prepatch` listener", function() {
|
|
const pre: any[] = [],
|
|
post: any[] = [];
|
|
function preCb(oldVnode, vnode) {
|
|
pre.push(pre);
|
|
}
|
|
function postCb(oldVnode, vnode) {
|
|
expect(pre.length).toBe(post.length + 1);
|
|
post.push(post);
|
|
}
|
|
const vnode1 = h("div", [
|
|
h("span", "First sibling"),
|
|
h("div", { hook: { prepatch: preCb, postpatch: postCb } }, [
|
|
h("span", "Child 1"),
|
|
h("span", "Child 2")
|
|
])
|
|
]);
|
|
const vnode2 = h("div", [
|
|
h("span", "First sibling"),
|
|
h("div", { hook: { prepatch: preCb, postpatch: postCb } }, [
|
|
h("span", "Child 1"),
|
|
h("span", "Child 2")
|
|
])
|
|
]);
|
|
patch(vnode0, vnode1);
|
|
patch(vnode1, vnode2);
|
|
expect(pre).toHaveLength(1);
|
|
expect(post).toHaveLength(1);
|
|
});
|
|
|
|
test("calls `update` listener", function() {
|
|
const result1: any[] = [];
|
|
const result2: any[] = [];
|
|
function cb(result, oldVnode, vnode) {
|
|
if (result.length > 0) {
|
|
console.log(result[result.length - 1]);
|
|
console.log(oldVnode);
|
|
expect(result[result.length - 1]).toBe(oldVnode);
|
|
}
|
|
result.push(vnode);
|
|
}
|
|
const vnode1 = h("div", [
|
|
h("span", "First sibling"),
|
|
h("div", { hook: { update: cb.bind(null, result1) } }, [
|
|
h("span", "Child 1"),
|
|
h("span", { hook: { update: cb.bind(null, result2) } }, "Child 2")
|
|
])
|
|
]);
|
|
const vnode2 = h("div", [
|
|
h("span", "First sibling"),
|
|
h("div", { hook: { update: cb.bind(null, result1) } }, [
|
|
h("span", "Child 1"),
|
|
h("span", { hook: { update: cb.bind(null, result2) } }, "Child 2")
|
|
])
|
|
]);
|
|
patch(vnode0, vnode1);
|
|
patch(vnode1, vnode2);
|
|
expect(result1).toHaveLength(1);
|
|
expect(result2).toHaveLength(1);
|
|
});
|
|
|
|
test("calls `remove` listener", function() {
|
|
const result: any[] = [];
|
|
function cb(vnode, rm) {
|
|
const parent = vnode.elm.parentNode;
|
|
expect(vnode.elm).toBeInstanceOf(Element);
|
|
expect(vnode.elm.children).toHaveLength(2);
|
|
expect(parent.children).toHaveLength(2);
|
|
result.push(vnode);
|
|
rm();
|
|
expect(parent.children).toHaveLength(1);
|
|
}
|
|
const vnode1 = h("div", [
|
|
h("span", "First sibling"),
|
|
h("div", { hook: { remove: cb } }, [h("span", "Child 1"), h("span", "Child 2")])
|
|
]);
|
|
const vnode2 = h("div", [h("span", "First sibling")]);
|
|
patch(vnode0, vnode1);
|
|
patch(vnode1, vnode2);
|
|
expect(result).toHaveLength(1);
|
|
});
|
|
|
|
test("calls `destroy` listener when patching text node over node with children", function() {
|
|
let calls = 0;
|
|
function cb(vnode) {
|
|
calls++;
|
|
}
|
|
const vnode1 = h("div", [h("div", { hook: { destroy: cb } }, [h("span", "Child 1")])]);
|
|
const vnode2 = h("div", "Text node");
|
|
patch(vnode0, vnode1);
|
|
patch(vnode1, vnode2);
|
|
expect(calls).toBe(1);
|
|
});
|
|
|
|
test("calls `init` and `prepatch` listeners on root", function() {
|
|
let count = 0;
|
|
function init(vnode) {
|
|
expect(vnode).toBe(vnode2);
|
|
count += 1;
|
|
}
|
|
function prepatch(oldVnode, vnode) {
|
|
expect(vnode).toBe(vnode1);
|
|
count += 1;
|
|
}
|
|
const vnode1 = h("div", { hook: { init: init, prepatch: prepatch } });
|
|
patch(vnode0, vnode1);
|
|
expect(count).toBe(1);
|
|
const vnode2 = h("span", { hook: { init: init, prepatch: prepatch } });
|
|
patch(vnode1, vnode2);
|
|
expect(count).toBe(2);
|
|
});
|
|
|
|
test("removes element when all remove listeners are done", function() {
|
|
let rm1, rm2, rm3;
|
|
const patch = init([
|
|
{
|
|
remove: function(_, rm) {
|
|
rm1 = rm;
|
|
}
|
|
},
|
|
{
|
|
remove: function(_, rm) {
|
|
rm2 = rm;
|
|
}
|
|
}
|
|
]);
|
|
const vnode1 = h("div", [
|
|
h("a", {
|
|
hook: {
|
|
remove: function(_, rm) {
|
|
rm3 = rm;
|
|
}
|
|
}
|
|
})
|
|
]);
|
|
const vnode2 = h("div", []);
|
|
elm = patch(vnode0, vnode1).elm;
|
|
expect(elm.children).toHaveLength(1);
|
|
elm = patch(vnode1, vnode2).elm;
|
|
expect(elm.children).toHaveLength(1);
|
|
rm1();
|
|
expect(elm.children).toHaveLength(1);
|
|
rm3();
|
|
expect(elm.children).toHaveLength(1);
|
|
rm2();
|
|
expect(elm.children).toHaveLength(0);
|
|
});
|
|
|
|
test("invokes remove hook on replaced root", function() {
|
|
const result: any[] = [];
|
|
const parent = document.createElement("div");
|
|
const vnode0 = document.createElement("div");
|
|
parent.appendChild(vnode0);
|
|
function cb(vnode, rm) {
|
|
result.push(vnode);
|
|
rm();
|
|
}
|
|
const vnode1 = h("div", { hook: { remove: cb } }, [h("b", "Child 1"), h("i", "Child 2")]);
|
|
const vnode2 = h("span", [h("b", "Child 1"), h("i", "Child 2")]);
|
|
patch(vnode0, vnode1);
|
|
patch(vnode1, vnode2);
|
|
expect(result).toHaveLength(1);
|
|
});
|
|
});
|
|
|
|
describe("module hooks", function() {
|
|
test("invokes `pre` and `post` hook", function() {
|
|
const result: any[] = [];
|
|
const patch = init([
|
|
{
|
|
pre: function() {
|
|
result.push("pre");
|
|
}
|
|
},
|
|
{
|
|
post: function() {
|
|
result.push("post");
|
|
}
|
|
}
|
|
]);
|
|
const vnode1 = h("div");
|
|
patch(vnode0, vnode1);
|
|
expect(result).toEqual(["pre", "post"]);
|
|
});
|
|
|
|
test("invokes global `destroy` hook for all removed children", function() {
|
|
const result: any[] = [];
|
|
function cb(vnode) {
|
|
result.push(vnode);
|
|
}
|
|
const vnode1 = h("div", [
|
|
h("span", "First sibling"),
|
|
h("div", [h("span", { hook: { destroy: cb } }, "Child 1"), h("span", "Child 2")])
|
|
]);
|
|
const vnode2 = h("div");
|
|
patch(vnode0, vnode1);
|
|
patch(vnode1, vnode2);
|
|
expect(result).toHaveLength(1);
|
|
});
|
|
|
|
test("handles text vnodes with `undefined` `data` property", function() {
|
|
const vnode1 = h("div", [" "]);
|
|
const vnode2 = h("div", []);
|
|
patch(vnode0, vnode1);
|
|
patch(vnode1, vnode2);
|
|
});
|
|
|
|
test("invokes `destroy` module hook for all removed children", function() {
|
|
let created = 0;
|
|
let destroyed = 0;
|
|
const patch = init([
|
|
{
|
|
create: function() {
|
|
created++;
|
|
}
|
|
},
|
|
{
|
|
destroy: function() {
|
|
destroyed++;
|
|
}
|
|
}
|
|
]);
|
|
const vnode1 = h("div", [
|
|
h("span", "First sibling"),
|
|
h("div", [h("span", "Child 1"), h("span", "Child 2")])
|
|
]);
|
|
const vnode2 = h("div");
|
|
patch(vnode0, vnode1);
|
|
patch(vnode1, vnode2);
|
|
expect(created).toBe(4);
|
|
expect(destroyed).toBe(4);
|
|
});
|
|
|
|
test("does not invoke `create` and `remove` module hook for text nodes", function() {
|
|
let created = 0;
|
|
let removed = 0;
|
|
const patch = init([
|
|
{
|
|
create: function() {
|
|
created++;
|
|
}
|
|
},
|
|
{
|
|
remove: function() {
|
|
removed++;
|
|
}
|
|
}
|
|
]);
|
|
const vnode1 = h("div", [h("span", "First child"), "", h("span", "Third child")]);
|
|
const vnode2 = h("div");
|
|
patch(vnode0, vnode1);
|
|
patch(vnode1, vnode2);
|
|
expect(created).toBe(2);
|
|
expect(removed).toBe(2);
|
|
});
|
|
|
|
test("does not invoke `destroy` module hook for text nodes", function() {
|
|
let created = 0;
|
|
let destroyed = 0;
|
|
const patch = init([
|
|
{
|
|
create: function() {
|
|
created++;
|
|
}
|
|
},
|
|
{
|
|
destroy: function() {
|
|
destroyed++;
|
|
}
|
|
}
|
|
]);
|
|
const vnode1 = h("div", [
|
|
h("span", "First sibling"),
|
|
h("div", [h("span", "Child 1"), h("span", ["Text 1", "Text 2"])])
|
|
]);
|
|
const vnode2 = h("div");
|
|
patch(vnode0, vnode1);
|
|
patch(vnode1, vnode2);
|
|
expect(created).toBe(4);
|
|
expect(destroyed).toBe(4);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("short circuiting", function() {
|
|
test("does not update strictly equal vnodes", function() {
|
|
const result: any[] = [];
|
|
function cb(vnode) {
|
|
result.push(vnode);
|
|
}
|
|
const vnode1 = h("div", [h("span", { hook: { update: cb } }, "Hello"), h("span", "there")]);
|
|
patch(vnode0, vnode1);
|
|
patch(vnode1, vnode1);
|
|
expect(result).toHaveLength(0);
|
|
});
|
|
|
|
test("does not update strictly equal children", function() {
|
|
const result: any[] = [];
|
|
function cb(vnode) {
|
|
result.push(vnode);
|
|
}
|
|
const vnode1 = h("div", [
|
|
h("span", { hook: <any>{ patch: cb } }, "Hello"),
|
|
h("span", "there")
|
|
]);
|
|
const vnode2 = h("div");
|
|
vnode2.children = vnode1.children;
|
|
patch(vnode0, vnode1);
|
|
patch(vnode1, vnode2);
|
|
expect(result).toHaveLength(0);
|
|
});
|
|
});
|
|
});
|