| @@ -356,6 +356,25 @@ function withProjectScripts( · 355 unmodified lines |
| 356 | }; | 356 | }; |
| 357 | } | 357 | } |
| 358 | | 358 | |
| | 359 | function setDraftThreadWithoutWorktree(): void { |
| | 360 | useComposerDraftStore.setState({ |
| | 361 | draftThreadsByThreadId: { |
| | 362 | [THREAD_ID]: { |
| | 363 | projectId: PROJECT_ID, |
| | 364 | createdAt: NOW_ISO, |
| | 365 | runtimeMode: "full-access", |
| | 366 | interactionMode: "default", |
| | 367 | branch: null, |
| | 368 | worktreePath: null, |
| | 369 | envMode: "local", |
| | 370 | }, |
| | 371 | }, |
| | 372 | projectDraftThreadIdByProjectId: { |
| | 373 | [PROJECT_ID]: THREAD_ID, |
| | 374 | }, |
| | 375 | }); |
| | 376 | } |
| | 377 | |
| 359 | function createSnapshotWithLongProposedPlan(): OrchestrationReadModel { | 378 | function createSnapshotWithLongProposedPlan(): OrchestrationReadModel { |
| 360 | const snapshot = createSnapshotForTargetUser({ | 379 | const snapshot = createSnapshotForTargetUser({ |
| 361 | targetMessageId: "msg-user-plan-target" as MessageId, | 380 | targetMessageId: "msg-user-plan-target" as MessageId, |
| @@ -1011,30 +1030,162 @@ describe("ChatView timeline estimator parity (full app)", () => { · 649 unmodified lines |
| 1011 | ); | 1030 | ); |
| 1012 | | 1031 | |
| 1013 | it("opens the project cwd for draft threads without a worktree path", async () => { | 1032 | it("opens the project cwd for draft threads without a worktree path", async () => { |
| 1014 | useComposerDraftStore.setState({ | 1033 | setDraftThreadWithoutWorktree(); |
| 1015 | draftThreadsByThreadId: { | 1034 | |
| 1016 | [THREAD_ID]: { | 1035 | const mounted = await mountChatView({ |
| 1017 | projectId: PROJECT_ID, | 1036 | viewport: DEFAULT_VIEWPORT, |
| 1018 | createdAt: NOW_ISO, | 1037 | snapshot: createDraftOnlySnapshot(), |
| 1019 | runtimeMode: "full-access", | 1038 | configureFixture: (nextFixture) => { |
| 1020 | interactionMode: "default", | 1039 | nextFixture.serverConfig = { |
| 1021 | branch: null, | 1040 | ...nextFixture.serverConfig, |
| 1022 | worktreePath: null, | 1041 | availableEditors: ["vscode"], |
| 1023 | envMode: "local", | 1042 | }; |
| | 1043 | }, |
| | 1044 | }); |
| | 1045 | |
| | 1046 | try { |
| | 1047 | const openButton = await waitForElement( |
| | 1048 | () => |
| | 1049 | Array.from(document.querySelectorAll("button")).find( |
| | 1050 | (button) => button.textContent?.trim() === "Open", |
| | 1051 | ) as HTMLButtonElement | null, |
| | 1052 | "Unable to find Open button.", |
| | 1053 | ); |
| | 1054 | openButton.click(); |
| | 1055 | |
| | 1056 | await vi.waitFor( |
| | 1057 | () => { |
| | 1058 | const openRequest = wsRequests.find( |
| | 1059 | (request) => request._tag === WS_METHODS.shellOpenInEditor, |
| | 1060 | ); |
| | 1061 | expect(openRequest).toMatchObject({ |
| | 1062 | _tag: WS_METHODS.shellOpenInEditor, |
| | 1063 | cwd: "/repo/project", |
| | 1064 | editor: "vscode", |
| | 1065 | }); |
| 1024 | }, | 1066 | }, |
| | 1067 | { timeout: 8_000, interval: 16 }, |
| | 1068 | ); |
| | 1069 | } finally { |
| | 1070 | await mounted.cleanup(); |
| | 1071 | } |
| | 1072 | }); |
| | 1073 | |
| | 1074 | it("opens the project cwd with VS Code Insiders when it is the only available editor", async () => { |
| | 1075 | setDraftThreadWithoutWorktree(); |
| | 1076 | |
| | 1077 | const mounted = await mountChatView({ |
| | 1078 | viewport: DEFAULT_VIEWPORT, |
| | 1079 | snapshot: createDraftOnlySnapshot(), |
| | 1080 | configureFixture: (nextFixture) => { |
| | 1081 | nextFixture.serverConfig = { |
| | 1082 | ...nextFixture.serverConfig, |
| | 1083 | availableEditors: ["vscode-insiders"], |
| | 1084 | }; |
| 1025 | }, | 1085 | }, |
| 1026 | projectDraftThreadIdByProjectId: { | 1086 | }); |
| 1027 | [PROJECT_ID]: THREAD_ID, | 1087 | |
| | 1088 | try { |
| | 1089 | const openButton = await waitForElement( |
| | 1090 | () => |
| | 1091 | Array.from(document.querySelectorAll("button")).find( |
| | 1092 | (button) => button.textContent?.trim() === "Open", |
| | 1093 | ) as HTMLButtonElement | null, |
| | 1094 | "Unable to find Open button.", |
| | 1095 | ); |
| | 1096 | openButton.click(); |
| | 1097 | |
| | 1098 | await vi.waitFor( |
| | 1099 | () => { |
| | 1100 | const openRequest = wsRequests.find( |
| | 1101 | (request) => request._tag === WS_METHODS.shellOpenInEditor, |
| | 1102 | ); |
| | 1103 | expect(openRequest).toMatchObject({ |
| | 1104 | _tag: WS_METHODS.shellOpenInEditor, |
| | 1105 | cwd: "/repo/project", |
| | 1106 | editor: "vscode-insiders", |
| | 1107 | }); |
| | 1108 | }, |
| | 1109 | { timeout: 8_000, interval: 16 }, |
| | 1110 | ); |
| | 1111 | } finally { |
| | 1112 | await mounted.cleanup(); |
| | 1113 | } |
| | 1114 | }); |
| | 1115 | |
| | 1116 | it("filters the open picker menu and opens VSCodium from the menu", async () => { |
| | 1117 | setDraftThreadWithoutWorktree(); |
| | 1118 | |
| | 1119 | const mounted = await mountChatView({ |
| | 1120 | viewport: DEFAULT_VIEWPORT, |
| | 1121 | snapshot: createDraftOnlySnapshot(), |
| | 1122 | configureFixture: (nextFixture) => { |
| | 1123 | nextFixture.serverConfig = { |
| | 1124 | ...nextFixture.serverConfig, |
| | 1125 | availableEditors: ["vscode-insiders", "vscodium"], |
| | 1126 | }; |
| 1028 | }, | 1127 | }, |
| 1029 | }); | 1128 | }); |
| 1030 | | 1129 | |
| | 1130 | try { |
| | 1131 | const menuButton = await waitForElement( |
| | 1132 | () => document.querySelector('button[aria-label="Copy options"]'), |
| | 1133 | "Unable to find Open picker button.", |
| | 1134 | ); |
| | 1135 | (menuButton as HTMLButtonElement).click(); |
| | 1136 | |
| | 1137 | await waitForElement( |
| | 1138 | () => |
| | 1139 | Array.from(document.querySelectorAll('[data-slot="menu-item"]')).find((item) => |
| | 1140 | item.textContent?.includes("VS Code Insiders"), |
| | 1141 | ) ?? null, |
| | 1142 | "Unable to find VS Code Insiders menu item.", |
| | 1143 | ); |
| | 1144 | |
| | 1145 | expect( |
| | 1146 | Array.from(document.querySelectorAll('[data-slot="menu-item"]')).some((item) => |
| | 1147 | item.textContent?.includes("Zed"), |
| | 1148 | ), |
| | 1149 | ).toBe(false); |
| | 1150 | |
| | 1151 | const vscodiumItem = await waitForElement( |
| | 1152 | () => |
| | 1153 | Array.from(document.querySelectorAll('[data-slot="menu-item"]')).find((item) => |
| | 1154 | item.textContent?.includes("VSCodium"), |
| | 1155 | ) ?? null, |
| | 1156 | "Unable to find VSCodium menu item.", |
| | 1157 | ); |
| | 1158 | (vscodiumItem as HTMLElement).click(); |
| | 1159 | |
| | 1160 | await vi.waitFor( |
| | 1161 | () => { |
| | 1162 | const openRequest = wsRequests.find( |
| | 1163 | (request) => request._tag === WS_METHODS.shellOpenInEditor, |
| | 1164 | ); |
| | 1165 | expect(openRequest).toMatchObject({ |
| | 1166 | _tag: WS_METHODS.shellOpenInEditor, |
| | 1167 | cwd: "/repo/project", |
| | 1168 | editor: "vscodium", |
| | 1169 | }); |
| | 1170 | }, |
| | 1171 | { timeout: 8_000, interval: 16 }, |
| | 1172 | ); |
| | 1173 | } finally { |
| | 1174 | await mounted.cleanup(); |
| | 1175 | } |
| | 1176 | }); |
| | 1177 | |
| | 1178 | it("falls back to the first installed editor when the stored favorite is unavailable", async () => { |
| | 1179 | localStorage.setItem("t3code:last-editor", "vscodium"); |
| | 1180 | setDraftThreadWithoutWorktree(); |
| | 1181 | |
| 1031 | const mounted = await mountChatView({ | 1182 | const mounted = await mountChatView({ |
| 1032 | viewport: DEFAULT_VIEWPORT, | 1183 | viewport: DEFAULT_VIEWPORT, |
| 1033 | snapshot: createDraftOnlySnapshot(), | 1184 | snapshot: createDraftOnlySnapshot(), |
| 1034 | configureFixture: (nextFixture) => { | 1185 | configureFixture: (nextFixture) => { |
| 1035 | nextFixture.serverConfig = { | 1186 | nextFixture.serverConfig = { |
| 1036 | ...nextFixture.serverConfig, | 1187 | ...nextFixture.serverConfig, |
| 1037 | availableEditors: ["vscode"], | 1188 | availableEditors: ["vscode-insiders"], |
| 1038 | }; | 1189 | }; |
| 1039 | }, | 1190 | }, |
| 1040 | }); | 1191 | }); |
| @@ -1057,7 +1208,7 @@ describe("ChatView timeline estimator parity (full app)", () => { · 16 unmodified lines |
| 1057 | expect(openRequest).toMatchObject({ | 1208 | expect(openRequest).toMatchObject({ |
| 1058 | _tag: WS_METHODS.shellOpenInEditor, | 1209 | _tag: WS_METHODS.shellOpenInEditor, |
| 1059 | cwd: "/repo/project", | 1210 | cwd: "/repo/project", |
| 1060 | editor: "vscode", | 1211 | editor: "vscode-insiders", |
| 1061 | }); | 1212 | }); |
| 1062 | }, | 1213 | }, |
| 1063 | { timeout: 8_000, interval: 16 }, | 1214 | { timeout: 8_000, interval: 16 }, |