1import { scopeThreadRef } from "@t3tools/client-runtime/environment";
2import { EnvironmentId, ThreadId } from "@t3tools/contracts";
3import { afterEach, beforeEach, expect, it, vi } from "vite-plus/test";
4
5import { selectThreadRightPanelState, useRightPanelStore } from "../rightPanelStore";
6import { selectThreadTerminalUiState, useTerminalUiStateStore } from "../terminalUiStateStore";
7import { revealThreadTerminal } from "./revealThreadTerminal";
8
9const threadRef = scopeThreadRef(EnvironmentId.make("local"), ThreadId.make("settled-thread"));
10const remoteRef = scopeThreadRef(EnvironmentId.make("remote"), threadRef.threadId);
11
12const preferences = new Map<string, string>();
13
14beforeEach(() => {
15 preferences.clear();
16 vi.stubGlobal("window", {
17 localStorage: { getItem: (key: string) => preferences.get(key) ?? null },
18 });
19 useTerminalUiStateStore.setState({
20 terminalUiStateByThreadKey: {},
21 suppressedTerminalIdsByThreadKey: {},
22 });
23 useRightPanelStore.setState({ byThreadKey: {} });
24});
25
26afterEach(() => vi.unstubAllGlobals());
27
28it("reveals a server-created setup terminal on the right without adding a default shell", () => {
29 revealThreadTerminal(threadRef, "setup-install");
30 expect(
31 selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, threadRef),
32 ).toMatchObject({
33 isOpen: true,
34 surfaces: [{ terminalIds: ["setup-install"], activeTerminalId: "setup-install" }],
35 });
36 expect(
37 selectThreadTerminalUiState(
38 useTerminalUiStateStore.getState().terminalUiStateByThreadKey,
39 threadRef,
40 ).terminalIds,
41 ).toEqual([]);
42});
43
44it("honors an explicit bottom preference for an unowned terminal", () => {
45 preferences.set("t3code.terminalPlacement", '"bottom"');
46 revealThreadTerminal(threadRef, "setup-install");
47 expect(
48 selectThreadTerminalUiState(
49 useTerminalUiStateStore.getState().terminalUiStateByThreadKey,
50 threadRef,
51 ),
52 ).toMatchObject({
53 terminalOpen: true,
54 terminalIds: ["setup-install"],
55 });
56 expect(
57 selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, threadRef).surfaces,
58 ).toEqual([]);
59});
60
61it("preserves an existing drawer session when the default becomes right", () => {
62 useTerminalUiStateStore.getState().ensureTerminal(threadRef, "setup-install", { open: false });
63 revealThreadTerminal(threadRef, "setup-install");
64 expect(
65 selectThreadTerminalUiState(
66 useTerminalUiStateStore.getState().terminalUiStateByThreadKey,
67 threadRef,
68 ),
69 ).toMatchObject({ terminalOpen: true, terminalIds: ["setup-install"] });
70 expect(
71 selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, threadRef).surfaces,
72 ).toEqual([]);
73});
74
75it("reopens the existing panel and selects a running terminal in its split", () => {
76 const panels = useRightPanelStore.getState();
77 panels.openTerminal(threadRef, "terminal-1");
78 const surfaceId = selectThreadRightPanelState(
79 useRightPanelStore.getState().byThreadKey,
80 threadRef,
81 ).activeSurfaceId!;
82 panels.splitTerminal(threadRef, surfaceId, "setup-install");
83 panels.activateTerminal(threadRef, surfaceId, "terminal-1");
84 panels.close(threadRef);
85
86 revealThreadTerminal(threadRef, "setup-install");
87 expect(
88 selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, threadRef),
89 ).toMatchObject({
90 isOpen: true,
91 activeSurfaceId: surfaceId,
92 surfaces: [
93 {
94 terminalIds: ["terminal-1", "setup-install"],
95 activeTerminalId: "setup-install",
96 },
97 ],
98 });
99 expect(
100 selectThreadTerminalUiState(
101 useTerminalUiStateStore.getState().terminalUiStateByThreadKey,
102 threadRef,
103 ).terminalIds,
104 ).toEqual([]);
105});
106
107it("reveals terminals in their own environment without changing another environment's drawer", () => {
108 useTerminalUiStateStore.getState().ensureTerminal(threadRef, "default", { open: false });
109 revealThreadTerminal(threadRef, "default");
110 revealThreadTerminal(remoteRef, "setup-install");
111 const state = useTerminalUiStateStore.getState().terminalUiStateByThreadKey;
112 expect(selectThreadTerminalUiState(state, threadRef).terminalIds).toEqual(["default"]);
113 expect(selectThreadTerminalUiState(state, remoteRef).terminalIds).toEqual([]);
114 expect(
115 selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, remoteRef).surfaces,
116 ).toMatchObject([{ terminalIds: ["setup-install"] }]);
117});
118
119it("adds and activates a terminal peer tab in the existing browser sidebar without duplicating it", () => {
120 const panels = useRightPanelStore.getState();
121 panels.openBrowser(threadRef, "existing-browser");
122 revealThreadTerminal(threadRef, "setup-install");
123 revealThreadTerminal(threadRef, "setup-install");
124 const sidebar = selectThreadRightPanelState(useRightPanelStore.getState().byThreadKey, threadRef);
125 expect(sidebar.isOpen).toBe(true);
126 expect(sidebar.activeSurfaceId).toBe("terminal:setup-install");
127 expect(sidebar.surfaces.map((surface) => surface.kind)).toEqual(["preview", "terminal"]);
128 expect(sidebar.surfaces[0]).toMatchObject({ resourceId: "existing-browser" });
129 expect(
130 selectThreadTerminalUiState(
131 useTerminalUiStateStore.getState().terminalUiStateByThreadKey,
132 threadRef,
133 ).terminalIds,
134 ).toEqual([]);
135});