Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/test/browser/services/openerService.test.ts
3296 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
import assert from 'assert';
6
import { Disposable } from '../../../../base/common/lifecycle.js';
7
import { URI } from '../../../../base/common/uri.js';
8
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
9
import { OpenerService } from '../../../browser/services/openerService.js';
10
import { TestCodeEditorService } from '../editorTestServices.js';
11
import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js';
12
import { NullCommandService } from '../../../../platform/commands/test/common/nullCommandService.js';
13
import { ITextEditorOptions } from '../../../../platform/editor/common/editor.js';
14
import { matchesScheme, matchesSomeScheme } from '../../../../base/common/network.js';
15
import { TestThemeService } from '../../../../platform/theme/test/common/testThemeService.js';
16
17
suite('OpenerService', function () {
18
const themeService = new TestThemeService();
19
const editorService = new TestCodeEditorService(themeService);
20
21
let lastCommand: { id: string; args: any[] } | undefined;
22
23
const commandService = new (class implements ICommandService {
24
declare readonly _serviceBrand: undefined;
25
onWillExecuteCommand = () => Disposable.None;
26
onDidExecuteCommand = () => Disposable.None;
27
executeCommand(id: string, ...args: any[]): Promise<any> {
28
lastCommand = { id, args };
29
return Promise.resolve(undefined);
30
}
31
})();
32
33
setup(function () {
34
lastCommand = undefined;
35
});
36
37
const store = ensureNoDisposablesAreLeakedInTestSuite();
38
39
test('delegate to editorService, scheme:///fff', async function () {
40
const openerService = new OpenerService(editorService, NullCommandService);
41
await openerService.open(URI.parse('another:///somepath'));
42
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection, undefined);
43
});
44
45
test('delegate to editorService, scheme:///fff#L123', async function () {
46
const openerService = new OpenerService(editorService, NullCommandService);
47
48
await openerService.open(URI.parse('file:///somepath#L23'));
49
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.startLineNumber, 23);
50
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.startColumn, 1);
51
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.endLineNumber, undefined);
52
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.endColumn, undefined);
53
assert.strictEqual(editorService.lastInput!.resource.fragment, '');
54
55
await openerService.open(URI.parse('another:///somepath#L23'));
56
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.startLineNumber, 23);
57
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.startColumn, 1);
58
59
await openerService.open(URI.parse('another:///somepath#L23,45'));
60
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.startLineNumber, 23);
61
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.startColumn, 45);
62
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.endLineNumber, undefined);
63
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.endColumn, undefined);
64
assert.strictEqual(editorService.lastInput!.resource.fragment, '');
65
});
66
67
test('delegate to editorService, scheme:///fff#123,123', async function () {
68
const openerService = new OpenerService(editorService, NullCommandService);
69
70
await openerService.open(URI.parse('file:///somepath#23'));
71
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.startLineNumber, 23);
72
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.startColumn, 1);
73
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.endLineNumber, undefined);
74
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.endColumn, undefined);
75
assert.strictEqual(editorService.lastInput!.resource.fragment, '');
76
77
await openerService.open(URI.parse('file:///somepath#23,45'));
78
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.startLineNumber, 23);
79
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.startColumn, 45);
80
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.endLineNumber, undefined);
81
assert.strictEqual((editorService.lastInput!.options as ITextEditorOptions)!.selection!.endColumn, undefined);
82
assert.strictEqual(editorService.lastInput!.resource.fragment, '');
83
});
84
85
test('delegate to commandsService, command:someid', async function () {
86
const openerService = new OpenerService(editorService, commandService);
87
88
const id = `aCommand${Math.random()}`;
89
store.add(CommandsRegistry.registerCommand(id, function () { }));
90
91
assert.strictEqual(lastCommand, undefined);
92
await openerService.open(URI.parse('command:' + id));
93
assert.strictEqual(lastCommand, undefined);
94
});
95
96
97
test('delegate to commandsService, command:someid, 2', async function () {
98
const openerService = new OpenerService(editorService, commandService);
99
100
const id = `aCommand${Math.random()}`;
101
store.add(CommandsRegistry.registerCommand(id, function () { }));
102
103
await openerService.open(URI.parse('command:' + id).with({ query: '\"123\"' }), { allowCommands: true });
104
assert.strictEqual(lastCommand!.id, id);
105
assert.strictEqual(lastCommand!.args.length, 1);
106
assert.strictEqual(lastCommand!.args[0], '123');
107
108
await openerService.open(URI.parse('command:' + id), { allowCommands: true });
109
assert.strictEqual(lastCommand!.id, id);
110
assert.strictEqual(lastCommand!.args.length, 0);
111
112
await openerService.open(URI.parse('command:' + id).with({ query: '123' }), { allowCommands: true });
113
assert.strictEqual(lastCommand!.id, id);
114
assert.strictEqual(lastCommand!.args.length, 1);
115
assert.strictEqual(lastCommand!.args[0], 123);
116
117
await openerService.open(URI.parse('command:' + id).with({ query: JSON.stringify([12, true]) }), { allowCommands: true });
118
assert.strictEqual(lastCommand!.id, id);
119
assert.strictEqual(lastCommand!.args.length, 2);
120
assert.strictEqual(lastCommand!.args[0], 12);
121
assert.strictEqual(lastCommand!.args[1], true);
122
});
123
124
test('links are protected by validators', async function () {
125
const openerService = new OpenerService(editorService, commandService);
126
127
store.add(openerService.registerValidator({ shouldOpen: () => Promise.resolve(false) }));
128
129
const httpResult = await openerService.open(URI.parse('https://www.microsoft.com'));
130
const httpsResult = await openerService.open(URI.parse('https://www.microsoft.com'));
131
assert.strictEqual(httpResult, false);
132
assert.strictEqual(httpsResult, false);
133
});
134
135
test('links validated by validators go to openers', async function () {
136
const openerService = new OpenerService(editorService, commandService);
137
138
store.add(openerService.registerValidator({ shouldOpen: () => Promise.resolve(true) }));
139
140
let openCount = 0;
141
store.add(openerService.registerOpener({
142
open: (resource: URI) => {
143
openCount++;
144
return Promise.resolve(true);
145
}
146
}));
147
148
await openerService.open(URI.parse('http://microsoft.com'));
149
assert.strictEqual(openCount, 1);
150
await openerService.open(URI.parse('https://microsoft.com'));
151
assert.strictEqual(openCount, 2);
152
});
153
154
test('links aren\'t manipulated before being passed to validator: PR #118226', async function () {
155
const openerService = new OpenerService(editorService, commandService);
156
157
store.add(openerService.registerValidator({
158
shouldOpen: (resource) => {
159
// We don't want it to convert strings into URIs
160
assert.strictEqual(resource instanceof URI, false);
161
return Promise.resolve(false);
162
}
163
}));
164
await openerService.open('https://wwww.microsoft.com');
165
await openerService.open('https://www.microsoft.com??params=CountryCode%3DUSA%26Name%3Dvscode"');
166
});
167
168
test('links validated by multiple validators', async function () {
169
const openerService = new OpenerService(editorService, commandService);
170
171
let v1 = 0;
172
openerService.registerValidator({
173
shouldOpen: () => {
174
v1++;
175
return Promise.resolve(true);
176
}
177
});
178
179
let v2 = 0;
180
openerService.registerValidator({
181
shouldOpen: () => {
182
v2++;
183
return Promise.resolve(true);
184
}
185
});
186
187
let openCount = 0;
188
openerService.registerOpener({
189
open: (resource: URI) => {
190
openCount++;
191
return Promise.resolve(true);
192
}
193
});
194
195
await openerService.open(URI.parse('http://microsoft.com'));
196
assert.strictEqual(openCount, 1);
197
assert.strictEqual(v1, 1);
198
assert.strictEqual(v2, 1);
199
await openerService.open(URI.parse('https://microsoft.com'));
200
assert.strictEqual(openCount, 2);
201
assert.strictEqual(v1, 2);
202
assert.strictEqual(v2, 2);
203
});
204
205
test('links invalidated by first validator do not continue validating', async function () {
206
const openerService = new OpenerService(editorService, commandService);
207
208
let v1 = 0;
209
openerService.registerValidator({
210
shouldOpen: () => {
211
v1++;
212
return Promise.resolve(false);
213
}
214
});
215
216
let v2 = 0;
217
openerService.registerValidator({
218
shouldOpen: () => {
219
v2++;
220
return Promise.resolve(true);
221
}
222
});
223
224
let openCount = 0;
225
openerService.registerOpener({
226
open: (resource: URI) => {
227
openCount++;
228
return Promise.resolve(true);
229
}
230
});
231
232
await openerService.open(URI.parse('http://microsoft.com'));
233
assert.strictEqual(openCount, 0);
234
assert.strictEqual(v1, 1);
235
assert.strictEqual(v2, 0);
236
await openerService.open(URI.parse('https://microsoft.com'));
237
assert.strictEqual(openCount, 0);
238
assert.strictEqual(v1, 2);
239
assert.strictEqual(v2, 0);
240
});
241
242
test('matchesScheme', function () {
243
assert.ok(matchesScheme('https://microsoft.com', 'https'));
244
assert.ok(matchesScheme('http://microsoft.com', 'http'));
245
assert.ok(matchesScheme('hTTPs://microsoft.com', 'https'));
246
assert.ok(matchesScheme('httP://microsoft.com', 'http'));
247
assert.ok(matchesScheme(URI.parse('https://microsoft.com'), 'https'));
248
assert.ok(matchesScheme(URI.parse('http://microsoft.com'), 'http'));
249
assert.ok(matchesScheme(URI.parse('hTTPs://microsoft.com'), 'https'));
250
assert.ok(matchesScheme(URI.parse('httP://microsoft.com'), 'http'));
251
assert.ok(!matchesScheme(URI.parse('https://microsoft.com'), 'http'));
252
assert.ok(!matchesScheme(URI.parse('htt://microsoft.com'), 'http'));
253
assert.ok(!matchesScheme(URI.parse('z://microsoft.com'), 'http'));
254
});
255
256
test('matchesSomeScheme', function () {
257
assert.ok(matchesSomeScheme('https://microsoft.com', 'http', 'https'));
258
assert.ok(matchesSomeScheme('http://microsoft.com', 'http', 'https'));
259
assert.ok(!matchesSomeScheme('x://microsoft.com', 'http', 'https'));
260
});
261
262
test('resolveExternalUri', async function () {
263
const openerService = new OpenerService(editorService, NullCommandService);
264
265
try {
266
await openerService.resolveExternalUri(URI.parse('file:///Users/user/folder'));
267
assert.fail('Should not reach here');
268
} catch {
269
// OK
270
}
271
272
const disposable = openerService.registerExternalUriResolver({
273
async resolveExternalUri(uri) {
274
return { resolved: uri, dispose() { } };
275
}
276
});
277
278
const result = await openerService.resolveExternalUri(URI.parse('file:///Users/user/folder'));
279
assert.deepStrictEqual(result.resolved.toString(), 'file:///Users/user/folder');
280
disposable.dispose();
281
});
282
283
test('vscode.open command can\'t open HTTP URL with hash (#) in it [extension development] #140907', async function () {
284
const openerService = new OpenerService(editorService, NullCommandService);
285
286
const actual: string[] = [];
287
288
openerService.setDefaultExternalOpener({
289
async openExternal(href) {
290
actual.push(href);
291
return true;
292
}
293
});
294
295
const href = 'https://gitlab.com/viktomas/test-project/merge_requests/new?merge_request%5Bsource_branch%5D=test-%23-hash';
296
const uri = URI.parse(href);
297
298
assert.ok(await openerService.open(uri));
299
assert.ok(await openerService.open(href));
300
301
assert.deepStrictEqual(actual, [
302
encodeURI(uri.toString(true)), // BAD, the encoded # (%23) is double encoded to %2523 (% is double encoded)
303
href // good
304
]);
305
});
306
});
307
308