Add SSH connections: saved hosts manager and hierarchical shell picker
This commit is contained in:
parent
4e5bc7e081
commit
872fb0e80e
14 changed files with 1324 additions and 171 deletions
|
|
@ -9,6 +9,7 @@ import {
|
|||
leafCount,
|
||||
walkLeaves,
|
||||
changeDistro,
|
||||
setLeafShell,
|
||||
changeLabel,
|
||||
toggleBroadcast,
|
||||
adjustFontSize,
|
||||
|
|
@ -38,14 +39,16 @@ function leafDistros(root: TreeNode): (string | undefined)[] {
|
|||
}
|
||||
|
||||
describe("newLeaf", () => {
|
||||
it("returns a leaf with a unique id and no extra metadata", () => {
|
||||
it("returns a leaf with a unique id, default shellKind=wsl, no other metadata", () => {
|
||||
const a = newLeaf();
|
||||
const b = newLeaf();
|
||||
expect(a.kind).toBe("leaf");
|
||||
expect(typeof a.id).toBe("string");
|
||||
expect(a.id).not.toEqual(b.id);
|
||||
expect(a.shellKind).toBe("wsl");
|
||||
expect(a.distro).toBeUndefined();
|
||||
expect(a.cwd).toBeUndefined();
|
||||
expect(a.sshHostId).toBeUndefined();
|
||||
expect(a.label).toBeUndefined();
|
||||
expect(a.broadcast).toBeUndefined();
|
||||
});
|
||||
|
|
@ -56,6 +59,14 @@ describe("newLeaf", () => {
|
|||
expect(leaf.cwd).toBe("/home");
|
||||
expect(leaf.label).toBe("ml");
|
||||
});
|
||||
|
||||
it("respects an explicit non-wsl shellKind", () => {
|
||||
const ps = newLeaf({ shellKind: "powershell" });
|
||||
expect(ps.shellKind).toBe("powershell");
|
||||
const ssh = newLeaf({ shellKind: "ssh", sshHostId: "host-1" });
|
||||
expect(ssh.shellKind).toBe("ssh");
|
||||
expect(ssh.sshHostId).toBe("host-1");
|
||||
});
|
||||
});
|
||||
|
||||
describe("newSplit", () => {
|
||||
|
|
@ -232,10 +243,11 @@ describe("walkLeaves", () => {
|
|||
});
|
||||
|
||||
describe("changeDistro", () => {
|
||||
it("sets the distro on the leaf", () => {
|
||||
const leaf = newLeaf({ distro: "Ubuntu" });
|
||||
const next = changeDistro(leaf, leaf.id, "Debian");
|
||||
expect((next as LeafNode).distro).toBe("Debian");
|
||||
it("sets the distro on the leaf and forces shellKind back to wsl", () => {
|
||||
const leaf = newLeaf({ shellKind: "powershell" });
|
||||
const next = changeDistro(leaf, leaf.id, "Debian") as LeafNode;
|
||||
expect(next.distro).toBe("Debian");
|
||||
expect(next.shellKind).toBe("wsl");
|
||||
});
|
||||
|
||||
it("MUST swap the leaf id (so {#key} remounts XtermPane and kills the PTY)", () => {
|
||||
|
|
@ -254,6 +266,52 @@ describe("changeDistro", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("setLeafShell", () => {
|
||||
it("switches a wsl leaf to powershell (and clears wsl-specific fields)", () => {
|
||||
const leaf = newLeaf({ distro: "Ubuntu", cwd: "/work", label: "keep" });
|
||||
const next = setLeafShell(leaf, leaf.id, { shellKind: "powershell" }) as LeafNode;
|
||||
expect(next.shellKind).toBe("powershell");
|
||||
expect(next.distro).toBeUndefined();
|
||||
expect(next.cwd).toBeUndefined();
|
||||
expect(next.label).toBe("keep");
|
||||
});
|
||||
|
||||
it("switches a leaf to ssh and records sshHostId", () => {
|
||||
const leaf = newLeaf({ distro: "Ubuntu" });
|
||||
const next = setLeafShell(leaf, leaf.id, {
|
||||
shellKind: "ssh",
|
||||
sshHostId: "host-abc",
|
||||
}) as LeafNode;
|
||||
expect(next.shellKind).toBe("ssh");
|
||||
expect(next.sshHostId).toBe("host-abc");
|
||||
expect(next.distro).toBeUndefined();
|
||||
});
|
||||
|
||||
it("MUST swap the leaf id (forces PTY respawn)", () => {
|
||||
const leaf = newLeaf({ shellKind: "powershell" });
|
||||
const next = setLeafShell(leaf, leaf.id, {
|
||||
shellKind: "ssh",
|
||||
sshHostId: "h1",
|
||||
}) as LeafNode;
|
||||
expect(next.id).not.toBe(leaf.id);
|
||||
});
|
||||
|
||||
it("preserves label / broadcast / fontSizeOffset across the shell change", () => {
|
||||
const leaf = newLeaf({
|
||||
distro: "Ubuntu",
|
||||
label: "my pane",
|
||||
broadcast: true,
|
||||
fontSizeOffset: 2,
|
||||
});
|
||||
const next = setLeafShell(leaf, leaf.id, {
|
||||
shellKind: "powershell",
|
||||
}) as LeafNode;
|
||||
expect(next.label).toBe("my pane");
|
||||
expect(next.broadcast).toBe(true);
|
||||
expect(next.fontSizeOffset).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("changeLabel", () => {
|
||||
it("sets a label", () => {
|
||||
const leaf = newLeaf();
|
||||
|
|
@ -466,10 +524,41 @@ describe("serialize / deserialize", () => {
|
|||
).toBeNull(); // missing ratio + children
|
||||
});
|
||||
|
||||
it("accepts a minimal leaf shape", () => {
|
||||
it("accepts a minimal leaf shape (backfilling shellKind for legacy data)", () => {
|
||||
expect(deserialize('{"kind": "leaf", "id": "x"}')).toEqual({
|
||||
kind: "leaf",
|
||||
id: "x",
|
||||
shellKind: "wsl",
|
||||
});
|
||||
});
|
||||
|
||||
it("migrates legacy PowerShell-sentinel leaves to shellKind=powershell", () => {
|
||||
const legacy = JSON.stringify({
|
||||
kind: "split",
|
||||
id: "s1",
|
||||
orientation: "h",
|
||||
ratio: 0.5,
|
||||
a: { kind: "leaf", id: "a", distro: "PowerShell" },
|
||||
b: { kind: "leaf", id: "b", distro: "Ubuntu" },
|
||||
});
|
||||
const back = deserialize(legacy) as SplitNode;
|
||||
const left = back.a as LeafNode;
|
||||
const right = back.b as LeafNode;
|
||||
expect(left.shellKind).toBe("powershell");
|
||||
expect(left.distro).toBeUndefined();
|
||||
expect(right.shellKind).toBe("wsl");
|
||||
expect(right.distro).toBe("Ubuntu");
|
||||
});
|
||||
|
||||
it("leaves shellKind alone on already-migrated leaves", () => {
|
||||
const fresh = JSON.stringify({
|
||||
kind: "leaf",
|
||||
id: "x",
|
||||
shellKind: "ssh",
|
||||
sshHostId: "h-1",
|
||||
});
|
||||
const back = deserialize(fresh) as LeafNode;
|
||||
expect(back.shellKind).toBe("ssh");
|
||||
expect(back.sshHostId).toBe("h-1");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue