Files
owl/tests/blockdom/list.test.ts
T
2022-02-11 10:18:01 +01:00

459 lines
14 KiB
TypeScript

import { list, mount, multi, patch, text, createBlock, VNode, withKey } from "../../src/blockdom";
import { makeTestFixture } from "./helpers";
//------------------------------------------------------------------------------
// Setup and helpers
//------------------------------------------------------------------------------
let fixture: HTMLElement;
beforeEach(() => {
fixture = makeTestFixture();
});
afterEach(() => {
fixture.remove();
});
function kText(str: string, key: any): VNode {
return withKey(text(str), key);
}
function n(n: number) {
return kText(String(n), n);
}
const span = createBlock("<span><block-text-0/></span>");
const p = createBlock("<p><block-text-0/></p>");
function kSpan(str: string, key: any): VNode {
return withKey(span([str]), key);
}
function kPair(n: number): VNode {
const bnodes = [p([String(n)]), p([String(n)])];
return withKey(multi(bnodes), n);
}
describe("list node: misc", () => {
test("list node", async () => {
const bnodes = [1, 2, 3].map((key) => kText(`text${key}`, key));
const tree = list(bnodes);
mount(tree, fixture);
expect(fixture.innerHTML).toBe("text1text2text3");
});
test("list vnode can be used as text", () => {
mount(text(list([text("a"), text("b")]) as any), fixture);
expect(fixture.innerHTML).toBe("ab");
});
test("a list block can be removed and leaves nothing", async () => {
const bnodes = [
{ id: 1, name: "sheep" },
{ id: 2, name: "cow" },
].map((elem) => kText(elem.name, elem.id));
const tree = list(bnodes);
expect(fixture.childNodes.length).toBe(0);
mount(tree, fixture);
expect(fixture.childNodes.length).toBe(3);
expect(fixture.innerHTML).toBe("sheepcow");
tree.remove();
expect(fixture.innerHTML).toBe("");
expect(fixture.childNodes.length).toBe(0);
});
test("patching a list block inside an elem block", async () => {
const block = createBlock("<div><block-child-0/></div>");
const tree = block();
mount(tree, fixture);
expect(fixture.innerHTML).toBe("<div></div>");
patch(tree, block([], [list([1, 2, 3].map(n))]));
expect(fixture.innerHTML).toBe("<div>123</div>");
patch(tree, block([], [list([])]));
expect(fixture.innerHTML).toBe("<div></div>");
patch(tree, block([], [list([1, 2, 3].map(n))]));
expect(fixture.innerHTML).toBe("<div>123</div>");
});
test("list of lists", async () => {
const tree = list([
withKey(list([kText("a1", "1"), kText("a2", "2")]), "a"),
withKey(list([kText("b1", "1"), kText("b2", "2")]), "b"),
]);
mount(tree, fixture);
expect(fixture.innerHTML).toBe("a1a2b1b2");
patch(
tree,
list([
withKey(list([kText("b1", "1"), kText("b2", "2")]), "b"),
withKey(list([kText("a1", "1"), kText("a2", "2")]), "a"),
])
);
expect(fixture.innerHTML).toBe("b1b2a1a2");
patch(
tree,
list([
withKey(list([kText("a2", "2"), kText("a1", "1")]), "a"),
withKey(list([kText("b2", "2"), kText("b1", "1")]), "b"),
])
);
expect(fixture.innerHTML).toBe("a2a1b2b1");
});
});
describe("adding/removing elements", () => {
test("removing elements", () => {
const tree = list([kText("a", "a")]);
mount(tree, fixture);
expect(fixture.innerHTML).toBe("a");
patch(tree, list([]));
expect(fixture.innerHTML).toBe("");
});
test("removing 1 elements from 2", () => {
const tree = list([kText("a", "a"), kText("b", "b")]);
mount(tree, fixture);
expect(fixture.innerHTML).toBe("ab");
patch(tree, list([kText("a", "a")]));
expect(fixture.innerHTML).toBe("a");
});
test("removing elements", () => {
const tree = list([kSpan("a", "a")]);
mount(tree, fixture);
expect(fixture.innerHTML).toBe("<span>a</span>");
patch(tree, list([]));
expect(fixture.innerHTML).toBe("");
});
test("removing elements, variation", () => {
const f = (i: number) => {
const b = multi([span([`a${i}`]), span([`b${i}`])]);
b.key = i;
return b;
};
const tree = list([f(1), f(2)]);
mount(tree, fixture);
expect(fixture.innerHTML).toBe("<span>a1</span><span>b1</span><span>a2</span><span>b2</span>");
patch(tree, list([]));
expect(fixture.innerHTML).toBe("");
});
test("adding one element at the end", () => {
const tree = list([n(1), n(2)]);
mount(tree, fixture);
expect(fixture.innerHTML).toBe("12");
patch(tree, list([n(1), n(2), n(3)]));
expect(fixture.innerHTML).toBe("123");
});
test("adding one element at the end", () => {
const tree = list([n(1)]);
mount(tree, fixture);
expect(fixture.innerHTML).toBe("1");
patch(tree, list([n(1), n(2), n(3)]));
expect(fixture.innerHTML).toBe("123");
});
test("prepend elements: 4,5 => 1,2,3,4,5", () => {
const tree = list([n(4), n(5)]);
mount(tree, fixture);
expect(fixture.innerHTML).toBe("45");
patch(tree, list([n(1), n(2), n(3), n(4), n(5)]));
expect(fixture.innerHTML).toBe("12345");
});
test("prepend elements: 4,5 => 1,2,3,4,5 (with multi)", () => {
const tree = list([kPair(4), kPair(5)]);
mount(tree, fixture);
expect(fixture.innerHTML).toBe("<p>4</p><p>4</p><p>5</p><p>5</p>");
patch(tree, list([1, 2, 3, 4, 5].map(kPair)));
expect(fixture.innerHTML).toBe(
"<p>1</p><p>1</p><p>2</p><p>2</p><p>3</p><p>3</p><p>4</p><p>4</p><p>5</p><p>5</p>"
);
});
test("add element in middle: 1,2,4,5 => 1,2,3,4,5", () => {
const tree = list([1, 2, 4, 5].map(n));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("1245");
patch(tree, list([1, 2, 3, 4, 5].map(n)));
expect(fixture.innerHTML).toBe("12345");
});
test("add element in middle: 1,2,4,5 => 1,2,3,4,5 (multi)", () => {
const tree = list([1, 2, 4, 5].map(kPair));
mount(tree, fixture);
expect(fixture.innerHTML).toBe(
"<p>1</p><p>1</p><p>2</p><p>2</p><p>4</p><p>4</p><p>5</p><p>5</p>"
);
patch(tree, list([1, 2, 3, 4, 5].map(kPair)));
expect(fixture.innerHTML).toBe(
"<p>1</p><p>1</p><p>2</p><p>2</p><p>3</p><p>3</p><p>4</p><p>4</p><p>5</p><p>5</p>"
);
});
test("add element at beginning and end: 2,3,4 => 1,2,3,4,5", () => {
const tree = list([2, 3, 4].map(n));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("234");
patch(tree, list([1, 2, 3, 4, 5].map(n)));
expect(fixture.innerHTML).toBe("12345");
});
test("add element at beginning and end: 2,3,4 => 1,2,3,4,5 (multi)", () => {
const tree = list([2, 3, 4].map(kPair));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("<p>2</p><p>2</p><p>3</p><p>3</p><p>4</p><p>4</p>");
patch(tree, list([1, 2, 3, 4, 5].map(kPair)));
expect(fixture.innerHTML).toBe(
"<p>1</p><p>1</p><p>2</p><p>2</p><p>3</p><p>3</p><p>4</p><p>4</p><p>5</p><p>5</p>"
);
});
test("adds children: [] => [1,2,3]", () => {
const tree = list([].map(n));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("");
patch(tree, list([1, 2, 3].map(n)));
expect(fixture.innerHTML).toBe("123");
});
test("adds children: [] => [1,2,3] (multi)", () => {
const tree = list([].map(kPair));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("");
patch(tree, list([1, 2, 3].map(kPair)));
expect(fixture.innerHTML).toBe("<p>1</p><p>1</p><p>2</p><p>2</p><p>3</p><p>3</p>");
});
test("adds children: [] => [1,2,3] (inside elem)", () => {
const block = createBlock("<p><block-child-0/></p>");
const tree = block([], []);
mount(tree, fixture);
expect(fixture.innerHTML).toBe("<p></p>");
patch(tree, block([], [list([1, 2, 3].map(n))]));
expect(fixture.innerHTML).toBe("<p>123</p>");
});
test("adds children: [] => [1,2,3] (inside elem, multi)", () => {
const block = createBlock("<p><block-child-0/></p>");
const tree = block([], []);
mount(tree, fixture);
expect(fixture.innerHTML).toBe("<p></p>");
patch(tree, block([], [list([1, 2, 3].map(kPair))]));
expect(fixture.innerHTML).toBe("<p><p>1</p><p>1</p><p>2</p><p>2</p><p>3</p><p>3</p></p>");
});
test("remove children: [1,2,3] => []", () => {
const tree = list([1, 2, 3].map(n));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("123");
patch(tree, list([].map(n)));
expect(fixture.innerHTML).toBe("");
});
test("remove children: [1,2,3] => [] (multi)", () => {
const tree = list([1, 2, 3].map(kPair));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("<p>1</p><p>1</p><p>2</p><p>2</p><p>3</p><p>3</p>");
patch(tree, list([].map(kPair)));
expect(fixture.innerHTML).toBe("");
});
test("remove children: [1,2,3] => [] (inside elem)", () => {
const block = createBlock("<p><block-child-0/></p>");
const tree = block([], [list([1, 2, 3].map(n))]);
mount(tree, fixture);
expect(fixture.innerHTML).toBe("<p>123</p>");
patch(tree, block([], [list([])]));
expect(fixture.innerHTML).toBe("<p></p>");
});
test("remove children from the beginning: [1,2,3,4,5] => [3,4,5]", () => {
const tree = list([1, 2, 3, 4, 5].map(n));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("12345");
patch(tree, list([3, 4, 5].map(n)));
expect(fixture.innerHTML).toBe("345");
});
test("remove children from the beginning: [1,2,3,4,5] => [3,4,5] (multi)", () => {
const tree = list([1, 2, 3, 4, 5].map(kPair));
mount(tree, fixture);
expect(fixture.innerHTML).toBe(
"<p>1</p><p>1</p><p>2</p><p>2</p><p>3</p><p>3</p><p>4</p><p>4</p><p>5</p><p>5</p>"
);
patch(tree, list([3, 4, 5].map(kPair)));
expect(fixture.innerHTML).toBe("<p>3</p><p>3</p><p>4</p><p>4</p><p>5</p><p>5</p>");
});
test("remove children from the end: [1,2,3,4,5] => [1,2,3]", () => {
const tree = list([1, 2, 3, 4, 5].map(n));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("12345");
patch(tree, list([1, 2, 3].map(n)));
expect(fixture.innerHTML).toBe("123");
});
test("remove children from the middle: [1,2,3,4,5] => [1,2,4,5]", () => {
const tree = list([1, 2, 3, 4, 5].map(n));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("12345");
patch(tree, list([1, 2, 4, 5].map(n)));
expect(fixture.innerHTML).toBe("1245");
});
});
describe("element reordering", () => {
test("move element forward: [1,2,3,4] => [2,3,1,4]", () => {
const tree = list([1, 2, 3, 4].map(n));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("1234");
patch(tree, list([2, 3, 1, 4].map(n)));
expect(fixture.innerHTML).toBe("2314");
});
test("move element forward: [1,2,3,4] => [2,3,1,4] (multi)", () => {
const tree = list([1, 2, 3, 4].map(kPair));
mount(tree, fixture);
expect(fixture.innerHTML).toBe(
"<p>1</p><p>1</p><p>2</p><p>2</p><p>3</p><p>3</p><p>4</p><p>4</p>"
);
patch(tree, list([2, 3, 1, 4].map(kPair)));
expect(fixture.innerHTML).toBe(
"<p>2</p><p>2</p><p>3</p><p>3</p><p>1</p><p>1</p><p>4</p><p>4</p>"
);
});
test("move element to end: [1,2,3] => [2,3,1]", () => {
const tree = list([1, 2, 3].map(n));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("123");
patch(tree, list([2, 3, 1].map(n)));
expect(fixture.innerHTML).toBe("231");
});
test("move element to end: [1,2,3] => [2,3,1] (multi)", () => {
const tree = list([1, 2, 3].map(kPair));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("<p>1</p><p>1</p><p>2</p><p>2</p><p>3</p><p>3</p>");
patch(tree, list([2, 3, 1].map(kPair)));
expect(fixture.innerHTML).toBe("<p>2</p><p>2</p><p>3</p><p>3</p><p>1</p><p>1</p>");
});
test("move element backward: [1,2,3,4] => [1,4,2,3]", () => {
const tree = list([1, 2, 3, 4].map(n));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("1234");
patch(tree, list([1, 4, 2, 3].map(n)));
expect(fixture.innerHTML).toBe("1423");
});
test("swaps first and last: [1,2,3,4] => [4,3,2,1]", () => {
const tree = list([1, 2, 3, 4].map(n));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("1234");
patch(tree, list([4, 3, 2, 1].map(n)));
expect(fixture.innerHTML).toBe("4321");
});
});
describe("miscellaneous operations", () => {
test("move to left and replace: [1,2,3,4,5] => [4,1,2,3,6]", () => {
const tree = list([1, 2, 3, 4, 5].map(n));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("12345");
patch(tree, list([4, 1, 2, 3, 6].map(n)));
expect(fixture.innerHTML).toBe("41236");
});
test("move to left and replace: [1,2,3,4,5] => [4,1,2,3,6] (multi)", () => {
const tree = list([1, 2, 3, 4, 5].map(kPair));
mount(tree, fixture);
expect(fixture.innerHTML).toBe(
"<p>1</p><p>1</p><p>2</p><p>2</p><p>3</p><p>3</p><p>4</p><p>4</p><p>5</p><p>5</p>"
);
patch(tree, list([4, 1, 2, 3, 6].map(kPair)));
expect(fixture.innerHTML).toBe(
"<p>4</p><p>4</p><p>1</p><p>1</p><p>2</p><p>2</p><p>3</p><p>3</p><p>6</p><p>6</p>"
);
});
test("move to left and leave hole: [1,4,5] => [4,6]", () => {
const tree = list([1, 4, 5].map(n));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("145");
patch(tree, list([4, 6].map(n)));
expect(fixture.innerHTML).toBe("46");
});
test("[2,4,5] => [4,5,3]", () => {
const tree = list([2, 4, 5].map(n));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("245");
patch(tree, list([4, 5, 3].map(n)));
expect(fixture.innerHTML).toBe("453");
});
test("reverse elements [1,2,3,4,5,6,7,8] => [8,7,6,5,4,3,2,1]", () => {
const tree = list([1, 2, 3, 4, 5, 6, 7, 8].map(n));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("12345678");
patch(tree, list([8, 7, 6, 5, 4, 3, 2, 1].map(n)));
expect(fixture.innerHTML).toBe("87654321");
});
test("some permutation [0,1,2,3,4,5] => [4,3,2,1,5,0]", () => {
const tree = list([0, 1, 2, 3, 4, 5].map(n));
mount(tree, fixture);
expect(fixture.innerHTML).toBe("012345");
patch(tree, list([4, 3, 2, 1, 5, 0].map(n)));
expect(fixture.innerHTML).toBe("432150");
});
});