270 lines
9.3 KiB
TypeScript
270 lines
9.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { DockviewApi } from "dockview-react";
|
|
import { buildExtraPanelActions } from "./dockview-panel-actions";
|
|
import { CENTER_GROUP } from "./layout-manager";
|
|
import { makeApi, makeStore } from "./dockview-panel-actions.test-utils";
|
|
import type { MockPanel } from "./dockview-panel-actions.test-utils";
|
|
|
|
describe("addPRPanel — dedup with legacy auto-shown panel", () => {
|
|
const PR_KEY = "testorg/testrepo/101";
|
|
const OTHER_PR_KEY = "testorg/testrepo/202";
|
|
const LEGACY_PR_ID = "pr-detail";
|
|
const KEYED_PR_ID = `pr-detail|${PR_KEY}`;
|
|
const OTHER_KEYED_PR_ID = `pr-detail|${OTHER_PR_KEY}`;
|
|
|
|
function buildExtra(api: DockviewApi) {
|
|
const store = makeStore(api);
|
|
return { api, actions: buildExtraPanelActions(store.get) };
|
|
}
|
|
|
|
/** Canonical PR Details panels retain the key currently shown for the task. */
|
|
function seedLegacyPanel(api: DockviewApi, prKey: string): void {
|
|
api.addPanel({
|
|
id: LEGACY_PR_ID,
|
|
component: "pr-detail",
|
|
title: "Pull Request",
|
|
params: { prKey },
|
|
position: { referenceGroup: CENTER_GROUP },
|
|
});
|
|
}
|
|
|
|
it("reuses the legacy panel when the requested PR is what it's already showing", () => {
|
|
const { api, actions } = buildExtra(makeApi());
|
|
seedLegacyPanel(api, PR_KEY);
|
|
|
|
actions.addPRPanel(PR_KEY);
|
|
|
|
const prPanels = api.panels.filter(
|
|
(p) => p.id === LEGACY_PR_ID || p.id.startsWith(`${LEGACY_PR_ID}|`),
|
|
);
|
|
expect(prPanels).toHaveLength(1);
|
|
expect(api.getPanel(KEYED_PR_ID)).toBeUndefined();
|
|
expect((api.getPanel(LEGACY_PR_ID) as unknown as MockPanel).isActive).toBe(true);
|
|
});
|
|
|
|
it("opens a distinct tab for a different PR instead of overwriting the legacy tab", () => {
|
|
const { api, actions } = buildExtra(makeApi());
|
|
seedLegacyPanel(api, PR_KEY);
|
|
|
|
// Multi-repo "+" menu click for a PR other than the one the legacy tab
|
|
// is showing must NOT repurpose that tab — it must open its own tab.
|
|
actions.addPRPanel(OTHER_PR_KEY);
|
|
|
|
const legacy = api.getPanel(LEGACY_PR_ID) as unknown as MockPanel;
|
|
expect(legacy.params.prKey).toBe(PR_KEY);
|
|
|
|
const other = api.getPanel(OTHER_KEYED_PR_ID) as unknown as MockPanel;
|
|
expect(other).toBeDefined();
|
|
expect(other.isActive).toBe(true);
|
|
|
|
const prPanels = api.panels.filter(
|
|
(p) => p.id === LEGACY_PR_ID || p.id.startsWith(`${LEGACY_PR_ID}|`),
|
|
);
|
|
expect(prPanels).toHaveLength(2);
|
|
});
|
|
|
|
it("creates a new keyed panel when no legacy panel exists", () => {
|
|
const { api, actions } = buildExtra(makeApi());
|
|
|
|
actions.addPRPanel(PR_KEY);
|
|
|
|
expect(api.getPanel(KEYED_PR_ID)).toBeDefined();
|
|
expect(api.getPanel(LEGACY_PR_ID)).toBeUndefined();
|
|
});
|
|
|
|
it("focuses an existing keyed panel exactly once", () => {
|
|
const { api, actions } = buildExtra(makeApi());
|
|
|
|
actions.addPRPanel(PR_KEY);
|
|
actions.addPRPanel(PR_KEY);
|
|
|
|
const prPanels = api.panels.filter(
|
|
(p) => p.id === LEGACY_PR_ID || p.id.startsWith(`${LEGACY_PR_ID}|`),
|
|
);
|
|
expect(prPanels).toHaveLength(1);
|
|
const keyed = api.getPanel(KEYED_PR_ID) as unknown as MockPanel;
|
|
expect(keyed.isActive).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("addPRPanel — layout-owned group placement", () => {
|
|
const PR_KEY = "testorg/testrepo/202";
|
|
const KEYED_PR_ID = `pr-detail|${PR_KEY}`;
|
|
const SESSION_ID = "s-1";
|
|
const SESSION_PANEL_ID = `session:${SESSION_ID}`;
|
|
const SESSION_GROUP = "group-session-host";
|
|
const PR_DETAILS_GROUP = "group-configured-pr-details";
|
|
|
|
function buildExtra(api: DockviewApi) {
|
|
const store = makeStore(api);
|
|
return { api, actions: buildExtraPanelActions(store.get) };
|
|
}
|
|
|
|
function seedSessionInGroup(api: DockviewApi, groupId: string): void {
|
|
api.addPanel({
|
|
id: SESSION_PANEL_ID,
|
|
component: "chat",
|
|
title: "Session",
|
|
params: { sessionId: SESSION_ID },
|
|
position: { referenceGroup: groupId },
|
|
});
|
|
}
|
|
|
|
function seedCanonicalPRDetails(api: DockviewApi, groupId = PR_DETAILS_GROUP): void {
|
|
api.addPanel({
|
|
id: "pr-detail",
|
|
component: "pr-detail",
|
|
title: "PR Details",
|
|
position: { referenceGroup: groupId },
|
|
});
|
|
}
|
|
|
|
it("places a new PR tab in PR Details' configured group, not beside Agent", () => {
|
|
const { api, actions } = buildExtra(makeApi());
|
|
seedSessionInGroup(api, SESSION_GROUP);
|
|
seedCanonicalPRDetails(api);
|
|
|
|
actions.addPRPanel(PR_KEY);
|
|
|
|
const pr = api.getPanel(KEYED_PR_ID) as unknown as MockPanel;
|
|
expect(pr).toBeDefined();
|
|
expect(pr.group.id).toBe(PR_DETAILS_GROUP);
|
|
expect(pr.group.id).not.toBe(SESSION_GROUP);
|
|
});
|
|
|
|
it("falls back to centerGroupId when no PR Details panel exists", () => {
|
|
const { api, actions } = buildExtra(makeApi());
|
|
actions.addPRPanel(PR_KEY);
|
|
|
|
const pr = api.getPanel(KEYED_PR_ID) as unknown as MockPanel;
|
|
expect(pr).toBeDefined();
|
|
expect(pr.group.id).toBe(CENTER_GROUP);
|
|
});
|
|
|
|
it("focuses an existing keyed PR tab without relocating it", () => {
|
|
const { api, actions } = buildExtra(makeApi());
|
|
seedCanonicalPRDetails(api);
|
|
api.addPanel({
|
|
id: KEYED_PR_ID,
|
|
component: "pr-detail",
|
|
title: "Pull Request",
|
|
params: { prKey: PR_KEY },
|
|
position: { referenceGroup: SESSION_GROUP },
|
|
});
|
|
|
|
actions.addPRPanel(PR_KEY);
|
|
|
|
const pr = api.getPanel(KEYED_PR_ID) as unknown as MockPanel;
|
|
expect(pr.group.id).toBe(SESSION_GROUP);
|
|
expect(pr.isActive).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("addMRPanel — layout-owned review tabs", () => {
|
|
const MR_KEY = "https://gitlab.example.test|platform/kandev|81";
|
|
const OTHER_MR_KEY = "https://gitlab.example.test|platform/kandev|82";
|
|
const LEGACY_MR_ID = "mr-detail";
|
|
|
|
function buildExtra(api: DockviewApi) {
|
|
const store = makeStore(api);
|
|
return { api, actions: buildExtraPanelActions(store.get) };
|
|
}
|
|
|
|
function seedLegacyPanel(api: DockviewApi, mrKey: string): void {
|
|
api.addPanel({
|
|
id: LEGACY_MR_ID,
|
|
component: "mr-detail",
|
|
title: "Merge Request",
|
|
params: { mrKey },
|
|
position: { referenceGroup: CENTER_GROUP },
|
|
});
|
|
}
|
|
|
|
it("focuses a matching legacy panel in place", () => {
|
|
const { api, actions } = buildExtra(makeApi());
|
|
seedLegacyPanel(api, MR_KEY);
|
|
|
|
actions.addMRPanel(MR_KEY);
|
|
|
|
expect(api.panels.filter((panel) => panel.id.startsWith(LEGACY_MR_ID))).toHaveLength(1);
|
|
expect(api.getPanel(`${LEGACY_MR_ID}|${MR_KEY}`)).toBeUndefined();
|
|
expect((api.getPanel(LEGACY_MR_ID) as unknown as MockPanel).isActive).toBe(true);
|
|
});
|
|
|
|
it("opens a keyed panel in the canonical PR Details group", () => {
|
|
const { api, actions } = buildExtra(makeApi());
|
|
seedLegacyPanel(api, MR_KEY);
|
|
api.addPanel({
|
|
id: "pr-detail",
|
|
component: "pr-detail",
|
|
title: "PR Details",
|
|
position: { referenceGroup: "group-configured-pr-details" },
|
|
});
|
|
|
|
actions.addMRPanel(OTHER_MR_KEY);
|
|
|
|
const keyed = api.getPanel(`${LEGACY_MR_ID}|${OTHER_MR_KEY}`) as unknown as MockPanel;
|
|
expect(keyed).toBeDefined();
|
|
expect(keyed.group.id).toBe("group-configured-pr-details");
|
|
expect(api.panels.filter((panel) => panel.id.startsWith(LEGACY_MR_ID))).toHaveLength(2);
|
|
});
|
|
});
|
|
|
|
const PLUGIN_ID = "kandev-plugin-notes";
|
|
const PLUGIN_PANEL_KEY = "notes";
|
|
const PLUGIN_PANEL_TITLE = "Notes";
|
|
const PLUGIN_PANEL_ID = "plugin:kandev-plugin-notes:notes";
|
|
|
|
describe("addPluginPanel / closePluginPanels", () => {
|
|
function buildExtra(api: DockviewApi) {
|
|
const store = makeStore(api);
|
|
return { api, actions: buildExtraPanelActions(store.get) };
|
|
}
|
|
|
|
it("opens a plugin panel beside chat by default, with the shared plugin-panel component/tab", () => {
|
|
const { api, actions } = buildExtra(makeApi());
|
|
|
|
actions.addPluginPanel(PLUGIN_ID, PLUGIN_PANEL_KEY, PLUGIN_PANEL_TITLE);
|
|
|
|
const panel = api.getPanel(PLUGIN_PANEL_ID) as unknown as MockPanel;
|
|
expect(panel).toBeDefined();
|
|
expect(panel.title).toBe(PLUGIN_PANEL_TITLE);
|
|
expect(panel.params).toEqual({ pluginId: PLUGIN_ID, panelKey: PLUGIN_PANEL_KEY });
|
|
});
|
|
|
|
it("focuses an already-open plugin panel instead of duplicating it", () => {
|
|
const { api, actions } = buildExtra(makeApi());
|
|
|
|
actions.addPluginPanel(PLUGIN_ID, PLUGIN_PANEL_KEY, PLUGIN_PANEL_TITLE);
|
|
(api.getPanel(PLUGIN_PANEL_ID) as unknown as MockPanel).isActive = false;
|
|
actions.addPluginPanel(PLUGIN_ID, PLUGIN_PANEL_KEY, PLUGIN_PANEL_TITLE);
|
|
|
|
expect(api.panels.filter((p) => p.id === PLUGIN_PANEL_ID)).toHaveLength(1);
|
|
expect((api.getPanel(PLUGIN_PANEL_ID) as unknown as MockPanel).isActive).toBe(true);
|
|
});
|
|
|
|
it("respects inCenter and quiet options", () => {
|
|
const { api, actions } = buildExtra(makeApi());
|
|
|
|
actions.addPluginPanel(PLUGIN_ID, PLUGIN_PANEL_KEY, PLUGIN_PANEL_TITLE, {
|
|
inCenter: true,
|
|
quiet: true,
|
|
});
|
|
|
|
const panel = api.getPanel(PLUGIN_PANEL_ID) as unknown as MockPanel;
|
|
expect(panel.group.id).toBe(CENTER_GROUP);
|
|
expect(panel.isActive).toBe(false);
|
|
});
|
|
|
|
it("closePluginPanels removes only the target plugin's panels", () => {
|
|
const { api, actions } = buildExtra(makeApi());
|
|
actions.addPluginPanel(PLUGIN_ID, PLUGIN_PANEL_KEY, PLUGIN_PANEL_TITLE);
|
|
actions.addPluginPanel("kandev-plugin-other", "widget", "Widget");
|
|
|
|
actions.closePluginPanels(PLUGIN_ID);
|
|
|
|
expect(api.getPanel(PLUGIN_PANEL_ID)).toBeUndefined();
|
|
expect(api.getPanel("plugin:kandev-plugin-other:widget")).toBeDefined();
|
|
});
|
|
});
|