feat(maestro): close a tab when its last session dies
Killing the only session in a tab left an empty tab behind for the operator to close by hand. The pane's tab now goes with it, unless it is the last tab (which stays, emptied).
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { emptyLayout, leaves, type Layout } from "./layout.js";
|
||||
import { addTab, emptyLayout, leaves, type Layout } from "./layout.js";
|
||||
import { splitLeaf } from "./layout.js";
|
||||
import { createMultiView, type MultiView } from "./multiview.js";
|
||||
import type { ConnectionPool } from "./connections.js";
|
||||
@@ -333,3 +333,98 @@ describe("openDraft newTab (#100)", () => {
|
||||
expect(leaves(tab.root)).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("multiview closeSession tab cleanup (#137)", () => {
|
||||
beforeEach(() => {
|
||||
stubFetch();
|
||||
document.body.replaceChildren();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it("closes the tab when its last session goes away", () => {
|
||||
const layout = emptyLayout();
|
||||
const first = layout.tabs[0];
|
||||
first.focusedLeafId = splitLeaf(first, "", "s1", "row", true);
|
||||
const second = addTab(layout);
|
||||
second.focusedLeafId = splitLeaf(second, "", "s2", "row", true);
|
||||
const mv = makeMv(layout);
|
||||
document.body.appendChild(mv.el);
|
||||
mv.render();
|
||||
|
||||
mv.closeSession("s2");
|
||||
|
||||
expect(layout.tabs).toHaveLength(1);
|
||||
expect(layout.tabs[0].id).toBe(first.id);
|
||||
expect(layout.activeTabId).toBe(first.id);
|
||||
});
|
||||
|
||||
it("closes a background tab without stealing focus from the active one", () => {
|
||||
const layout = emptyLayout();
|
||||
const background = layout.tabs[0];
|
||||
background.focusedLeafId = splitLeaf(background, "", "s1", "row", true);
|
||||
const active = addTab(layout);
|
||||
active.focusedLeafId = splitLeaf(active, "", "s2", "row", true);
|
||||
const mv = makeMv(layout);
|
||||
document.body.appendChild(mv.el);
|
||||
mv.render();
|
||||
|
||||
mv.closeSession("s1");
|
||||
|
||||
expect(layout.tabs).toHaveLength(1);
|
||||
expect(layout.tabs[0].id).toBe(active.id);
|
||||
expect(layout.activeTabId).toBe(active.id);
|
||||
});
|
||||
|
||||
it("keeps a tab that still holds another pane", () => {
|
||||
const layout = emptyLayout();
|
||||
const tab = layout.tabs[0];
|
||||
const first = splitLeaf(tab, "", "s1", "row", true);
|
||||
tab.focusedLeafId = splitLeaf(tab, first, "s2", "row", true);
|
||||
addTab(layout);
|
||||
layout.activeTabId = tab.id;
|
||||
const mv = makeMv(layout);
|
||||
document.body.appendChild(mv.el);
|
||||
mv.render();
|
||||
|
||||
mv.closeSession("s2");
|
||||
|
||||
expect(layout.tabs).toHaveLength(2);
|
||||
expect(leaves(tab.root)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("keeps a tab holding a draft pane", async () => {
|
||||
const layout = emptyLayout();
|
||||
const tab = layout.tabs[0];
|
||||
const s1 = splitLeaf(tab, "", "s1", "row", true);
|
||||
splitLeaf(tab, s1, null, "row", true);
|
||||
addTab(layout);
|
||||
layout.activeTabId = tab.id;
|
||||
const mv = makeMv(layout);
|
||||
document.body.appendChild(mv.el);
|
||||
mv.render();
|
||||
await flush();
|
||||
|
||||
mv.closeSession("s1");
|
||||
|
||||
expect(layout.tabs).toHaveLength(2);
|
||||
expect(leaves(tab.root)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("never closes the only tab", () => {
|
||||
const layout = emptyLayout();
|
||||
const tab = layout.tabs[0];
|
||||
tab.focusedLeafId = splitLeaf(tab, "", "s1", "row", true);
|
||||
const mv = makeMv(layout);
|
||||
document.body.appendChild(mv.el);
|
||||
mv.render();
|
||||
|
||||
mv.closeSession("s1");
|
||||
|
||||
expect(layout.tabs).toHaveLength(1);
|
||||
expect(layout.tabs[0].root).toBeNull();
|
||||
expect(layout.activeTabId).toBe(tab.id);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -553,6 +553,10 @@ export function createMultiView(opts: MultiViewOptions): MultiView {
|
||||
if (loc.tab.focusedLeafId === loc.leaf.id) {
|
||||
loc.tab.focusedLeafId = leaves(loc.tab.root)[0]?.id ?? null;
|
||||
}
|
||||
// A tab that held nothing but the dead session goes with it (#137): a killed
|
||||
// session should not leave an empty tab behind for the operator to close by
|
||||
// hand. `closeTab` keeps the last tab (emptied) so there is always one.
|
||||
if (!loc.tab.root) closeTab(layout, loc.tab.id);
|
||||
render();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user