Files
2026-07-13 13:09:14 +08:00

370 lines
12 KiB
TypeScript
Vendored

import { test } from "vitest";
import assert from "node:assert/strict";
import {
reconcileLayout,
buildTreeNodesFromLayout,
createGroup,
deleteGroup,
renameGroup,
moveConnectionToGroup,
reorderEntry,
toggleGroupCollapsed,
appendConnectionToLayout,
removeConnectionFromSidebarLayout,
emptyLayout,
remapSidebarLayoutConnectionIds,
collapseAllGroups,
} from "../../apps/desktop/src/lib/sidebar/sidebarLayout.ts";
import type { ConnectionConfig, SidebarLayout } from "../../apps/desktop/src/types/database.ts";
function conn(id: string, name?: string): ConnectionConfig {
return {
id,
name: name || id,
db_type: "postgres",
host: "localhost",
port: 5432,
username: "user",
password: "",
};
}
function groupConnectionIds(entry: SidebarLayout["order"][number]): string[] {
assert.equal(entry.type, "group");
return (entry.children ?? []).filter((child) => child.type === "connection").map((child) => child.id);
}
// --- reconcileLayout ---
test("reconcileLayout returns all connections ungrouped when layout is null", () => {
const result = reconcileLayout(["a", "b", "c"], null);
assert.deepEqual(result.groups, []);
assert.deepEqual(result.order, [
{ type: "connection", id: "a" },
{ type: "connection", id: "b" },
{ type: "connection", id: "c" },
]);
});
test("reconcileLayout appends new connections not in layout", () => {
const layout: SidebarLayout = {
groups: [],
order: [{ type: "connection", id: "a" }],
};
const result = reconcileLayout(["a", "b"], layout);
assert.equal(result.order.length, 2);
assert.deepEqual(result.order[1], { type: "connection", id: "b" });
});
test("reconcileLayout removes stale connections from layout", () => {
const layout: SidebarLayout = {
groups: [{ id: "g1", name: "Group", collapsed: false }],
order: [
{ type: "group", id: "g1", connectionIds: ["a", "removed"] },
{ type: "connection", id: "b" },
],
};
const result = reconcileLayout(["a", "b"], layout);
const groupEntry = result.order.find((e) => e.type === "group");
assert.ok(groupEntry && groupEntry.type === "group");
assert.deepEqual(groupConnectionIds(groupEntry), ["a"]);
assert.equal(result.order.length, 2);
});
test("reconcileLayout removes groups with no order entry", () => {
const layout: SidebarLayout = {
groups: [
{ id: "g1", name: "Used", collapsed: false },
{ id: "g2", name: "Orphan", collapsed: false },
],
order: [{ type: "group", id: "g1", connectionIds: ["a"] }],
};
const result = reconcileLayout(["a"], layout);
assert.equal(result.groups.length, 1);
assert.equal(result.groups[0].id, "g1");
});
test("remapSidebarLayoutConnectionIds preserves imported grouping with new connection ids", () => {
const layout: SidebarLayout = {
groups: [{ id: "g1", name: "Imported", collapsed: false }],
order: [
{ type: "connection", id: "old-root" },
{ type: "group", id: "g1", connectionIds: ["old-a", "old-b"] },
],
};
const remapped = remapSidebarLayoutConnectionIds(
layout,
new Map([
["old-root", "new-root"],
["old-a", "new-a"],
["old-b", "new-b"],
]),
);
const reconciled = reconcileLayout(["new-root", "new-a", "new-b"], remapped);
assert.deepEqual(reconciled.order, [
{ type: "connection", id: "new-root" },
{
type: "group",
id: "g1",
children: [
{ type: "connection", id: "new-a" },
{ type: "connection", id: "new-b" },
],
},
]);
});
// --- buildTreeNodesFromLayout ---
test("buildTreeNodesFromLayout creates group nodes with connection children", () => {
const layout: SidebarLayout = {
groups: [{ id: "g1", name: "Production", collapsed: false }],
order: [
{ type: "group", id: "g1", connectionIds: ["a", "b"] },
{ type: "connection", id: "c" },
],
};
const connections = [conn("a", "Server A"), conn("b", "Server B"), conn("c", "Server C")];
const nodes = buildTreeNodesFromLayout(layout, connections, new Set());
assert.equal(nodes.length, 2);
assert.equal(nodes[0].type, "connection-group");
assert.equal(nodes[0].label, "Production");
assert.equal(nodes[0].children?.length, 2);
assert.equal(nodes[0].isExpanded, true);
assert.equal(nodes[1].type, "connection");
assert.equal(nodes[1].id, "c");
});
test("buildTreeNodesFromLayout respects collapsed groups", () => {
const layout: SidebarLayout = {
groups: [{ id: "g1", name: "G", collapsed: true }],
order: [{ type: "group", id: "g1", connectionIds: ["a"] }],
};
const nodes = buildTreeNodesFromLayout(layout, [conn("a")], new Set());
assert.equal(nodes[0].isExpanded, false);
});
test("buildTreeNodesFromLayout applies pinning within groups", () => {
const layout: SidebarLayout = {
groups: [{ id: "g1", name: "G", collapsed: false }],
order: [{ type: "group", id: "g1", connectionIds: ["a", "b"] }],
};
const nodes = buildTreeNodesFromLayout(layout, [conn("a"), conn("b")], new Set(["b"]));
const children = nodes[0].children!;
assert.equal(children[0].id, "b");
assert.equal(children[1].id, "a");
});
// --- createGroup ---
test("createGroup adds a new empty group", () => {
const layout = emptyLayout();
const result = createGroup(layout, "Dev");
assert.equal(result.layout.groups.length, 1);
assert.equal(result.layout.groups[0].name, "Dev");
assert.equal(result.layout.order.length, 1);
assert.ok(result.layout.order[0].type === "group");
});
test("createGroup expands parent group when adding a subgroup", () => {
const layout: SidebarLayout = {
groups: [{ id: "g1", name: "Parent", collapsed: true }],
order: [{ type: "group", id: "g1", children: [] }],
};
const result = createGroup(layout, "Child", "g1");
const parentGroup = result.layout.groups.find((group) => group.id === "g1");
const parentEntry = result.layout.order[0];
assert.equal(parentGroup?.collapsed, false);
assert.equal(parentEntry.type, "group");
assert.deepEqual(parentEntry.type === "group" ? parentEntry.children : undefined, [{ type: "group", id: result.groupId, children: [] }]);
});
// --- renameGroup ---
test("renameGroup updates group name", () => {
const { layout, groupId } = createGroup(emptyLayout(), "Old");
const result = renameGroup(layout, groupId, "New");
assert.equal(result.groups[0].name, "New");
});
// --- deleteGroup ---
test("deleteGroup moves connections to ungrouped", () => {
let layout = emptyLayout();
layout = appendConnectionToLayout(layout, "a");
const { layout: withGroup, groupId } = createGroup(layout, "G");
const moved = moveConnectionToGroup(withGroup, "a", groupId);
const result = deleteGroup(moved, groupId);
assert.equal(result.groups.length, 0);
assert.deepEqual(result.order, [{ type: "connection", id: "a" }]);
});
// --- toggleGroupCollapsed ---
test("toggleGroupCollapsed flips collapsed state", () => {
const { layout, groupId } = createGroup(emptyLayout(), "G");
assert.equal(layout.groups[0].collapsed, false);
const toggled = toggleGroupCollapsed(layout, groupId);
assert.equal(toggled.groups[0].collapsed, true);
});
test("collapseAllGroups keeps other groups collapsed after one group is reopened", async () => {
const layout: SidebarLayout = {
groups: [
{ id: "g1", name: "G1", collapsed: false },
{ id: "g2", name: "G2", collapsed: false },
],
order: [
{ type: "group", id: "g1", children: [{ type: "connection", id: "a" }] },
{ type: "group", id: "g2", children: [{ type: "connection", id: "b" }] },
],
};
const collapsed = collapseAllGroups(layout);
assert.deepEqual(
collapsed.groups.map((group) => [group.id, group.collapsed]),
[
["g1", true],
["g2", true],
],
);
const reopenedFirst = toggleGroupCollapsed(collapsed, "g1");
assert.deepEqual(
reopenedFirst.groups.map((group) => [group.id, group.collapsed]),
[
["g1", false],
["g2", true],
],
);
});
// --- moveConnectionToGroup ---
test("moveConnectionToGroup moves connection into a group", () => {
let layout: SidebarLayout = {
groups: [{ id: "g1", name: "G", collapsed: false }],
order: [
{ type: "group", id: "g1", connectionIds: [] },
{ type: "connection", id: "a" },
],
};
const result = moveConnectionToGroup(layout, "a", "g1");
const groupEntry = result.order.find((e) => e.type === "group" && e.id === "g1");
assert.ok(groupEntry && groupEntry.type === "group");
assert.deepEqual(groupConnectionIds(groupEntry), ["a"]);
assert.equal(result.order.length, 1);
});
test("moveConnectionToGroup moves connection out of a group", () => {
let layout: SidebarLayout = {
groups: [{ id: "g1", name: "G", collapsed: false }],
order: [{ type: "group", id: "g1", connectionIds: ["a"] }],
};
const result = moveConnectionToGroup(layout, "a", null);
assert.equal(result.order.length, 2);
assert.deepEqual(result.order[1], { type: "connection", id: "a" });
});
// --- reorderEntry ---
test("reorderEntry moves connection before another", () => {
const layout: SidebarLayout = {
groups: [],
order: [
{ type: "connection", id: "a" },
{ type: "connection", id: "b" },
{ type: "connection", id: "c" },
],
};
const result = reorderEntry(layout, "c", "a", "before");
assert.deepEqual(
result.order.map((e) => e.id),
["c", "a", "b"],
);
});
test("reorderEntry moves connection after another", () => {
const layout: SidebarLayout = {
groups: [],
order: [
{ type: "connection", id: "a" },
{ type: "connection", id: "b" },
{ type: "connection", id: "c" },
],
};
const result = reorderEntry(layout, "a", "b", "after");
assert.deepEqual(
result.order.map((e) => e.id),
["b", "a", "c"],
);
});
test("reorderEntry moves connection inside a group", () => {
const layout: SidebarLayout = {
groups: [{ id: "g1", name: "G", collapsed: false }],
order: [
{ type: "group", id: "g1", connectionIds: [] },
{ type: "connection", id: "a" },
],
};
const result = reorderEntry(layout, "a", "g1", "inside");
assert.equal(result.order.length, 1);
const groupEntry = result.order[0];
assert.ok(groupEntry.type === "group");
assert.deepEqual(groupConnectionIds(groupEntry), ["a"]);
});
test("reorderEntry is a no-op when dragging to same position", () => {
const layout: SidebarLayout = {
groups: [],
order: [{ type: "connection", id: "a" }],
};
const result = reorderEntry(layout, "a", "a", "before");
assert.deepEqual(result, layout);
});
// --- appendConnectionToLayout / removeConnectionFromSidebarLayout ---
test("appendConnectionToLayout adds to the end", () => {
const layout = appendConnectionToLayout(emptyLayout(), "x");
assert.equal(layout.order.length, 1);
assert.deepEqual(layout.order[0], { type: "connection", id: "x" });
});
test("appendConnectionToLayout adds to target group and expands it", () => {
const layout: SidebarLayout = {
groups: [{ id: "g1", name: "G", collapsed: true }],
order: [{ type: "group", id: "g1", connectionIds: ["a"] }],
};
const result = appendConnectionToLayout(layout, "b", "g1");
const groupEntry = result.order[0];
assert.equal(result.groups[0].collapsed, false);
assert.ok(groupEntry.type === "group");
assert.deepEqual(groupConnectionIds(groupEntry), ["a", "b"]);
});
test("removeConnectionFromSidebarLayout removes from ungrouped", () => {
let layout = appendConnectionToLayout(emptyLayout(), "x");
layout = removeConnectionFromSidebarLayout(layout, "x");
assert.equal(layout.order.length, 0);
});
test("removeConnectionFromSidebarLayout removes from inside a group", () => {
const layout: SidebarLayout = {
groups: [{ id: "g1", name: "G", collapsed: false }],
order: [{ type: "group", id: "g1", connectionIds: ["a", "b"] }],
};
const result = removeConnectionFromSidebarLayout(layout, "a");
const groupEntry = result.order[0];
assert.ok(groupEntry.type === "group");
assert.deepEqual(groupConnectionIds(groupEntry), ["b"]);
});