mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
305 lines
15 KiB
TypeScript
305 lines
15 KiB
TypeScript
// Copyright 2024, Command Line Inc.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
import { assert, test } from "vitest";
|
|
import {
|
|
addChildAt,
|
|
addIntermediateNode,
|
|
balanceNode,
|
|
findNextInsertLocation,
|
|
newLayoutNode,
|
|
} from "../lib/layoutNode.js";
|
|
import { LayoutNode } from "../lib/model.js";
|
|
import { FlexDirection } from "../lib/utils.js";
|
|
import { TestData } from "./model.js";
|
|
|
|
test("newLayoutNode", () => {
|
|
assert.throws(
|
|
() => newLayoutNode<TestData>(FlexDirection.Column),
|
|
"Invalid node",
|
|
undefined,
|
|
"calls to the constructor without data or children should fail"
|
|
);
|
|
assert.throws(
|
|
() => newLayoutNode<TestData>(FlexDirection.Column, undefined, [], { name: "hello" }),
|
|
"Invalid node",
|
|
undefined,
|
|
"calls to the constructor with both data and children should fail"
|
|
);
|
|
assert.doesNotThrow(
|
|
() => newLayoutNode<TestData>(FlexDirection.Column, undefined, undefined, { name: "hello" }),
|
|
"Invalid node",
|
|
undefined,
|
|
"calls to the constructor with only data defined should succeed"
|
|
);
|
|
assert.throws(() => newLayoutNode<TestData>(FlexDirection.Column, undefined, [], undefined)),
|
|
"Invalid node",
|
|
undefined,
|
|
"calls to the constructor with empty children array should fail";
|
|
assert.doesNotThrow(() =>
|
|
newLayoutNode<TestData>(
|
|
FlexDirection.Column,
|
|
undefined,
|
|
[newLayoutNode<TestData>(FlexDirection.Column, undefined, undefined, { name: "hello" })],
|
|
undefined
|
|
)
|
|
),
|
|
"Invalid node",
|
|
undefined,
|
|
"calls to the constructor with children array containing at least one child should succeed";
|
|
});
|
|
|
|
test("addIntermediateNode", () => {
|
|
let node1: LayoutNode<TestData> = newLayoutNode<TestData>(FlexDirection.Column, undefined, [
|
|
newLayoutNode<TestData>(FlexDirection.Row, undefined, undefined, { name: "hello" }),
|
|
]);
|
|
assert(node1.children![0].data!.name === "hello", "node1 should have one child which should have data");
|
|
const intermediateNode1 = addIntermediateNode(node1);
|
|
assert(
|
|
node1.children !== undefined && node1.children.length === 1 && node1.children?.includes(intermediateNode1),
|
|
"node1 should have a single child intermediateNode1"
|
|
);
|
|
assert(intermediateNode1.flexDirection === FlexDirection.Row, "intermediateNode1 should have flexDirection Row");
|
|
assert(
|
|
intermediateNode1.children![0].children![0].data!.name === "hello" &&
|
|
intermediateNode1.children![0].children![0].flexDirection === FlexDirection.Row,
|
|
"intermediateNode1 should have a nested child which should have data and flexDirection Row"
|
|
);
|
|
let node2: LayoutNode<TestData> = newLayoutNode<TestData>(FlexDirection.Column, undefined, undefined, {
|
|
name: "hello",
|
|
});
|
|
const intermediateNode2 = addIntermediateNode(node2);
|
|
assert(
|
|
node2.children !== undefined &&
|
|
node2.data === undefined &&
|
|
node2.children.length === 1 &&
|
|
node2.children.includes(intermediateNode2),
|
|
"node2 should have no data and a single child intermediateNode2"
|
|
);
|
|
assert(
|
|
intermediateNode2.data.name === "hello" && intermediateNode2.children === undefined,
|
|
"intermediateNode2 should have no children and should have data matching the old value of node2"
|
|
);
|
|
});
|
|
|
|
test("addChildAt - same flexDirection, no children", () => {
|
|
let node1 = newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1" });
|
|
let node2 = newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node2" });
|
|
addChildAt(node1, 1, node2);
|
|
assert(node1.data === undefined, "node1 should have no data");
|
|
assert(node1.children!.length === 2, "node1 should have two children");
|
|
assert(node1.children![0].data!.name === "node1", "node1's first child should have node1's data");
|
|
assert(node1.children![1].id === node2.id, "node1's second child should be node2");
|
|
assert(node1.children![1].flexDirection === FlexDirection.Column, "node2 should now have flexDirection Column");
|
|
});
|
|
|
|
test("addChildAt - different flexDirection, no children", () => {
|
|
let node1 = newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1" });
|
|
let node2 = newLayoutNode(FlexDirection.Column, undefined, undefined, { name: "node2" });
|
|
addChildAt(node1, 1, node2);
|
|
assert(node1.data === undefined, "node1 should have no data");
|
|
assert(node1.children!.length === 2, "node1 should have two children");
|
|
assert(node1.children![0].data!.name === "node1", "node1's first child should have node1's data");
|
|
assert(node1.children![0].data!.name === "node1", "node1's first child should have flexDirection Column");
|
|
assert(node1.children![1].id === node2.id, "node1's second child should be node2");
|
|
assert(node1.children![1].flexDirection === FlexDirection.Column, "node2 should have flexDirection Row");
|
|
});
|
|
|
|
test("addChildAt - same flexDirection, first node has children, second doesn't", () => {
|
|
let node1 = newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Column, undefined, undefined, { name: "node1" }),
|
|
]);
|
|
let node2 = newLayoutNode(FlexDirection.Column, undefined, undefined, { name: "node2" });
|
|
addChildAt(node1, 1, node2);
|
|
assert(node1.data === undefined, "node1 should have no data");
|
|
assert(node1.children!.length === 2, "node1 should have two children");
|
|
assert(node1.children![0].data!.name === "node1", "node1's first child should have node1's data");
|
|
assert(
|
|
node1.children![0].flexDirection === FlexDirection.Column,
|
|
"node1's first child should have flexDirection Column"
|
|
);
|
|
assert(node1.children![1].id === node2.id, "node1's second child should be node2");
|
|
assert(node1.children![1].flexDirection === FlexDirection.Column, "node2 should have flexDirection Column");
|
|
});
|
|
|
|
test("addChildAt - different flexDirection, first node has children, second doesn't", () => {
|
|
let node1 = newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Column, undefined, undefined, { name: "node1" }),
|
|
]);
|
|
let node2 = newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node2" });
|
|
addChildAt(node1, 1, node2);
|
|
assert(node1.data === undefined, "node1 should have no data");
|
|
assert(node1.children!.length === 2, "node1 should have two children");
|
|
assert(node1.children![0].data!.name === "node1", "node1's first child should have node1's data");
|
|
assert(node1.children![1].id === node2.id, "node1's second child should be node2");
|
|
assert(node1.children![1].flexDirection === FlexDirection.Column, "node2 should now have flexDirection Column");
|
|
});
|
|
|
|
test("addChildAt - same flexDirection, first node has children, second has children", () => {
|
|
let node1 = newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Column, undefined, undefined, { name: "node1" }),
|
|
]);
|
|
let node2 = newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Column, undefined, undefined, { name: "node2" }),
|
|
]);
|
|
addChildAt(node1, 1, node2);
|
|
assert(node1.data === undefined, "node1 should have no data");
|
|
assert(node1.children!.length === 2, "node1 should have two children");
|
|
assert(node1.children![0].data!.name === "node1", "node1's first child should have node1's data");
|
|
assert(
|
|
node1.children![0].flexDirection === FlexDirection.Column,
|
|
"node1's first child should have flexDirection Column"
|
|
);
|
|
assert(node1.children![1].id === node2.children![0].id, "node1's second child should be node2's child");
|
|
assert(
|
|
node1.children![1].flexDirection === FlexDirection.Column,
|
|
"node1's second child should have flexDirection Column"
|
|
);
|
|
});
|
|
|
|
test("addChildAt - different flexDirection, first node has children, second has children", () => {
|
|
let node1 = newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Column, undefined, undefined, { name: "node1" }),
|
|
]);
|
|
let node2 = newLayoutNode(FlexDirection.Column, undefined, [
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node2" }),
|
|
]);
|
|
addChildAt(node1, 1, node2);
|
|
assert(node1.data === undefined, "node1 should have no data");
|
|
assert(node1.children!.length === 2, "node1 should have two children");
|
|
assert(node1.children![0].data!.name === "node1", "node1's first child should have node1's data");
|
|
assert(
|
|
node1.children![0].flexDirection === FlexDirection.Column,
|
|
"node1's first child should have flexDirection Column"
|
|
);
|
|
assert(node1.children![1].id === node2.id, "node1's second child should be node2");
|
|
assert(
|
|
node1.children![1].flexDirection === FlexDirection.Column,
|
|
"node1's second child should have flexDirection Column"
|
|
);
|
|
});
|
|
|
|
test("balanceNode - corrects flex directions", () => {
|
|
let node1 = newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1Inner1" }),
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1Inner2" }),
|
|
]);
|
|
const newNode1 = balanceNode(node1).node;
|
|
assert(newNode1 !== undefined, "newNode1 should not be undefined");
|
|
node1 = newNode1;
|
|
assert(node1.data === undefined, "node1 should have no data");
|
|
assert(node1.children![0].flexDirection !== node1.flexDirection);
|
|
});
|
|
|
|
test("balanceNode - collapses nodes with single grandchild 1", () => {
|
|
let node1 = newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Column, undefined, [
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1" }),
|
|
]),
|
|
]);
|
|
const newNode1 = balanceNode(node1).node;
|
|
assert(newNode1 !== undefined, "newNode1 should not be undefined");
|
|
node1 = newNode1;
|
|
assert(node1.children === undefined, "node1 should have no children");
|
|
assert(node1.data!.name === "node1", "node1 should have data 'node1'");
|
|
});
|
|
|
|
test("balanceNode - collapses nodes with single grandchild 2", () => {
|
|
let node2 = newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Column, undefined, [
|
|
newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Column, undefined, undefined, { name: "node2Inner1" }),
|
|
newLayoutNode(FlexDirection.Column, undefined, undefined, { name: "node2Inner2" }),
|
|
]),
|
|
]),
|
|
]);
|
|
const { node: newNode2, leafs } = balanceNode(node2);
|
|
assert(newNode2 !== undefined, "newNode2 should not be undefined");
|
|
node2 = newNode2;
|
|
assert(node2.children!.length === 2, "node2 should have two children");
|
|
assert(node2.children[0].data!.name === "node2Inner1", "node2's first child should have data 'node2Inner1'");
|
|
assert(leafs.length === 2, "leafs should have two leafs");
|
|
assert(leafs[0].data!.name === "node2Inner1", "leafs[0] should have data 'node2Inner1'");
|
|
assert(leafs[1].data!.name === "node2Inner2", "leafs[1] should have data 'node2Inner2'");
|
|
});
|
|
|
|
test("balanceNode - collapses nodes with single grandchild 3", () => {
|
|
let node3 = newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Column, undefined, [
|
|
newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Column, undefined, undefined, { name: "node3" }),
|
|
]),
|
|
]),
|
|
]);
|
|
const newNode3 = balanceNode(node3).node;
|
|
assert(newNode3 !== undefined, "newNode3 should not be undefined");
|
|
node3 = newNode3;
|
|
assert(node3.children === undefined, "node3 should have no children");
|
|
assert(node3.data!.name === "node3", "node3 should have data 'node3'");
|
|
});
|
|
|
|
test("balanceNode - collapses nodes with single grandchild 4", () => {
|
|
let node4 = newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Column, undefined, [
|
|
newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Column, undefined, [
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node4Inner1" }),
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node4Inner2" }),
|
|
]),
|
|
]),
|
|
]),
|
|
]);
|
|
const newNode4 = balanceNode(node4);
|
|
assert(newNode4 !== undefined, "newNode4 should not be undefined");
|
|
node4 = newNode4.node;
|
|
assert(node4.children!.length === 1, "node4 should have one child");
|
|
assert(node4.children![0].children!.length === 2, "node4 should have two grandchildren");
|
|
assert(
|
|
node4.children[0].children![0].data!.name === "node4Inner1",
|
|
"node4's first child should have data 'node4Inner1'"
|
|
);
|
|
});
|
|
|
|
test("findNextInsertLocation", () => {
|
|
const node1 = newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1" }),
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1" }),
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1" }),
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1" }),
|
|
]);
|
|
|
|
const insertLoc1 = findNextInsertLocation(node1, 5);
|
|
assert(insertLoc1.node.id === node1.id, "should insert into node1");
|
|
assert(insertLoc1.index === 4, "should insert into index 4 of node1");
|
|
|
|
const node2Inner5 = newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node2Inner5" });
|
|
const node2 = newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1" }),
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1" }),
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1" }),
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1" }),
|
|
node2Inner5,
|
|
]);
|
|
|
|
const insertLoc2 = findNextInsertLocation(node2, 5);
|
|
assert(insertLoc2.node.id === node2Inner5.id, "should insert into node2Inner5");
|
|
assert(insertLoc2.index === 1, "should insert into index 1 of node2Inner1");
|
|
|
|
const node3Inner5 = newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1" }),
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1" }),
|
|
]);
|
|
const node3Inner4 = newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node3Inner4" });
|
|
const node3 = newLayoutNode(FlexDirection.Row, undefined, [
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1" }),
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1" }),
|
|
newLayoutNode(FlexDirection.Row, undefined, undefined, { name: "node1" }),
|
|
node3Inner4,
|
|
node3Inner5,
|
|
]);
|
|
|
|
const insertLoc3 = findNextInsertLocation(node3, 5);
|
|
assert(insertLoc3.node.id === node3Inner4.id, "should insert into node3Inner4");
|
|
assert(insertLoc3.index === 1, "should insert into index 1 of node3Inner4");
|
|
});
|