Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/externalTerminal/test/electron-browser/externalTerminal.contribution.test.ts
13406 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import { URI } from '../../../../../base/common/uri.js';
8
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
9
import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js';
10
import { mock } from '../../../../../base/test/common/mock.js';
11
import { IHistoryService } from '../../../../services/history/common/history.js';
12
import { IExternalTerminalService } from '../../../../../platform/externalTerminal/electron-browser/externalTerminalService.js';
13
import { IExternalTerminalSettings } from '../../../../../platform/externalTerminal/common/externalTerminal.js';
14
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';
15
import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js';
16
import { IRemoteAuthorityResolverService } from '../../../../../platform/remote/common/remoteAuthorityResolver.js';
17
import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from '../../../../../platform/workspace/common/workspace.js';
18
import { CommandsRegistry } from '../../../../../platform/commands/common/commands.js';
19
import { IQuickInputService, IQuickPickItem } from '../../../../../platform/quickinput/common/quickInput.js';
20
import { ILabelService } from '../../../../../platform/label/common/label.js';
21
import '../../electron-browser/externalTerminal.contribution.js';
22
23
suite('ExternalTerminal contribution', () => {
24
const store = ensureNoDisposablesAreLeakedInTestSuite();
25
26
let instantiationService: TestInstantiationService;
27
let openTerminalCalls: { cwd: string | undefined }[];
28
let pickCalls: IQuickPickItem[][];
29
30
function createWorkspaceFolder(uri: URI, name: string, index: number): IWorkspaceFolder {
31
return {
32
uri,
33
name,
34
index,
35
toResource: (relativePath: string) => URI.joinPath(uri, relativePath)
36
};
37
}
38
39
function setupServices(options: {
40
folders: IWorkspaceFolder[];
41
lastActiveRoot?: URI;
42
lastActiveFile?: URI;
43
pickedFolder?: IWorkspaceFolder | undefined;
44
}) {
45
instantiationService = store.add(new TestInstantiationService());
46
47
openTerminalCalls = [];
48
pickCalls = [];
49
50
instantiationService.stub(IHistoryService, new class extends mock<IHistoryService>() {
51
override getLastActiveWorkspaceRoot() {
52
return options.lastActiveRoot;
53
}
54
override getLastActiveFile(_schemeFilter: string) {
55
return options.lastActiveFile;
56
}
57
});
58
59
instantiationService.stub(IExternalTerminalService, new class extends mock<IExternalTerminalService>() {
60
override async openTerminal(_config: IExternalTerminalSettings, cwd: string | undefined) {
61
openTerminalCalls.push({ cwd });
62
}
63
});
64
65
instantiationService.stub(IConfigurationService, new TestConfigurationService({
66
terminal: { external: { linuxExec: 'xterm', osxExec: 'Terminal.app', windowsExec: 'cmd' } }
67
}));
68
69
instantiationService.stub(IRemoteAuthorityResolverService, new class extends mock<IRemoteAuthorityResolverService>() {
70
});
71
72
instantiationService.stub(IWorkspaceContextService, new class extends mock<IWorkspaceContextService>() {
73
override getWorkspace(): IWorkspace {
74
return {
75
id: 'test-workspace',
76
folders: options.folders,
77
};
78
}
79
});
80
81
instantiationService.stub(IQuickInputService, new class extends mock<IQuickInputService>() {
82
override async pick<T extends IQuickPickItem>(picks: T[]): Promise<T | undefined> {
83
pickCalls.push(picks);
84
if (options.pickedFolder) {
85
const index = options.folders.indexOf(options.pickedFolder);
86
return picks[index];
87
}
88
return undefined;
89
}
90
});
91
92
instantiationService.stub(ILabelService, new class extends mock<ILabelService>() {
93
override getUriLabel(uri: URI) {
94
return uri.fsPath;
95
}
96
});
97
}
98
99
test('single folder - uses last active workspace root', async () => {
100
const folderUri = URI.file('/workspace/project');
101
const folder = createWorkspaceFolder(folderUri, 'project', 0);
102
103
setupServices({
104
folders: [folder],
105
lastActiveRoot: folderUri,
106
});
107
108
const handler = CommandsRegistry.getCommand('workbench.action.terminal.openNativeConsole')!.handler;
109
await instantiationService.invokeFunction(handler);
110
111
assert.deepStrictEqual(openTerminalCalls, [{ cwd: folderUri.fsPath }]);
112
assert.deepStrictEqual(pickCalls, []);
113
});
114
115
test('multiple folders - shows picker and opens selected folder', async () => {
116
const folder1Uri = URI.file('/workspace/project1');
117
const folder2Uri = URI.file('/workspace/project2');
118
const folder1 = createWorkspaceFolder(folder1Uri, 'project1', 0);
119
const folder2 = createWorkspaceFolder(folder2Uri, 'project2', 1);
120
121
setupServices({
122
folders: [folder1, folder2],
123
pickedFolder: folder2,
124
});
125
126
const handler = CommandsRegistry.getCommand('workbench.action.terminal.openNativeConsole')!.handler;
127
await instantiationService.invokeFunction(handler);
128
129
assert.strictEqual(pickCalls.length, 1);
130
assert.deepStrictEqual(openTerminalCalls, [{ cwd: folder2Uri.fsPath }]);
131
});
132
133
test('multiple folders - picker cancelled does not open terminal', async () => {
134
const folder1Uri = URI.file('/workspace/project1');
135
const folder2Uri = URI.file('/workspace/project2');
136
const folder1 = createWorkspaceFolder(folder1Uri, 'project1', 0);
137
const folder2 = createWorkspaceFolder(folder2Uri, 'project2', 1);
138
139
setupServices({
140
folders: [folder1, folder2],
141
pickedFolder: undefined,
142
});
143
144
const handler = CommandsRegistry.getCommand('workbench.action.terminal.openNativeConsole')!.handler;
145
await instantiationService.invokeFunction(handler);
146
147
assert.strictEqual(pickCalls.length, 1);
148
assert.deepStrictEqual(openTerminalCalls, []);
149
});
150
151
test('no workspace root - falls back to active file directory', async () => {
152
const fileUri = URI.file('/workspace/project/src/file.ts');
153
const expectedDir = URI.file('/workspace/project/src').fsPath;
154
155
setupServices({
156
folders: [],
157
lastActiveRoot: undefined,
158
lastActiveFile: fileUri,
159
});
160
161
const handler = CommandsRegistry.getCommand('workbench.action.terminal.openNativeConsole')!.handler;
162
await instantiationService.invokeFunction(handler);
163
164
assert.deepStrictEqual(openTerminalCalls, [{ cwd: expectedDir }]);
165
});
166
167
test('no workspace, no file - opens terminal without cwd', async () => {
168
setupServices({
169
folders: [],
170
lastActiveRoot: undefined,
171
lastActiveFile: undefined,
172
});
173
174
const handler = CommandsRegistry.getCommand('workbench.action.terminal.openNativeConsole')!.handler;
175
await instantiationService.invokeFunction(handler);
176
177
assert.deepStrictEqual(openTerminalCalls, [{ cwd: undefined }]);
178
});
179
});
180
181