449 lines · 15.6 KB · TypeScript
RawDownload
import { scopeThreadRef } from "@t3tools/client-runtime/environment";import { type EnvironmentId, ThreadId } from "@t3tools/contracts";import { beforeEach, describe, expect, it } from "vite-plus/test"; import {  migratePersistedRightPanelState,  selectActiveRightPanel,  selectActiveRightPanelSurface,  selectThreadRightPanelState,  useRightPanelStore,} from "./rightPanelStore"; const refA = scopeThreadRef("env-1" as EnvironmentId, ThreadId.make("thread-A"));const refB = scopeThreadRef("env-1" as EnvironmentId, ThreadId.make("thread-B")); beforeEach(() => {  useRightPanelStore.setState({ byThreadKey: {} });}); describe("rightPanelStore", () => {  it("drops the legacy singleton terminal surface during migration", () => {    expect(      migratePersistedRightPanelState({        byThreadKey: {          "env-1:thread-A": {            activeSurfaceId: "terminal",            surfaces: [              { id: "browser:tab-a", kind: "preview", resourceId: "tab-a" },              { id: "terminal", kind: "terminal" },            ],          },        },      }),    ).toEqual({      byThreadKey: {        "env-1:thread-A": {          isOpen: false,          activeSurfaceId: null,          surfaces: [{ id: "browser:tab-a", kind: "preview", resourceId: "tab-a" }],        },      },    });  });   it("upgrades saved single-session terminal surfaces to split-capable surfaces", () => {    expect(      migratePersistedRightPanelState({        byThreadKey: {          "env-1:thread-A": {            isOpen: true,            activeSurfaceId: "terminal:term-1",            surfaces: [{ id: "terminal:term-1", kind: "terminal", resourceId: "term-1" }],          },        },      }),    ).toEqual({      byThreadKey: {        "env-1:thread-A": {          isOpen: true,          activeSurfaceId: "terminal:term-1",          surfaces: [            {              id: "terminal:term-1",              kind: "terminal",              resourceId: "term-1",              terminalIds: ["term-1"],              activeTerminalId: "term-1",            },          ],        },      },    });  });   it("upgrades saved file surfaces with neutral reveal state", () => {    expect(      migratePersistedRightPanelState({        byThreadKey: {          "env-1:thread-A": {            isOpen: true,            activeSurfaceId: "file:src/index.ts",            surfaces: [{ id: "file:src/index.ts", kind: "file", relativePath: "src/index.ts" }],          },        },      }),    ).toEqual({      byThreadKey: {        "env-1:thread-A": {          isOpen: true,          activeSurfaceId: "file:src/index.ts",          surfaces: [            {              id: "file:src/index.ts",              kind: "file",              relativePath: "src/index.ts",              revealLine: null,              revealRequestId: 0,            },          ],        },      },    });  });   it("open sets the active panel for a thread", () => {    useRightPanelStore.getState().open(refA, "preview");    expect(selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, refA)).toBe("preview");    expect(selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, refB)).toBeNull();  });   it("opening a different kind keeps both surfaces and activates the new one", () => {    useRightPanelStore.getState().open(refA, "plan");    useRightPanelStore.getState().open(refA, "preview");    expect(selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, refA)).toBe("preview");    expect(      selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA).surfaces,    ).toHaveLength(2);  });   it("reopening an inactive singleton activates its existing surface", () => {    useRightPanelStore.getState().open(refA, "diff");    useRightPanelStore.getState().open(refA, "plan");    useRightPanelStore.getState().open(refA, "diff");     expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      isOpen: true,      activeSurfaceId: "diff",      surfaces: [        { id: "diff", kind: "diff" },        { id: "plan", kind: "plan" },      ],    });  });   it("keeps files as a singleton surface", () => {    useRightPanelStore.getState().open(refA, "files");    useRightPanelStore.getState().open(refA, "files");    expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      isOpen: true,      activeSurfaceId: "files",      surfaces: [{ id: "files", kind: "files" }],    });  });   it("replaces the standalone explorer with peer file surfaces", () => {    useRightPanelStore.getState().open(refA, "files");    useRightPanelStore.getState().openFile(refA, "src/index.ts");    useRightPanelStore.getState().openFile(refA, "src/index.ts");    useRightPanelStore.getState().openFile(refA, "README.md");     expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      isOpen: true,      activeSurfaceId: "file:README.md",      surfaces: [        {          id: "file:src/index.ts",          kind: "file",          relativePath: "src/index.ts",          revealLine: null,          revealRequestId: 2,        },        {          id: "file:README.md",          kind: "file",          relativePath: "README.md",          revealLine: null,          revealRequestId: 1,        },      ],    });  });   it("updates line reveal requests when reopening a file surface", () => {    useRightPanelStore.getState().openFile(refA, "src/index.ts", 42);    useRightPanelStore.getState().openFile(refA, "src/index.ts", 87);     expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      isOpen: true,      activeSurfaceId: "file:src/index.ts",      surfaces: [        {          id: "file:src/index.ts",          kind: "file",          relativePath: "src/index.ts",          revealLine: 87,          revealRequestId: 2,        },      ],    });     useRightPanelStore.getState().openFile(refA, "src/index.ts");     expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      isOpen: true,      activeSurfaceId: "file:src/index.ts",      surfaces: [        {          id: "file:src/index.ts",          kind: "file",          relativePath: "src/index.ts",          revealLine: null,          revealRequestId: 3,        },      ],    });  });   it("removes persisted file surfaces when their workspace no longer exists", () => {    useRightPanelStore.getState().openFile(refA, "src/index.ts");    useRightPanelStore.getState().open(refA, "plan");    useRightPanelStore.getState().openFile(refA, "README.md");     useRightPanelStore.getState().reconcileFileSurfaces(refA, false);     expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      isOpen: true,      activeSurfaceId: "plan",      surfaces: [{ id: "plan", kind: "plan" }],    });     useRightPanelStore.getState().openFile(refB, "conductor.json");    useRightPanelStore.getState().reconcileFileSurfaces(refB, false);    expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refB)).toEqual({      isOpen: false,      activeSurfaceId: null,      surfaces: [],    });  });   it("close hides the panel without clearing its selected surface", () => {    useRightPanelStore.getState().open(refA, "plan");    useRightPanelStore.getState().close(refA);    expect(selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, refA)).toBeNull();    expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      isOpen: false,      activeSurfaceId: "plan",      surfaces: [{ id: "plan", kind: "plan" }],    });  });   it("toggles empty panel visibility without creating a surface", () => {    useRightPanelStore.getState().toggleVisibility(refA);    expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      isOpen: true,      activeSurfaceId: null,      surfaces: [],    });     useRightPanelStore.getState().toggleVisibility(refA);    expect(useRightPanelStore.getState().byThreadKey).toEqual({});  });   it("toggle hides the panel without discarding the active surface", () => {    useRightPanelStore.getState().toggle(refA, "diff");    expect(selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, refA)).toBe("diff");    useRightPanelStore.getState().toggle(refA, "diff");    expect(selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, refA)).toBeNull();    expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      isOpen: false,      activeSurfaceId: "diff",      surfaces: [{ id: "diff", kind: "diff" }],    });  });   it("toggle to a different kind switches active", () => {    useRightPanelStore.getState().toggle(refA, "preview");    useRightPanelStore.getState().toggle(refA, "plan");    expect(selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, refA)).toBe("plan");  });   it("removeThread clears persisted state", () => {    useRightPanelStore.getState().open(refA, "plan");    useRightPanelStore.getState().removeThread(refA);    expect(selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, refA)).toBeNull();  });   it("close on never-opened thread is a no-op", () => {    useRightPanelStore.getState().close(refA);    expect(useRightPanelStore.getState().byThreadKey).toEqual({});  });   it("tracks one surface per browser session", () => {    useRightPanelStore.getState().openBrowser(refA, "tab-a");    useRightPanelStore.getState().openBrowser(refA, "tab-b");     const state = selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA);    expect(state.surfaces.map((surface) => surface.id)).toEqual(["browser:tab-a", "browser:tab-b"]);    expect(selectActiveRightPanelSurface(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      id: "browser:tab-b",      kind: "preview",      resourceId: "tab-b",    });  });   it("tracks one surface per terminal session", () => {    useRightPanelStore.getState().openTerminal(refA, "term-1");    useRightPanelStore.getState().openTerminal(refA, "term-2");     const state = selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA);    expect(state.surfaces).toEqual([      {        id: "terminal:term-1",        kind: "terminal",        resourceId: "term-1",        terminalIds: ["term-1"],        activeTerminalId: "term-1",      },      {        id: "terminal:term-2",        kind: "terminal",        resourceId: "term-2",        terminalIds: ["term-2"],        activeTerminalId: "term-2",      },    ]);    expect(state.activeSurfaceId).toBe("terminal:term-2");  });   it("tracks split panes and the active pane within a terminal surface", () => {    useRightPanelStore.getState().openTerminal(refA, "term-1");    useRightPanelStore.getState().splitTerminal(refA, "terminal:term-1", "term-2");     expect(selectActiveRightPanelSurface(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      id: "terminal:term-1",      kind: "terminal",      resourceId: "term-1",      terminalIds: ["term-1", "term-2"],      activeTerminalId: "term-2",    });     useRightPanelStore.getState().activateTerminal(refA, "terminal:term-1", "term-1");    useRightPanelStore.getState().closeTerminal(refA, "terminal:term-1", "term-1");    expect(selectActiveRightPanelSurface(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      id: "terminal:term-1",      kind: "terminal",      resourceId: "term-1",      terminalIds: ["term-2"],      activeTerminalId: "term-2",    });  });   it("tracks vertical layout for a terminal surface", () => {    useRightPanelStore.getState().openTerminal(refA, "term-1");    useRightPanelStore.getState().splitTerminal(refA, "terminal:term-1", "term-2", "vertical");     expect(selectActiveRightPanelSurface(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      id: "terminal:term-1",      kind: "terminal",      resourceId: "term-1",      terminalIds: ["term-1", "term-2"],      activeTerminalId: "term-2",      splitDirection: "vertical",    });  });   it("closing the final terminal pane removes its surface and closes the panel", () => {    useRightPanelStore.getState().openTerminal(refA, "term-1");    useRightPanelStore.getState().closeTerminal(refA, "terminal:term-1", "term-1");     expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      isOpen: false,      activeSurfaceId: null,      surfaces: [],    });  });   it("closing the active surface activates a neighboring surface", () => {    useRightPanelStore.getState().openBrowser(refA, "tab-a");    useRightPanelStore.getState().openTerminal(refA, "term-1");    useRightPanelStore.getState().closeSurface(refA, "terminal:term-1");     expect(selectActiveRightPanelSurface(useRightPanelStore.getState().byThreadKey, refA)?.id).toBe(      "browser:tab-a",    );  });   it("closing the final surface closes the panel", () => {    useRightPanelStore.getState().openTerminal(refA, "term-1");    useRightPanelStore.getState().closeSurface(refA, "terminal:term-1");     expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      isOpen: false,      activeSurfaceId: null,      surfaces: [],    });  });   it("closing other surfaces keeps the selected surface active", () => {    useRightPanelStore.getState().openBrowser(refA, "tab-a");    useRightPanelStore.getState().openFile(refA, "src/index.ts");    useRightPanelStore.getState().openTerminal(refA, "term-1");     useRightPanelStore.getState().closeOtherSurfaces(refA, "file:src/index.ts");     expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      isOpen: true,      activeSurfaceId: "file:src/index.ts",      surfaces: [        {          id: "file:src/index.ts",          kind: "file",          relativePath: "src/index.ts",          revealLine: null,          revealRequestId: 1,        },      ],    });  });   it("closing surfaces to the right activates the selected surface when active was removed", () => {    useRightPanelStore.getState().openBrowser(refA, "tab-a");    useRightPanelStore.getState().openFile(refA, "src/index.ts");    useRightPanelStore.getState().openTerminal(refA, "term-1");     useRightPanelStore.getState().closeSurfacesToRight(refA, "browser:tab-a");     expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      isOpen: true,      activeSurfaceId: "browser:tab-a",      surfaces: [{ id: "browser:tab-a", kind: "preview", resourceId: "tab-a" }],    });  });   it("closing all surfaces closes the panel", () => {    useRightPanelStore.getState().openBrowser(refA, "tab-a");    useRightPanelStore.getState().openFile(refA, "src/index.ts");     useRightPanelStore.getState().closeAllSurfaces(refA);     expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      isOpen: false,      activeSurfaceId: null,      surfaces: [],    });  });   it("reconciles browser surfaces without deleting other surface kinds", () => {    useRightPanelStore.getState().openTerminal(refA, "term-1");    useRightPanelStore.getState().openBrowser(refA, "tab-a");    useRightPanelStore.getState().openBrowser(refA, "tab-b");    useRightPanelStore.getState().reconcileBrowserSurfaces(refA, ["tab-b", "tab-c"]);     expect(      selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA).surfaces.map(        (surface) => surface.id,      ),    ).toEqual(["terminal:term-1", "browser:tab-b", "browser:tab-c"]);  });});