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(""); const p = createBlock("

"); 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("
"); const tree = block(); mount(tree, fixture); expect(fixture.innerHTML).toBe("
"); patch(tree, block([], [list([1, 2, 3].map(n))])); expect(fixture.innerHTML).toBe("
123
"); patch(tree, block([], [list([])])); expect(fixture.innerHTML).toBe("
"); patch(tree, block([], [list([1, 2, 3].map(n))])); expect(fixture.innerHTML).toBe("
123
"); }); 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("a"); 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("a1b1a2b2"); 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("

4

4

5

5

"); patch(tree, list([1, 2, 3, 4, 5].map(kPair))); expect(fixture.innerHTML).toBe( "

1

1

2

2

3

3

4

4

5

5

" ); }); 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( "

1

1

2

2

4

4

5

5

" ); patch(tree, list([1, 2, 3, 4, 5].map(kPair))); expect(fixture.innerHTML).toBe( "

1

1

2

2

3

3

4

4

5

5

" ); }); 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("

2

2

3

3

4

4

"); patch(tree, list([1, 2, 3, 4, 5].map(kPair))); expect(fixture.innerHTML).toBe( "

1

1

2

2

3

3

4

4

5

5

" ); }); 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("

1

1

2

2

3

3

"); }); test("adds children: [] => [1,2,3] (inside elem)", () => { const block = createBlock("

"); const tree = block([], []); mount(tree, fixture); expect(fixture.innerHTML).toBe("

"); patch(tree, block([], [list([1, 2, 3].map(n))])); expect(fixture.innerHTML).toBe("

123

"); }); test("adds children: [] => [1,2,3] (inside elem, multi)", () => { const block = createBlock("

"); const tree = block([], []); mount(tree, fixture); expect(fixture.innerHTML).toBe("

"); patch(tree, block([], [list([1, 2, 3].map(kPair))])); expect(fixture.innerHTML).toBe("

1

1

2

2

3

3

"); }); 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("

1

1

2

2

3

3

"); patch(tree, list([].map(kPair))); expect(fixture.innerHTML).toBe(""); }); test("remove children: [1,2,3] => [] (inside elem)", () => { const block = createBlock("

"); const tree = block([], [list([1, 2, 3].map(n))]); mount(tree, fixture); expect(fixture.innerHTML).toBe("

123

"); patch(tree, block([], [list([])])); expect(fixture.innerHTML).toBe("

"); }); 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( "

1

1

2

2

3

3

4

4

5

5

" ); patch(tree, list([3, 4, 5].map(kPair))); expect(fixture.innerHTML).toBe("

3

3

4

4

5

5

"); }); 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( "

1

1

2

2

3

3

4

4

" ); patch(tree, list([2, 3, 1, 4].map(kPair))); expect(fixture.innerHTML).toBe( "

2

2

3

3

1

1

4

4

" ); }); 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("

1

1

2

2

3

3

"); patch(tree, list([2, 3, 1].map(kPair))); expect(fixture.innerHTML).toBe("

2

2

3

3

1

1

"); }); 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( "

1

1

2

2

3

3

4

4

5

5

" ); patch(tree, list([4, 1, 2, 3, 6].map(kPair))); expect(fixture.innerHTML).toBe( "

4

4

1

1

2

2

3

3

6

6

" ); }); 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"); }); });