920 lines · 33.2 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,  pullRequestSurface,  pullRequestSurfaceId,  selectActiveRightPanel,  selectActiveRightPanelSurface,  selectSelectedRightPanelSurface,  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: {}, userActionRevisionByThreadKey: {} });}); describe("rightPanelStore", () => {  it("gives each host/device its own tab and preserves renamed tabs", () => {    const store = useRightPanelStore.getState();    const android = {      hostId: "nucbox",      deviceId: "emulator-5580",      name: "Pixel",      platform: "android",    } as const;    const ios = { hostId: "macmini", deviceId: "ios-1", name: "iPhone", platform: "ios" } as const;    store.open(refA, "device");    store.openDevice(refA, android);    store.open(refA, "device");    expect(      selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA).surfaces,    ).toHaveLength(2);    store.openDevice(refA, ios);    let state = selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA);    expect(state.surfaces.map((surface) => surface.id)).toEqual([      "device:nucbox:emulator-5580",      "device:macmini:ios-1",    ]);    store.renameDevice(refA, "device:nucbox:emulator-5580", "Android test");    store.openDevice(refA, android);    state = selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA);    expect(state.surfaces).toHaveLength(2);    expect(state.surfaces[0]).toMatchObject({ title: "Android test", target: android });    expect(state.activeSurfaceId).toBe("device:nucbox:emulator-5580");    store.closeSurface(refA, state.activeSurfaceId!);    expect(      selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA).surfaces,    ).toEqual([expect.objectContaining({ target: ios })]);  });   it("does not collide when two hosts expose the same device id", () => {    const store = useRightPanelStore.getState();    const device = { deviceId: "emulator-5554", name: "Pixel", platform: "android" } as const;    store.openDevice(refA, { ...device, hostId: "a:b" });    store.openDevice(refA, { ...device, hostId: "a" });    expect(      selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA).surfaces,    ).toHaveLength(2);    expect(      selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refB).surfaces,    ).toHaveLength(0);  });   it.each(["one", "all", "others", "right"])(    "keeps device tabs dismissed across reload after closing %s",    (mode) => {      const store = useRightPanelStore.getState();      const target = {        hostId: "nucbox",        deviceId: "emulator-5580",        name: "Pixel",        platform: "android",      } as const;      store.open(refA, "files");      store.openDevice(refA, target);      if (mode === "one") store.closeSurface(refA, "device:nucbox:emulator-5580");      if (mode === "all") store.closeAllSurfaces(refA);      if (mode === "others") store.closeOtherSurfaces(refA, "files");      if (mode === "right") store.closeSurfacesToRight(refA, "files");      const persisted = JSON.parse(        JSON.stringify({ byThreadKey: useRightPanelStore.getState().byThreadKey }),      );      useRightPanelStore.setState(migratePersistedRightPanelState(persisted));      store.openDevice(refA, target, true);      expect(        selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA).surfaces.some(          (surface) => surface.kind === "device",        ),      ).toBe(false);      store.openDevice(refA, target);      expect(        selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA).surfaces.some(          (surface) => surface.kind === "device",        ),      ).toBe(true);    },  );   const completedDiff = { id: "diff", kind: "diff" } as const;  const linkedPullRequest = pullRequestSurface({    projectId: "project-a",    repository: "pingdotgg/t3code",    number: 42,  });   it.each(["diff-first", "pull-request-first"])(    "prioritizes the linked pull request over browser and diff with %s delivery",    (order) => {      const store = useRightPanelStore.getState();      store.openBrowser(refA, "existing-browser");      const revision = store.getUserActionRevision(refA);      const requests =        order === "diff-first"          ? [completedDiff, linkedPullRequest]          : [linkedPullRequest, completedDiff];      for (const surface of requests) store.openProactive(refA, surface, revision);      store.reconcileBrowserSurfaces(refA, ["existing-browser", "agent-browser"]);       expect(        selectActiveRightPanelSurface(useRightPanelStore.getState().byThreadKey, refA),      ).toEqual(linkedPullRequest);       store.open(refA, "diff");      expect(selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, refA)).toBe("diff");    },  );   it.each([    { choice: "file", choose: () => useRightPanelStore.getState().openFile(refA, "src/app.ts") },    {      choice: "pull request",      choose: () =>        useRightPanelStore.getState().openPullRequest(refA, { ...linkedPullRequest, number: 41 }),    },    { choice: "browser", choose: () => useRightPanelStore.getState().openBrowser(refA, "tab-a") },    {      choice: "terminal",      choose: () => useRightPanelStore.getState().openTerminal(refA, "term-1"),    },    {      choice: "same tab",      choose: () => useRightPanelStore.getState().activateSurface(refA, "diff"),    },    { choice: "hide", choose: () => useRightPanelStore.getState().close(refA) },    { choice: "toggle", choose: () => useRightPanelStore.getState().toggle(refA, "diff") },    { choice: "close all", choose: () => useRightPanelStore.getState().closeAllSurfaces(refA) },    {      choice: "terminal close",      choose: () => {        const store = useRightPanelStore.getState();        store.openTerminal(refA, "term-1");        store.closeTerminal(refA, "terminal:term-1", "term-1");      },    },  ])("keeps a later $choice choice when automatic requests arrive", ({ choose }) => {    const store = useRightPanelStore.getState();    store.open(refA, "diff");    const revision = store.getUserActionRevision(refA);    choose();    const chosen = selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA);     expect(store.openProactive(refA, completedDiff, revision)).toBe(false);    expect(store.openProactive(refA, linkedPullRequest, revision)).toBe(false);    expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toBe(      chosen,    );  });   it("allows automatic panels for a later turn after a manual choice", () => {    const store = useRightPanelStore.getState();    const firstTurnRevision = store.getUserActionRevision(refA);    store.openFile(refA, "src/app.ts");    expect(store.openProactive(refA, completedDiff, firstTurnRevision)).toBe(false);     const nextTurnRevision = store.getUserActionRevision(refA);    expect(store.openProactive(refA, completedDiff, nextTurnRevision)).toBe(true);    expect(selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, refA)).toBe("diff");  });   it("keeps manual choices scoped to their thread and environment", () => {    const otherEnvironment = scopeThreadRef("env-2" as EnvironmentId, refA.threadId);    const store = useRightPanelStore.getState();    const revision = store.getUserActionRevision(refA);    store.openFile(refB, "src/app.ts");    store.openFile(otherEnvironment, "src/app.ts");     expect(store.openProactive(refA, completedDiff, revision)).toBe(true);    expect(selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, refB)).toBe("file");    expect(      selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, otherEnvironment),    ).toBe("file");  });   it("does not treat resource reconciliation as a manual choice", () => {    const store = useRightPanelStore.getState();    store.openFile(refA, "src/app.ts");    const revision = store.getUserActionRevision(refA);    store.reconcileBrowserSurfaces(refA, ["agent-browser"]);    store.reconcileFileSurfaces(refA, false);     expect(store.openProactive(refA, completedDiff, revision)).toBe(true);    expect(selectActiveRightPanel(useRightPanelStore.getState().byThreadKey, refA)).toBe("diff");  });   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("upgrades the legacy singleton pull request surface to a reference-keyed tab", () => {    const id = pullRequestSurfaceId({      projectId: "project-a",      repository: "pingdotgg/t3code",      number: 4909,    });    expect(      migratePersistedRightPanelState({        byThreadKey: {          "env-1:thread-A": {            isOpen: true,            activeSurfaceId: "pull-request",            surfaces: [              {                id: "pull-request",                kind: "pull-request",                projectId: "project-a",                repository: "pingdotgg/t3code",                number: 4909,              },            ],          },        },      }),    ).toEqual({      byThreadKey: {        "env-1:thread-A": {          isOpen: true,          activeSurfaceId: id,          surfaces: [            {              id,              kind: "pull-request",              projectId: "project-a",              repository: "pingdotgg/t3code",              number: 4909,            },          ],        },      },    });  });   it("drops the pull-request list's shared panel so a restart opens the page fresh", () => {    const id = pullRequestSurfaceId({      projectId: "project-a",      repository: "pingdotgg/t3code",      number: 4909,    });    const panelState = {      isOpen: true,      activeSurfaceId: id,      surfaces: [        {          id,          kind: "pull-request" as const,          projectId: "project-a",          repository: "pingdotgg/t3code",          number: 4909,        },      ],    };    expect(      migratePersistedRightPanelState({        byThreadKey: {          "env-1:pull-requests-panel": panelState,          "env-1:thread-A": panelState,        },      }),    ).toEqual({ byThreadKey: { "env-1:thread-A": panelState } });  });   it("drops persisted plan surfaces and does not reopen an empty panel", () => {    expect(      migratePersistedRightPanelState({        byThreadKey: {          "env-1:thread-A": {            isOpen: true,            activeSurfaceId: "plan",            surfaces: [{ id: "plan", kind: "plan" }],          },          "env-1:thread-B": {            isOpen: true,            activeSurfaceId: "plan",            surfaces: [              { id: "plan", kind: "plan" },              { id: "diff", kind: "diff" },            ],          },        },      }),    ).toEqual({      byThreadKey: {        "env-1:thread-A": {          isOpen: false,          activeSurfaceId: null,          surfaces: [],        },        "env-1:thread-B": {          isOpen: true,          activeSurfaceId: "diff",          surfaces: [{ id: "diff", kind: "diff" }],        },      },    });  });   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, "agents");    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, "agents");    useRightPanelStore.getState().open(refA, "diff");     expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      isOpen: true,      activeSurfaceId: "diff",      surfaces: [        { id: "diff", kind: "diff" },        { id: "agents", kind: "agents" },      ],    });  });   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("opens an attachment as a file surface without the standalone explorer", () => {    const attachment = {      type: "file" as const,      id: "thread-A-attachment-pdf",      name: "report.pdf",      mimeType: "application/pdf",      sizeBytes: 42,    };    useRightPanelStore.getState().open(refA, "files");    useRightPanelStore.getState().openAttachment(refA, attachment);     expect(selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, refA)).toEqual({      isOpen: true,      activeSurfaceId: "attachment:thread-A-attachment-pdf",      surfaces: [        {          id: "attachment:thread-A-attachment-pdf",          kind: "file",          relativePath: "report.pdf",          revealLine: null,          revealRequestId: 0,          attachment,        },      ],    });  });   it("keeps attachment and workspace file ids disjoint", () => {    useRightPanelStore.getState().openFile(refA, "attachment:shared-id");    useRightPanelStore.getState().openAttachment(refA, {      type: "file",

Lines 1–500 of 920.