@theia/api-tests 1.34.3 → 1.34.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,779 +1,779 @@
1
- // *****************************************************************************
2
- // Copyright (C) 2020 TypeFox and others.
3
- //
4
- // This program and the accompanying materials are made available under the
5
- // terms of the Eclipse Public License v. 2.0 which is available at
6
- // http://www.eclipse.org/legal/epl-2.0.
7
- //
8
- // This Source Code may also be made available under the following Secondary
9
- // Licenses when the conditions for such availability set forth in the Eclipse
10
- // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
- // with the GNU Classpath Exception which is available at
12
- // https://www.gnu.org/software/classpath/license.html.
13
- //
14
- // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
- // *****************************************************************************
16
-
17
- // @ts-check
18
- describe('TypeScript', function () {
19
- this.timeout(30_000);
20
-
21
- const { assert } = chai;
22
-
23
- const Uri = require('@theia/core/lib/common/uri');
24
- const { DisposableCollection } = require('@theia/core/lib/common/disposable');
25
- const { BrowserMainMenuFactory } = require('@theia/core/lib/browser/menu/browser-menu-plugin');
26
- const { EditorManager } = require('@theia/editor/lib/browser/editor-manager');
27
- const { EditorWidget } = require('@theia/editor/lib/browser/editor-widget');
28
- const { EDITOR_CONTEXT_MENU } = require('@theia/editor/lib/browser/editor-menu');
29
- const { WorkspaceService } = require('@theia/workspace/lib/browser/workspace-service');
30
- const { MonacoEditor } = require('@theia/monaco/lib/browser/monaco-editor');
31
- const { HostedPluginSupport } = require('@theia/plugin-ext/lib/hosted/browser/hosted-plugin');
32
- const { ContextKeyService } = require('@theia/core/lib/browser/context-key-service');
33
- const { CommandRegistry } = require('@theia/core/lib/common/command');
34
- const { KeybindingRegistry } = require('@theia/core/lib/browser/keybinding');
35
- const { OpenerService, open } = require('@theia/core/lib/browser/opener-service');
36
- const { animationFrame } = require('@theia/core/lib/browser/browser');
37
- const { PreferenceService, PreferenceScope } = require('@theia/core/lib/browser/preferences/preference-service');
38
- const { ProgressStatusBarItem } = require('@theia/core/lib/browser/progress-status-bar-item');
39
- const { PluginViewRegistry } = require('@theia/plugin-ext/lib/main/browser/view/plugin-view-registry');
40
- const { Range } = require('@theia/monaco-editor-core/esm/vs/editor/common/core/range');
41
- const { Selection } = require('@theia/monaco-editor-core/esm/vs/editor/common/core/selection');
42
-
43
- const container = window.theia.container;
44
- const editorManager = container.get(EditorManager);
45
- const workspaceService = container.get(WorkspaceService);
46
- const menuFactory = container.get(BrowserMainMenuFactory);
47
- const pluginService = container.get(HostedPluginSupport);
48
- const contextKeyService = container.get(ContextKeyService);
49
- const commands = container.get(CommandRegistry);
50
- const openerService = container.get(OpenerService);
51
- /** @type {KeybindingRegistry} */
52
- const keybindings = container.get(KeybindingRegistry);
53
- /** @type {import('@theia/core/lib/browser/preferences/preference-service').PreferenceService} */
54
- const preferences = container.get(PreferenceService);
55
- const progressStatusBarItem = container.get(ProgressStatusBarItem);
56
- /** @type {PluginViewRegistry} */
57
- const pluginViewRegistry = container.get(PluginViewRegistry);
58
-
59
- const typescriptPluginId = 'vscode.typescript-language-features';
60
- const referencesPluginId = 'ms-vscode.references-view';
61
- /** @type Uri.URI */
62
- const rootUri = workspaceService.tryGetRoots()[0].resource;
63
- const demoFileUri = rootUri.resolveToAbsolute('../api-tests/test-ts-workspace/demo-file.ts');
64
- const definitionFileUri = rootUri.resolveToAbsolute('../api-tests/test-ts-workspace/demo-definitions-file.ts');
65
- let originalAutoSaveValue = preferences.inspect('files.autoSave').globalValue;
66
-
67
- before(async function () {
68
- await pluginService.didStart;
69
- await Promise.all([typescriptPluginId, referencesPluginId].map(async pluginId => {
70
- if (!pluginService.getPlugin(pluginId)) {
71
- throw new Error(pluginId + ' should be started');
72
- }
73
- await pluginService.activatePlugin(pluginId);
74
- }).concat(preferences.set('files.autoSave', 'off', PreferenceScope.User)));
75
- });
76
-
77
- beforeEach(async function () {
78
- await editorManager.closeAll({ save: false });
79
- });
80
-
81
- const toTearDown = new DisposableCollection();
82
- afterEach(async () => {
83
- toTearDown.dispose();
84
- await editorManager.closeAll({ save: false });
85
- });
86
-
87
- after(async () => {
88
- await preferences.set('files.autoSave', originalAutoSaveValue, PreferenceScope.User);
89
- })
90
-
91
- /**
92
- * @param {Uri.default} uri
93
- * @param {boolean} preview
94
- */
95
- async function openEditor(uri, preview = false) {
96
- const widget = await open(openerService, uri, { mode: 'activate', preview });
97
- const editorWidget = widget instanceof EditorWidget ? widget : undefined;
98
- const editor = MonacoEditor.get(editorWidget);
99
- assert.isDefined(editor);
100
- // wait till tsserver is running, see:
101
- // https://github.com/microsoft/vscode/blob/93cbbc5cae50e9f5f5046343c751b6d010468200/extensions/typescript-language-features/src/extension.ts#L98-L103
102
- await waitForAnimation(() => contextKeyService.match('typescript.isManagedFile'));
103
- // wait till projects are loaded, see:
104
- // https://github.com/microsoft/vscode/blob/4aac84268c6226d23828cc6a1fe45ee3982927f0/extensions/typescript-language-features/src/typescriptServiceClient.ts#L911
105
- await waitForAnimation(() => !progressStatusBarItem.currentProgress);
106
- return /** @type {MonacoEditor} */ (editor);
107
- }
108
-
109
- /**
110
- * @param {() => Promise<unknown> | unknown} condition
111
- * @param {number | undefined} [timeout]
112
- * @param {string | undefined} [message]
113
- * @returns {Promise<void>}
114
- */
115
- function waitForAnimation(condition, timeout, message) {
116
- const success = new Promise(async (resolve, reject) => {
117
- toTearDown.push({ dispose: () => reject(message ?? 'Test terminated before resolution.') });
118
- do {
119
- await animationFrame();
120
- } while (!condition());
121
- resolve();
122
- });
123
- if (timeout !== undefined) {
124
- const timedOut = new Promise((_, fail) => {
125
- const toClear = setTimeout(() => fail(new Error(message ?? 'Wait for animation timed out.')), timeout);
126
- toTearDown.push({ dispose: () => (fail(new Error(message ?? 'Wait for animation timed out.')), clearTimeout(toClear)) });
127
- });
128
- return Promise.race([success, timedOut]);
129
- }
130
- return success;
131
- }
132
-
133
- /**
134
- * We ignore attributes on purpose since they are not stable.
135
- * But structure is important for us to see whether the plain text is rendered or markdown.
136
- *
137
- * @param {Element} element
138
- * @returns {string}
139
- */
140
- function nodeAsString(element, indentation = '') {
141
- const header = element.tagName;
142
- let body = '';
143
- const childIndentation = indentation + ' ';
144
- for (let i = 0; i < element.childNodes.length; i++) {
145
- const childNode = element.childNodes.item(i);
146
- if (childNode.nodeType === childNode.TEXT_NODE) {
147
- body += childIndentation + `"${childNode.textContent}"` + '\n';
148
- } else if (childNode instanceof HTMLElement) {
149
- body += childIndentation + nodeAsString(childNode, childIndentation) + '\n';
150
- }
151
- }
152
- const result = header + (body ? ' {\n' + body + indentation + '}' : '');
153
- if (indentation) {
154
- return result;
155
- }
156
- return `\n${result}\n`;
157
- }
158
-
159
- /**
160
- * @param {MonacoEditor} editor
161
- */
162
- async function assertPeekOpened(editor) {
163
- /** @type any */
164
- const referencesController = editor.getControl().getContribution('editor.contrib.referencesController');
165
- await waitForAnimation(() => referencesController._widget && referencesController._widget._tree.getFocus().length);
166
-
167
- assert.isFalse(contextKeyService.match('editorTextFocus'));
168
- assert.isTrue(contextKeyService.match('referenceSearchVisible'));
169
- assert.isTrue(contextKeyService.match('listFocus'));
170
- }
171
-
172
- /**
173
- * @param {MonacoEditor} editor
174
- */
175
- async function openPeek(editor) {
176
- assert.isTrue(contextKeyService.match('editorTextFocus'));
177
- assert.isFalse(contextKeyService.match('referenceSearchVisible'));
178
- assert.isFalse(contextKeyService.match('listFocus'));
179
-
180
- await commands.executeCommand('editor.action.peekDefinition');
181
- await assertPeekOpened(editor);
182
- }
183
-
184
- async function openReference() {
185
- keybindings.dispatchKeyDown('Enter');
186
- await waitForAnimation(() => contextKeyService.match('listFocus'));
187
- assert.isFalse(contextKeyService.match('editorTextFocus'));
188
- assert.isTrue(contextKeyService.match('referenceSearchVisible'));
189
- assert.isTrue(contextKeyService.match('listFocus'));
190
- }
191
-
192
- /**
193
- * @param {MonacoEditor} editor
194
- */
195
- async function closePeek(editor) {
196
- await assertPeekOpened(editor);
197
-
198
- keybindings.dispatchKeyDown('Escape');
199
- await waitForAnimation(() => !contextKeyService.match('listFocus'));
200
- assert.isTrue(contextKeyService.match('editorTextFocus'));
201
- assert.isFalse(contextKeyService.match('referenceSearchVisible'));
202
- assert.isFalse(contextKeyService.match('listFocus'));
203
- }
204
-
205
- it('document formatting should be visible and enabled', async function () {
206
- await openEditor(demoFileUri);
207
- const menu = menuFactory.createContextMenu(EDITOR_CONTEXT_MENU);
208
- const item = menu.items.find(i => i.command === 'editor.action.formatDocument');
209
- if (item) {
210
- assert.isTrue(item.isVisible);
211
- assert.isTrue(item.isEnabled);
212
- } else {
213
- assert.isDefined(item);
214
- }
215
- });
216
-
217
- describe('editor.action.revealDefinition', function () {
218
- for (const preview of [false, true]) {
219
- const from = 'an editor' + (preview ? ' preview' : '');
220
- it('within ' + from, async function () {
221
- const editor = await openEditor(demoFileUri, preview);
222
- // const demoInstance = new Demo|Class('demo');
223
- editor.getControl().setPosition({ lineNumber: 24, column: 30 });
224
- assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'DemoClass');
225
-
226
- await commands.executeCommand('editor.action.revealDefinition');
227
-
228
- const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
229
- assert.equal(editorManager.activeEditor.isPreview, preview);
230
- assert.equal(activeEditor.uri.toString(), demoFileUri.toString());
231
- // constructor(someString: string) {
232
- const { lineNumber, column } = activeEditor.getControl().getPosition();
233
- assert.deepEqual({ lineNumber, column }, { lineNumber: 11, column: 5 });
234
- assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'constructor');
235
- });
236
-
237
- it(`from ${from} to another editor`, async function () {
238
- await editorManager.open(definitionFileUri, { mode: 'open' });
239
-
240
- const editor = await openEditor(demoFileUri, preview);
241
- // const bar: Defined|Interface = { coolField: [] };
242
- editor.getControl().setPosition({ lineNumber: 32, column: 19 });
243
- assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'DefinedInterface');
244
-
245
- await commands.executeCommand('editor.action.revealDefinition');
246
-
247
- const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
248
- assert.isFalse(editorManager.activeEditor.isPreview);
249
- assert.equal(activeEditor.uri.toString(), definitionFileUri.toString());
250
-
251
- // export interface |DefinedInterface {
252
- const { lineNumber, column } = activeEditor.getControl().getPosition();
253
- assert.deepEqual({ lineNumber, column }, { lineNumber: 2, column: 18 });
254
- assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'DefinedInterface');
255
- });
256
-
257
- it(`from ${from} to an editor preview`, async function () {
258
- const editor = await openEditor(demoFileUri);
259
- // const bar: Defined|Interface = { coolField: [] };
260
- editor.getControl().setPosition({ lineNumber: 32, column: 19 });
261
- assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'DefinedInterface');
262
-
263
- await commands.executeCommand('editor.action.revealDefinition');
264
-
265
- const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
266
- assert.isTrue(editorManager.activeEditor.isPreview);
267
- assert.equal(activeEditor.uri.toString(), definitionFileUri.toString());
268
- // export interface |DefinedInterface {
269
- const { lineNumber, column } = activeEditor.getControl().getPosition();
270
- assert.deepEqual({ lineNumber, column }, { lineNumber: 2, column: 18 });
271
- assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'DefinedInterface');
272
- });
273
- }
274
- });
275
-
276
- describe('editor.action.peekDefinition', function () {
277
-
278
- for (const preview of [false, true]) {
279
- const from = 'an editor' + (preview ? ' preview' : '');
280
- it('within ' + from, async function () {
281
- const editor = await openEditor(demoFileUri, preview);
282
- editor.getControl().revealLine(24);
283
- // const demoInstance = new Demo|Class('demo');
284
- editor.getControl().setPosition({ lineNumber: 24, column: 30 });
285
- assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'DemoClass');
286
-
287
- await openPeek(editor);
288
- await openReference();
289
-
290
- const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
291
- assert.equal(editorManager.activeEditor.isPreview, preview);
292
- assert.equal(activeEditor.uri.toString(), demoFileUri.toString());
293
- // constructor(someString: string) {
294
- const { lineNumber, column } = activeEditor.getControl().getPosition();
295
- assert.deepEqual({ lineNumber, column }, { lineNumber: 11, column: 5 });
296
- assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'constructor');
297
-
298
- await closePeek(activeEditor);
299
- });
300
-
301
- it(`from ${from} to another editor`, async function () {
302
- await editorManager.open(definitionFileUri, { mode: 'open' });
303
-
304
- const editor = await openEditor(demoFileUri, preview);
305
- editor.getControl().revealLine(32);
306
- // const bar: Defined|Interface = { coolField: [] };
307
- editor.getControl().setPosition({ lineNumber: 32, column: 19 });
308
- assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'DefinedInterface');
309
-
310
- await openPeek(editor);
311
- await openReference();
312
-
313
- const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
314
- assert.isFalse(editorManager.activeEditor.isPreview);
315
- assert.equal(activeEditor.uri.toString(), definitionFileUri.toString());
316
- // export interface |DefinedInterface {
317
- const { lineNumber, column } = activeEditor.getControl().getPosition();
318
- assert.deepEqual({ lineNumber, column }, { lineNumber: 2, column: 18 });
319
- assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'DefinedInterface');
320
-
321
- await closePeek(activeEditor);
322
- });
323
-
324
- it(`from ${from} to an editor preview`, async function () {
325
- const editor = await openEditor(demoFileUri);
326
- editor.getControl().revealLine(32);
327
- // const bar: Defined|Interface = { coolField: [] };
328
- editor.getControl().setPosition({ lineNumber: 32, column: 19 });
329
- assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'DefinedInterface');
330
-
331
- await openPeek(editor);
332
- await openReference();
333
-
334
- const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
335
- assert.isTrue(editorManager.activeEditor.isPreview);
336
- assert.equal(activeEditor.uri.toString(), definitionFileUri.toString());
337
- // export interface |DefinedInterface {
338
- const { lineNumber, column } = activeEditor.getControl().getPosition();
339
- assert.deepEqual({ lineNumber, column }, { lineNumber: 2, column: 18 });
340
- assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'DefinedInterface');
341
-
342
- await closePeek(activeEditor);
343
- });
344
- }
345
- });
346
-
347
- it('editor.action.triggerSuggest', async function () {
348
- const editor = await openEditor(demoFileUri);
349
- // const demoVariable = demoInstance.[stringField];
350
- editor.getControl().setPosition({ lineNumber: 26, column: 46 });
351
- editor.getControl().setSelection(new Selection(26, 46, 26, 35));
352
- assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'stringField');
353
-
354
- assert.isTrue(contextKeyService.match('editorTextFocus'));
355
- assert.isFalse(contextKeyService.match('suggestWidgetVisible'));
356
-
357
- await commands.executeCommand('editor.action.triggerSuggest');
358
- await waitForAnimation(() => contextKeyService.match('suggestWidgetVisible'));
359
-
360
- assert.isTrue(contextKeyService.match('editorTextFocus'));
361
- assert.isTrue(contextKeyService.match('suggestWidgetVisible'));
362
-
363
- keybindings.dispatchKeyDown('Enter');
364
- await waitForAnimation(() => !contextKeyService.match('suggestWidgetVisible'));
365
-
366
- assert.isTrue(contextKeyService.match('editorTextFocus'));
367
- assert.isFalse(contextKeyService.match('suggestWidgetVisible'));
368
-
369
- const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
370
- assert.equal(activeEditor.uri.toString(), demoFileUri.toString());
371
- // demoInstance.stringField;
372
- const { lineNumber, column } = activeEditor.getControl().getPosition();
373
- assert.deepEqual({ lineNumber, column }, { lineNumber: 26, column: 46 });
374
- assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'doSomething');
375
- });
376
-
377
- it('editor.action.triggerSuggest navigate', async function () {
378
- const editor = await openEditor(demoFileUri);
379
- // demoInstance.[|stringField];
380
- editor.getControl().setPosition({ lineNumber: 26, column: 46 });
381
- editor.getControl().setSelection(new Selection(26, 46, 26, 35));
382
- assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'stringField');
383
-
384
- /** @type {import('@theia/monaco-editor-core/src/vs/editor/contrib/suggest/browser/suggestController').SuggestController} */
385
- const suggest = editor.getControl().getContribution('editor.contrib.suggestController');
386
- const getFocusedLabel = () => {
387
- const focusedItem = suggest.widget.value.getFocusedItem();
388
- return focusedItem && focusedItem.item.completion.label;
389
- };
390
-
391
- assert.isUndefined(getFocusedLabel());
392
- assert.isFalse(contextKeyService.match('suggestWidgetVisible'));
393
-
394
- await commands.executeCommand('editor.action.triggerSuggest');
395
- await waitForAnimation(() => contextKeyService.match('suggestWidgetVisible') && getFocusedLabel() === 'doSomething', 5000);
396
-
397
- assert.equal(getFocusedLabel(), 'doSomething');
398
- assert.isTrue(contextKeyService.match('suggestWidgetVisible'));
399
-
400
- keybindings.dispatchKeyDown('ArrowDown');
401
- await waitForAnimation(() => contextKeyService.match('suggestWidgetVisible') && getFocusedLabel() === 'numberField', 2000);
402
-
403
- assert.equal(getFocusedLabel(), 'numberField');
404
- assert.isTrue(contextKeyService.match('suggestWidgetVisible'));
405
-
406
- keybindings.dispatchKeyDown('ArrowUp');
407
- await waitForAnimation(() => contextKeyService.match('suggestWidgetVisible') && getFocusedLabel() === 'doSomething', 2000);
408
-
409
- assert.equal(getFocusedLabel(), 'doSomething');
410
- assert.isTrue(contextKeyService.match('suggestWidgetVisible'));
411
-
412
- keybindings.dispatchKeyDown('Escape');
413
- await waitForAnimation(() => !contextKeyService.match('suggestWidgetVisible') && getFocusedLabel() === undefined, 5000);
414
-
415
- assert.isUndefined(getFocusedLabel());
416
- assert.isFalse(contextKeyService.match('suggestWidgetVisible'));
417
- });
418
-
419
- it('editor.action.rename', async function () {
420
- const editor = await openEditor(demoFileUri);
421
- // const |demoVariable = demoInstance.stringField;
422
- editor.getControl().setPosition({ lineNumber: 26, column: 7 });
423
- assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'demoVariable');
424
-
425
- assert.isTrue(contextKeyService.match('editorTextFocus'));
426
- assert.isFalse(contextKeyService.match('renameInputVisible'));
427
-
428
- commands.executeCommand('editor.action.rename');
429
- await waitForAnimation(() => contextKeyService.match('renameInputVisible')
430
- && document.activeElement instanceof HTMLInputElement
431
- && document.activeElement.selectionEnd === 'demoVariable'.length);
432
- assert.isFalse(contextKeyService.match('editorTextFocus'));
433
- assert.isTrue(contextKeyService.match('renameInputVisible'));
434
-
435
- const input = document.activeElement;
436
- if (!(input instanceof HTMLInputElement)) {
437
- assert.fail('expected focused input, but: ' + input);
438
- return;
439
- }
440
-
441
- input.value = 'foo';
442
- keybindings.dispatchKeyDown('Enter', input);
443
-
444
- // all rename edits should be grouped in one edit operation and applied in the same tick
445
- await new Promise(resolve => editor.getControl().onDidChangeModelContent(resolve));
446
-
447
- assert.isTrue(contextKeyService.match('editorTextFocus'));
448
- assert.isFalse(contextKeyService.match('renameInputVisible'));
449
-
450
- const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
451
- assert.equal(activeEditor.uri.toString(), demoFileUri.toString());
452
- // const |foo = new Container();
453
- const { lineNumber, column } = activeEditor.getControl().getPosition();
454
- assert.deepEqual({ lineNumber, column }, { lineNumber: 26, column: 7 });
455
- assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber: 28, column: 1 }).word, 'foo');
456
- });
457
-
458
- it('editor.action.triggerParameterHints', async function () {
459
- const editor = await openEditor(demoFileUri);
460
- // const demoInstance = new DemoClass('|demo');
461
- editor.getControl().setPosition({ lineNumber: 24, column: 37 });
462
- assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, "demo");
463
-
464
- assert.isTrue(contextKeyService.match('editorTextFocus'));
465
- assert.isFalse(contextKeyService.match('parameterHintsVisible'));
466
-
467
- await commands.executeCommand('editor.action.triggerParameterHints');
468
- await waitForAnimation(() => contextKeyService.match('parameterHintsVisible'));
469
-
470
- assert.isTrue(contextKeyService.match('editorTextFocus'));
471
- assert.isTrue(contextKeyService.match('parameterHintsVisible'));
472
-
473
- keybindings.dispatchKeyDown('Escape');
474
- await waitForAnimation(() => !contextKeyService.match('parameterHintsVisible'));
475
-
476
- assert.isTrue(contextKeyService.match('editorTextFocus'));
477
- assert.isFalse(contextKeyService.match('parameterHintsVisible'));
478
- });
479
-
480
- it('editor.action.showHover', async function () {
481
- const editor = await openEditor(demoFileUri);
482
- // class |DemoClass);
483
- editor.getControl().setPosition({ lineNumber: 8, column: 7 });
484
- assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'DemoClass');
485
-
486
- /** @type {import('@theia/monaco-editor-core/src/vs/editor/contrib/hover/browser/hover').ModesHoverController} */
487
- const hover = editor.getControl().getContribution('editor.contrib.hover');
488
-
489
- assert.isTrue(contextKeyService.match('editorTextFocus'));
490
- assert.isFalse(Boolean(hover['_contentWidget']?.['_widget']?.['_visibleData']));
491
- await commands.executeCommand('editor.action.showHover');
492
- let doLog = true;
493
- await waitForAnimation(() => hover['_contentWidget']?.['_widget']?.['_visibleData']);
494
- assert.isTrue(contextKeyService.match('editorTextFocus'));
495
- assert.isTrue(Boolean(hover['_contentWidget']?.['_widget']?.['_visibleData']));
496
- assert.deepEqual(nodeAsString(hover['_contentWidget']?.['_widget']?.['_hover']?.['contentsDomNode']).trim(), `
497
- DIV {
498
- DIV {
499
- DIV {
500
- DIV {
501
- DIV {
502
- SPAN {
503
- DIV {
504
- SPAN {
505
- "class"
506
- }
507
- SPAN {
508
- " "
509
- }
510
- SPAN {
511
- "DemoClass"
512
- }
513
- }
514
- }
515
- }
516
- }
517
- }
518
- }
519
- }`.trim());
520
- keybindings.dispatchKeyDown('Escape');
521
- await waitForAnimation(() => !hover['_contentWidget']?.['_widget']?.['_visibleData']);
522
- assert.isTrue(contextKeyService.match('editorTextFocus'));
523
- assert.isFalse(Boolean(hover['_contentWidget']?.['_widget']?.['_visibleData']));
524
- });
525
-
526
- it('highlight semantic (write) occurrences', async function () {
527
- const editor = await openEditor(demoFileUri);
528
- // const |container = new Container();
529
- const lineNumber = 24;
530
- const column = 7;
531
- const endColumn = column + 'demoInstance'.length;
532
-
533
- const hasWriteDecoration = () => {
534
- for (const decoration of editor.getControl().getModel().getLineDecorations(lineNumber)) {
535
- if (decoration.range.startColumn === column && decoration.range.endColumn === endColumn && decoration.options.className === 'wordHighlightStrong') {
536
- return true;
537
- }
538
- }
539
- return false;
540
- };
541
- assert.isFalse(hasWriteDecoration());
542
-
543
- editor.getControl().setPosition({ lineNumber, column });
544
- assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'demoInstance');
545
- // highlight occurrences is not trigged on the explicit position change, so move a cursor as a user
546
- keybindings.dispatchKeyDown('ArrowRight');
547
- await waitForAnimation(() => hasWriteDecoration());
548
-
549
- assert.isTrue(hasWriteDecoration());
550
- });
551
-
552
- it('editor.action.goToImplementation', async function () {
553
- const editor = await openEditor(demoFileUri);
554
- // const demoInstance = new Demo|Class('demo');
555
- editor.getControl().setPosition({ lineNumber: 24, column: 30 });
556
- assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'DemoClass');
557
-
558
- await commands.executeCommand('editor.action.goToImplementation');
559
-
560
- const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
561
- assert.equal(activeEditor.uri.toString(), demoFileUri.toString());
562
- // class |DemoClass implements DemoInterface {
563
- const { lineNumber, column } = activeEditor.getControl().getPosition();
564
- assert.deepEqual({ lineNumber, column }, { lineNumber: 8, column: 7 });
565
- assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'DemoClass');
566
- });
567
-
568
- it('editor.action.goToTypeDefinition', async function () {
569
- const editor = await openEditor(demoFileUri);
570
- // const demoVariable = demo|Instance.stringField;
571
- editor.getControl().setPosition({ lineNumber: 26, column: 26 });
572
- assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'demoInstance');
573
-
574
- await commands.executeCommand('editor.action.goToTypeDefinition');
575
-
576
- const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
577
- assert.equal(activeEditor.uri.toString(), demoFileUri.toString());
578
- // class |DemoClass implements DemoInterface {
579
- const { lineNumber, column } = activeEditor.getControl().getPosition();
580
- assert.deepEqual({ lineNumber, column }, { lineNumber: 8, column: 7 });
581
- assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'DemoClass');
582
- });
583
-
584
- it('run reference code lens', async function () {
585
- const preferenceName = 'typescript.referencesCodeLens.enabled';
586
- const globalValue = preferences.inspect(preferenceName).globalValue;
587
- toTearDown.push({ dispose: () => preferences.set(preferenceName, globalValue, PreferenceScope.User) });
588
- await preferences.set(preferenceName, false, PreferenceScope.User);
589
-
590
- const editor = await openEditor(demoFileUri);
591
-
592
- /** @type {import('@theia/monaco-editor-core/src/vs/editor/contrib/codelens/browser/codelensController').CodeLensContribution} */
593
- const codeLens = editor.getControl().getContribution('css.editor.codeLens');
594
- const codeLensNode = () => codeLens['_lenses'][0]?.['_contentWidget']?.['_domNode'];
595
- const codeLensNodeVisible = () => {
596
- const n = codeLensNode();
597
- return !!n && n.style.visibility !== 'hidden';
598
- };
599
-
600
- assert.isFalse(codeLensNodeVisible());
601
-
602
- // |interface DemoInterface {
603
- const position = { lineNumber: 2, column: 1 };
604
- await preferences.set(preferenceName, true, PreferenceScope.User);
605
-
606
- editor.getControl().revealPosition(position);
607
- await waitForAnimation(() => codeLensNodeVisible());
608
-
609
- assert.isTrue(codeLensNodeVisible());
610
- const node = codeLensNode();
611
- assert.isDefined(node);
612
- assert.equal(nodeAsString(node), `
613
- SPAN {
614
- A {
615
- "1 reference"
616
- }
617
- }
618
- `);
619
- const link = node.getElementsByTagName('a').item(0);
620
- assert.isDefined(link);
621
- link.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
622
- await assertPeekOpened(editor);
623
- await closePeek(editor);
624
- });
625
-
626
- it('editor.action.quickFix', async function () {
627
- const column = 45;
628
- const lineNumber = 26;
629
- const editor = await openEditor(demoFileUri);
630
- const currentChar = () => editor.getControl().getModel().getLineContent(lineNumber).charAt(column - 1);
631
-
632
- // const demoVariable = demoInstance.stringField; --> const demoVariable = demoInstance.stringFiel;
633
- editor.getControl().getModel().applyEdits([{
634
- range: {
635
- startLineNumber: lineNumber,
636
- endLineNumber: lineNumber,
637
- startColumn: 45,
638
- endColumn: 46
639
- },
640
- forceMoveMarkers: false,
641
- text: ''
642
- }]);
643
- editor.getControl().setPosition({ lineNumber, column });
644
- editor.getControl().revealPosition({ lineNumber, column });
645
- assert.equal(currentChar(), ';');
646
-
647
- /** @type {import('@theia/monaco-editor-core/src/vs/editor/contrib/codeAction/browser/codeActionCommands').CodeActionController} */
648
- const codeActionController = editor.getControl().getContribution('editor.contrib.codeActionController');
649
- const lightBulbNode = () => {
650
- const ui = codeActionController['_ui'].rawValue;
651
- const lightBulb = ui && ui['_lightBulbWidget'].rawValue;
652
- return lightBulb && lightBulb['_domNode'];
653
- };
654
- const lightBulbVisible = () => {
655
- const node = lightBulbNode();
656
- return !!node && node.style.visibility !== 'hidden';
657
- };
658
-
659
- assert.isFalse(lightBulbVisible());
660
- await waitForAnimation(() => lightBulbVisible());
661
-
662
- await commands.executeCommand('editor.action.quickFix');
663
- const codeActionSelector = '.codeActionWidget';
664
- assert.isFalse(!!document.querySelector(codeActionSelector), 'codeActionWidget should not be visible');
665
-
666
- await waitForAnimation(() => !!document.querySelector(codeActionSelector), 5000);
667
- await animationFrame();
668
-
669
- keybindings.dispatchKeyDown('Enter');
670
-
671
- await waitForAnimation(() => currentChar() === 'd', 5000);
672
- assert.equal(currentChar(), 'd');
673
-
674
- await waitForAnimation(() => !lightBulbVisible());
675
- assert.isFalse(lightBulbVisible());
676
- });
677
-
678
- it('editor.action.formatDocument', async function () {
679
- const lineNumber = 5;
680
- const editor = await openEditor(demoFileUri);
681
- const originalLength = editor.getControl().getModel().getLineLength(lineNumber);
682
-
683
- // doSomething(): number; --> doSomething() : number;
684
- editor.getControl().getModel().applyEdits([{
685
- range: Range.fromPositions({ lineNumber, column: 18 }, { lineNumber, column: 18 }),
686
- forceMoveMarkers: false,
687
- text: ' '
688
- }]);
689
-
690
- assert.equal(editor.getControl().getModel().getLineLength(lineNumber), originalLength + 1);
691
-
692
- await commands.executeCommand('editor.action.formatDocument');
693
-
694
- assert.equal(editor.getControl().getModel().getLineLength(lineNumber), originalLength);
695
- });
696
-
697
- it('editor.action.formatSelection', async function () {
698
- // doSomething(): number {
699
- const lineNumber = 15;
700
- const editor = await openEditor(demoFileUri);
701
- const originalLength /* 28 */ = editor.getControl().getModel().getLineLength(lineNumber);
702
-
703
- // doSomething( ) : number {
704
- editor.getControl().getModel().applyEdits([{
705
- range: Range.fromPositions({ lineNumber, column: 17 }, { lineNumber, column: 18 }),
706
- forceMoveMarkers: false,
707
- text: ' ) '
708
- }]);
709
-
710
- assert.equal(editor.getControl().getModel().getLineLength(lineNumber), originalLength + 4);
711
-
712
- // [const { Container }] = require('inversify');
713
- editor.getControl().setSelection({ startLineNumber: lineNumber, startColumn: 1, endLineNumber: lineNumber, endColumn: 32 });
714
-
715
- await commands.executeCommand('editor.action.formatSelection');
716
-
717
- // [const { Container }] = require('inversify');
718
- assert.equal(editor.getControl().getModel().getLineLength(lineNumber), originalLength);
719
- });
720
-
721
- for (const referenceViewCommand of ['references-view.find', 'references-view.findImplementations']) {
722
- it(referenceViewCommand, async function () {
723
- let steps = 0;
724
- const editor = await openEditor(demoFileUri);
725
- // const demo|Instance = new DemoClass('demo');
726
- editor.getControl().setPosition({ lineNumber: 24, column: 11 });
727
- assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'demoInstance');
728
- const view = await pluginViewRegistry.openView('references-view.tree', { reveal: true });
729
- assert.isDefined(view);
730
- assert.isTrue(view.isVisible);
731
- await commands.executeCommand('references-view.clear');
732
- const expectedMessage = referenceViewCommand === 'references-view.find' ? '2 results in 1 file' : '1 result in 1 file';
733
- const getResultText = () => view.node.getElementsByClassName('theia-TreeViewInfo').item(0)?.textContent;
734
- await commands.executeCommand(referenceViewCommand);
735
- await waitForAnimation(() => getResultText() === expectedMessage, 5000);
736
- assert.equal(getResultText(), expectedMessage);
737
- });
738
- }
739
-
740
- it('Can execute code actions', async function () {
741
- const editor = await openEditor(demoFileUri);
742
- /** @type {import('@theia/monaco-editor-core/src/vs/editor/contrib/codeAction/browser/codeActionCommands').CodeActionController} */
743
- const codeActionController = editor.getControl().getContribution('editor.contrib.codeActionController');
744
- const isActionAvailable = () => {
745
- const lightbulbVisibility = codeActionController['_ui'].rawValue?.['_lightBulbWidget'].rawValue?.['_domNode'].style.visibility;
746
- return lightbulbVisibility !== undefined && lightbulbVisibility !== 'hidden';
747
- }
748
- assert.isFalse(isActionAvailable());
749
- // import { DefinedInterface } from "./demo-definitions-file";
750
- assert.strictEqual(editor.getControl().getModel().getLineContent(30), 'import { DefinedInterface } from "./demo-definitions-file";');
751
- editor.getControl().revealLine(30);
752
- editor.getControl().setSelection(new Selection(30, 1, 30, 60));
753
- await waitForAnimation(() => isActionAvailable(), 5000, 'No code action available. (1)');
754
- assert.isTrue(isActionAvailable());
755
-
756
- await commands.executeCommand('editor.action.quickFix');
757
- await waitForAnimation(() => Boolean(document.querySelector('.context-view-pointerBlock')), 5000, 'No context menu appeared. (1)');
758
- await animationFrame();
759
-
760
- keybindings.dispatchKeyDown('Enter');
761
-
762
- assert.isNotNull(editor.getControl());
763
- assert.isNotNull(editor.getControl().getModel());
764
- console.log(`content: ${editor.getControl().getModel().getLineContent(30)}`);
765
- await waitForAnimation(() => editor.getControl().getModel().getLineContent(30) === 'import * as demoDefinitionsFile from "./demo-definitions-file";', 5000, 'The namespace import did not take effect.');
766
-
767
- editor.getControl().setSelection(new Selection(30, 1, 30, 64));
768
- await waitForAnimation(() => isActionAvailable(), 5000, 'No code action available. (2)');
769
-
770
- // Change it back: https://github.com/eclipse-theia/theia/issues/11059
771
- await commands.executeCommand('editor.action.quickFix');
772
- await waitForAnimation(() => Boolean(document.querySelector('.context-view-pointerBlock')), 5000, 'No context menu appeared. (2)');
773
- await animationFrame();
774
-
775
- keybindings.dispatchKeyDown('Enter');
776
-
777
- await waitForAnimation(() => editor.getControl().getModel().getLineContent(30) === 'import { DefinedInterface } from "./demo-definitions-file";', 5000, 'The named import did not take effect.');
778
- });
779
- });
1
+ // *****************************************************************************
2
+ // Copyright (C) 2020 TypeFox and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ // @ts-check
18
+ describe('TypeScript', function () {
19
+ this.timeout(30_000);
20
+
21
+ const { assert } = chai;
22
+
23
+ const Uri = require('@theia/core/lib/common/uri');
24
+ const { DisposableCollection } = require('@theia/core/lib/common/disposable');
25
+ const { BrowserMainMenuFactory } = require('@theia/core/lib/browser/menu/browser-menu-plugin');
26
+ const { EditorManager } = require('@theia/editor/lib/browser/editor-manager');
27
+ const { EditorWidget } = require('@theia/editor/lib/browser/editor-widget');
28
+ const { EDITOR_CONTEXT_MENU } = require('@theia/editor/lib/browser/editor-menu');
29
+ const { WorkspaceService } = require('@theia/workspace/lib/browser/workspace-service');
30
+ const { MonacoEditor } = require('@theia/monaco/lib/browser/monaco-editor');
31
+ const { HostedPluginSupport } = require('@theia/plugin-ext/lib/hosted/browser/hosted-plugin');
32
+ const { ContextKeyService } = require('@theia/core/lib/browser/context-key-service');
33
+ const { CommandRegistry } = require('@theia/core/lib/common/command');
34
+ const { KeybindingRegistry } = require('@theia/core/lib/browser/keybinding');
35
+ const { OpenerService, open } = require('@theia/core/lib/browser/opener-service');
36
+ const { animationFrame } = require('@theia/core/lib/browser/browser');
37
+ const { PreferenceService, PreferenceScope } = require('@theia/core/lib/browser/preferences/preference-service');
38
+ const { ProgressStatusBarItem } = require('@theia/core/lib/browser/progress-status-bar-item');
39
+ const { PluginViewRegistry } = require('@theia/plugin-ext/lib/main/browser/view/plugin-view-registry');
40
+ const { Range } = require('@theia/monaco-editor-core/esm/vs/editor/common/core/range');
41
+ const { Selection } = require('@theia/monaco-editor-core/esm/vs/editor/common/core/selection');
42
+
43
+ const container = window.theia.container;
44
+ const editorManager = container.get(EditorManager);
45
+ const workspaceService = container.get(WorkspaceService);
46
+ const menuFactory = container.get(BrowserMainMenuFactory);
47
+ const pluginService = container.get(HostedPluginSupport);
48
+ const contextKeyService = container.get(ContextKeyService);
49
+ const commands = container.get(CommandRegistry);
50
+ const openerService = container.get(OpenerService);
51
+ /** @type {KeybindingRegistry} */
52
+ const keybindings = container.get(KeybindingRegistry);
53
+ /** @type {import('@theia/core/lib/browser/preferences/preference-service').PreferenceService} */
54
+ const preferences = container.get(PreferenceService);
55
+ const progressStatusBarItem = container.get(ProgressStatusBarItem);
56
+ /** @type {PluginViewRegistry} */
57
+ const pluginViewRegistry = container.get(PluginViewRegistry);
58
+
59
+ const typescriptPluginId = 'vscode.typescript-language-features';
60
+ const referencesPluginId = 'ms-vscode.references-view';
61
+ /** @type Uri.URI */
62
+ const rootUri = workspaceService.tryGetRoots()[0].resource;
63
+ const demoFileUri = rootUri.resolveToAbsolute('../api-tests/test-ts-workspace/demo-file.ts');
64
+ const definitionFileUri = rootUri.resolveToAbsolute('../api-tests/test-ts-workspace/demo-definitions-file.ts');
65
+ let originalAutoSaveValue = preferences.inspect('files.autoSave').globalValue;
66
+
67
+ before(async function () {
68
+ await pluginService.didStart;
69
+ await Promise.all([typescriptPluginId, referencesPluginId].map(async pluginId => {
70
+ if (!pluginService.getPlugin(pluginId)) {
71
+ throw new Error(pluginId + ' should be started');
72
+ }
73
+ await pluginService.activatePlugin(pluginId);
74
+ }).concat(preferences.set('files.autoSave', 'off', PreferenceScope.User)));
75
+ });
76
+
77
+ beforeEach(async function () {
78
+ await editorManager.closeAll({ save: false });
79
+ });
80
+
81
+ const toTearDown = new DisposableCollection();
82
+ afterEach(async () => {
83
+ toTearDown.dispose();
84
+ await editorManager.closeAll({ save: false });
85
+ });
86
+
87
+ after(async () => {
88
+ await preferences.set('files.autoSave', originalAutoSaveValue, PreferenceScope.User);
89
+ })
90
+
91
+ /**
92
+ * @param {Uri.default} uri
93
+ * @param {boolean} preview
94
+ */
95
+ async function openEditor(uri, preview = false) {
96
+ const widget = await open(openerService, uri, { mode: 'activate', preview });
97
+ const editorWidget = widget instanceof EditorWidget ? widget : undefined;
98
+ const editor = MonacoEditor.get(editorWidget);
99
+ assert.isDefined(editor);
100
+ // wait till tsserver is running, see:
101
+ // https://github.com/microsoft/vscode/blob/93cbbc5cae50e9f5f5046343c751b6d010468200/extensions/typescript-language-features/src/extension.ts#L98-L103
102
+ await waitForAnimation(() => contextKeyService.match('typescript.isManagedFile'));
103
+ // wait till projects are loaded, see:
104
+ // https://github.com/microsoft/vscode/blob/4aac84268c6226d23828cc6a1fe45ee3982927f0/extensions/typescript-language-features/src/typescriptServiceClient.ts#L911
105
+ await waitForAnimation(() => !progressStatusBarItem.currentProgress);
106
+ return /** @type {MonacoEditor} */ (editor);
107
+ }
108
+
109
+ /**
110
+ * @param {() => Promise<unknown> | unknown} condition
111
+ * @param {number | undefined} [timeout]
112
+ * @param {string | undefined} [message]
113
+ * @returns {Promise<void>}
114
+ */
115
+ function waitForAnimation(condition, timeout, message) {
116
+ const success = new Promise(async (resolve, reject) => {
117
+ toTearDown.push({ dispose: () => reject(message ?? 'Test terminated before resolution.') });
118
+ do {
119
+ await animationFrame();
120
+ } while (!condition());
121
+ resolve();
122
+ });
123
+ if (timeout !== undefined) {
124
+ const timedOut = new Promise((_, fail) => {
125
+ const toClear = setTimeout(() => fail(new Error(message ?? 'Wait for animation timed out.')), timeout);
126
+ toTearDown.push({ dispose: () => (fail(new Error(message ?? 'Wait for animation timed out.')), clearTimeout(toClear)) });
127
+ });
128
+ return Promise.race([success, timedOut]);
129
+ }
130
+ return success;
131
+ }
132
+
133
+ /**
134
+ * We ignore attributes on purpose since they are not stable.
135
+ * But structure is important for us to see whether the plain text is rendered or markdown.
136
+ *
137
+ * @param {Element} element
138
+ * @returns {string}
139
+ */
140
+ function nodeAsString(element, indentation = '') {
141
+ const header = element.tagName;
142
+ let body = '';
143
+ const childIndentation = indentation + ' ';
144
+ for (let i = 0; i < element.childNodes.length; i++) {
145
+ const childNode = element.childNodes.item(i);
146
+ if (childNode.nodeType === childNode.TEXT_NODE) {
147
+ body += childIndentation + `"${childNode.textContent}"` + '\n';
148
+ } else if (childNode instanceof HTMLElement) {
149
+ body += childIndentation + nodeAsString(childNode, childIndentation) + '\n';
150
+ }
151
+ }
152
+ const result = header + (body ? ' {\n' + body + indentation + '}' : '');
153
+ if (indentation) {
154
+ return result;
155
+ }
156
+ return `\n${result}\n`;
157
+ }
158
+
159
+ /**
160
+ * @param {MonacoEditor} editor
161
+ */
162
+ async function assertPeekOpened(editor) {
163
+ /** @type any */
164
+ const referencesController = editor.getControl().getContribution('editor.contrib.referencesController');
165
+ await waitForAnimation(() => referencesController._widget && referencesController._widget._tree.getFocus().length);
166
+
167
+ assert.isFalse(contextKeyService.match('editorTextFocus'));
168
+ assert.isTrue(contextKeyService.match('referenceSearchVisible'));
169
+ assert.isTrue(contextKeyService.match('listFocus'));
170
+ }
171
+
172
+ /**
173
+ * @param {MonacoEditor} editor
174
+ */
175
+ async function openPeek(editor) {
176
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
177
+ assert.isFalse(contextKeyService.match('referenceSearchVisible'));
178
+ assert.isFalse(contextKeyService.match('listFocus'));
179
+
180
+ await commands.executeCommand('editor.action.peekDefinition');
181
+ await assertPeekOpened(editor);
182
+ }
183
+
184
+ async function openReference() {
185
+ keybindings.dispatchKeyDown('Enter');
186
+ await waitForAnimation(() => contextKeyService.match('listFocus'));
187
+ assert.isFalse(contextKeyService.match('editorTextFocus'));
188
+ assert.isTrue(contextKeyService.match('referenceSearchVisible'));
189
+ assert.isTrue(contextKeyService.match('listFocus'));
190
+ }
191
+
192
+ /**
193
+ * @param {MonacoEditor} editor
194
+ */
195
+ async function closePeek(editor) {
196
+ await assertPeekOpened(editor);
197
+
198
+ keybindings.dispatchKeyDown('Escape');
199
+ await waitForAnimation(() => !contextKeyService.match('listFocus'));
200
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
201
+ assert.isFalse(contextKeyService.match('referenceSearchVisible'));
202
+ assert.isFalse(contextKeyService.match('listFocus'));
203
+ }
204
+
205
+ it('document formatting should be visible and enabled', async function () {
206
+ await openEditor(demoFileUri);
207
+ const menu = menuFactory.createContextMenu(EDITOR_CONTEXT_MENU);
208
+ const item = menu.items.find(i => i.command === 'editor.action.formatDocument');
209
+ if (item) {
210
+ assert.isTrue(item.isVisible);
211
+ assert.isTrue(item.isEnabled);
212
+ } else {
213
+ assert.isDefined(item);
214
+ }
215
+ });
216
+
217
+ describe('editor.action.revealDefinition', function () {
218
+ for (const preview of [false, true]) {
219
+ const from = 'an editor' + (preview ? ' preview' : '');
220
+ it('within ' + from, async function () {
221
+ const editor = await openEditor(demoFileUri, preview);
222
+ // const demoInstance = new Demo|Class('demo');
223
+ editor.getControl().setPosition({ lineNumber: 24, column: 30 });
224
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'DemoClass');
225
+
226
+ await commands.executeCommand('editor.action.revealDefinition');
227
+
228
+ const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
229
+ assert.equal(editorManager.activeEditor.isPreview, preview);
230
+ assert.equal(activeEditor.uri.toString(), demoFileUri.toString());
231
+ // constructor(someString: string) {
232
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
233
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 11, column: 5 });
234
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'constructor');
235
+ });
236
+
237
+ it(`from ${from} to another editor`, async function () {
238
+ await editorManager.open(definitionFileUri, { mode: 'open' });
239
+
240
+ const editor = await openEditor(demoFileUri, preview);
241
+ // const bar: Defined|Interface = { coolField: [] };
242
+ editor.getControl().setPosition({ lineNumber: 32, column: 19 });
243
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'DefinedInterface');
244
+
245
+ await commands.executeCommand('editor.action.revealDefinition');
246
+
247
+ const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
248
+ assert.isFalse(editorManager.activeEditor.isPreview);
249
+ assert.equal(activeEditor.uri.toString(), definitionFileUri.toString());
250
+
251
+ // export interface |DefinedInterface {
252
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
253
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 2, column: 18 });
254
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'DefinedInterface');
255
+ });
256
+
257
+ it(`from ${from} to an editor preview`, async function () {
258
+ const editor = await openEditor(demoFileUri);
259
+ // const bar: Defined|Interface = { coolField: [] };
260
+ editor.getControl().setPosition({ lineNumber: 32, column: 19 });
261
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'DefinedInterface');
262
+
263
+ await commands.executeCommand('editor.action.revealDefinition');
264
+
265
+ const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
266
+ assert.isTrue(editorManager.activeEditor.isPreview);
267
+ assert.equal(activeEditor.uri.toString(), definitionFileUri.toString());
268
+ // export interface |DefinedInterface {
269
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
270
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 2, column: 18 });
271
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'DefinedInterface');
272
+ });
273
+ }
274
+ });
275
+
276
+ describe('editor.action.peekDefinition', function () {
277
+
278
+ for (const preview of [false, true]) {
279
+ const from = 'an editor' + (preview ? ' preview' : '');
280
+ it('within ' + from, async function () {
281
+ const editor = await openEditor(demoFileUri, preview);
282
+ editor.getControl().revealLine(24);
283
+ // const demoInstance = new Demo|Class('demo');
284
+ editor.getControl().setPosition({ lineNumber: 24, column: 30 });
285
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'DemoClass');
286
+
287
+ await openPeek(editor);
288
+ await openReference();
289
+
290
+ const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
291
+ assert.equal(editorManager.activeEditor.isPreview, preview);
292
+ assert.equal(activeEditor.uri.toString(), demoFileUri.toString());
293
+ // constructor(someString: string) {
294
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
295
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 11, column: 5 });
296
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'constructor');
297
+
298
+ await closePeek(activeEditor);
299
+ });
300
+
301
+ it(`from ${from} to another editor`, async function () {
302
+ await editorManager.open(definitionFileUri, { mode: 'open' });
303
+
304
+ const editor = await openEditor(demoFileUri, preview);
305
+ editor.getControl().revealLine(32);
306
+ // const bar: Defined|Interface = { coolField: [] };
307
+ editor.getControl().setPosition({ lineNumber: 32, column: 19 });
308
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'DefinedInterface');
309
+
310
+ await openPeek(editor);
311
+ await openReference();
312
+
313
+ const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
314
+ assert.isFalse(editorManager.activeEditor.isPreview);
315
+ assert.equal(activeEditor.uri.toString(), definitionFileUri.toString());
316
+ // export interface |DefinedInterface {
317
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
318
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 2, column: 18 });
319
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'DefinedInterface');
320
+
321
+ await closePeek(activeEditor);
322
+ });
323
+
324
+ it(`from ${from} to an editor preview`, async function () {
325
+ const editor = await openEditor(demoFileUri);
326
+ editor.getControl().revealLine(32);
327
+ // const bar: Defined|Interface = { coolField: [] };
328
+ editor.getControl().setPosition({ lineNumber: 32, column: 19 });
329
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'DefinedInterface');
330
+
331
+ await openPeek(editor);
332
+ await openReference();
333
+
334
+ const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
335
+ assert.isTrue(editorManager.activeEditor.isPreview);
336
+ assert.equal(activeEditor.uri.toString(), definitionFileUri.toString());
337
+ // export interface |DefinedInterface {
338
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
339
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 2, column: 18 });
340
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'DefinedInterface');
341
+
342
+ await closePeek(activeEditor);
343
+ });
344
+ }
345
+ });
346
+
347
+ it('editor.action.triggerSuggest', async function () {
348
+ const editor = await openEditor(demoFileUri);
349
+ // const demoVariable = demoInstance.[stringField];
350
+ editor.getControl().setPosition({ lineNumber: 26, column: 46 });
351
+ editor.getControl().setSelection(new Selection(26, 46, 26, 35));
352
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'stringField');
353
+
354
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
355
+ assert.isFalse(contextKeyService.match('suggestWidgetVisible'));
356
+
357
+ await commands.executeCommand('editor.action.triggerSuggest');
358
+ await waitForAnimation(() => contextKeyService.match('suggestWidgetVisible'));
359
+
360
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
361
+ assert.isTrue(contextKeyService.match('suggestWidgetVisible'));
362
+
363
+ keybindings.dispatchKeyDown('Enter');
364
+ await waitForAnimation(() => !contextKeyService.match('suggestWidgetVisible'));
365
+
366
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
367
+ assert.isFalse(contextKeyService.match('suggestWidgetVisible'));
368
+
369
+ const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
370
+ assert.equal(activeEditor.uri.toString(), demoFileUri.toString());
371
+ // demoInstance.stringField;
372
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
373
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 26, column: 46 });
374
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'doSomething');
375
+ });
376
+
377
+ it('editor.action.triggerSuggest navigate', async function () {
378
+ const editor = await openEditor(demoFileUri);
379
+ // demoInstance.[|stringField];
380
+ editor.getControl().setPosition({ lineNumber: 26, column: 46 });
381
+ editor.getControl().setSelection(new Selection(26, 46, 26, 35));
382
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'stringField');
383
+
384
+ /** @type {import('@theia/monaco-editor-core/src/vs/editor/contrib/suggest/browser/suggestController').SuggestController} */
385
+ const suggest = editor.getControl().getContribution('editor.contrib.suggestController');
386
+ const getFocusedLabel = () => {
387
+ const focusedItem = suggest.widget.value.getFocusedItem();
388
+ return focusedItem && focusedItem.item.completion.label;
389
+ };
390
+
391
+ assert.isUndefined(getFocusedLabel());
392
+ assert.isFalse(contextKeyService.match('suggestWidgetVisible'));
393
+
394
+ await commands.executeCommand('editor.action.triggerSuggest');
395
+ await waitForAnimation(() => contextKeyService.match('suggestWidgetVisible') && getFocusedLabel() === 'doSomething', 5000);
396
+
397
+ assert.equal(getFocusedLabel(), 'doSomething');
398
+ assert.isTrue(contextKeyService.match('suggestWidgetVisible'));
399
+
400
+ keybindings.dispatchKeyDown('ArrowDown');
401
+ await waitForAnimation(() => contextKeyService.match('suggestWidgetVisible') && getFocusedLabel() === 'numberField', 2000);
402
+
403
+ assert.equal(getFocusedLabel(), 'numberField');
404
+ assert.isTrue(contextKeyService.match('suggestWidgetVisible'));
405
+
406
+ keybindings.dispatchKeyDown('ArrowUp');
407
+ await waitForAnimation(() => contextKeyService.match('suggestWidgetVisible') && getFocusedLabel() === 'doSomething', 2000);
408
+
409
+ assert.equal(getFocusedLabel(), 'doSomething');
410
+ assert.isTrue(contextKeyService.match('suggestWidgetVisible'));
411
+
412
+ keybindings.dispatchKeyDown('Escape');
413
+ await waitForAnimation(() => !contextKeyService.match('suggestWidgetVisible') && getFocusedLabel() === undefined, 5000);
414
+
415
+ assert.isUndefined(getFocusedLabel());
416
+ assert.isFalse(contextKeyService.match('suggestWidgetVisible'));
417
+ });
418
+
419
+ it('editor.action.rename', async function () {
420
+ const editor = await openEditor(demoFileUri);
421
+ // const |demoVariable = demoInstance.stringField;
422
+ editor.getControl().setPosition({ lineNumber: 26, column: 7 });
423
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'demoVariable');
424
+
425
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
426
+ assert.isFalse(contextKeyService.match('renameInputVisible'));
427
+
428
+ commands.executeCommand('editor.action.rename');
429
+ await waitForAnimation(() => contextKeyService.match('renameInputVisible')
430
+ && document.activeElement instanceof HTMLInputElement
431
+ && document.activeElement.selectionEnd === 'demoVariable'.length);
432
+ assert.isFalse(contextKeyService.match('editorTextFocus'));
433
+ assert.isTrue(contextKeyService.match('renameInputVisible'));
434
+
435
+ const input = document.activeElement;
436
+ if (!(input instanceof HTMLInputElement)) {
437
+ assert.fail('expected focused input, but: ' + input);
438
+ return;
439
+ }
440
+
441
+ input.value = 'foo';
442
+ keybindings.dispatchKeyDown('Enter', input);
443
+
444
+ // all rename edits should be grouped in one edit operation and applied in the same tick
445
+ await new Promise(resolve => editor.getControl().onDidChangeModelContent(resolve));
446
+
447
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
448
+ assert.isFalse(contextKeyService.match('renameInputVisible'));
449
+
450
+ const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
451
+ assert.equal(activeEditor.uri.toString(), demoFileUri.toString());
452
+ // const |foo = new Container();
453
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
454
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 26, column: 7 });
455
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber: 28, column: 1 }).word, 'foo');
456
+ });
457
+
458
+ it('editor.action.triggerParameterHints', async function () {
459
+ const editor = await openEditor(demoFileUri);
460
+ // const demoInstance = new DemoClass('|demo');
461
+ editor.getControl().setPosition({ lineNumber: 24, column: 37 });
462
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, "demo");
463
+
464
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
465
+ assert.isFalse(contextKeyService.match('parameterHintsVisible'));
466
+
467
+ await commands.executeCommand('editor.action.triggerParameterHints');
468
+ await waitForAnimation(() => contextKeyService.match('parameterHintsVisible'));
469
+
470
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
471
+ assert.isTrue(contextKeyService.match('parameterHintsVisible'));
472
+
473
+ keybindings.dispatchKeyDown('Escape');
474
+ await waitForAnimation(() => !contextKeyService.match('parameterHintsVisible'));
475
+
476
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
477
+ assert.isFalse(contextKeyService.match('parameterHintsVisible'));
478
+ });
479
+
480
+ it('editor.action.showHover', async function () {
481
+ const editor = await openEditor(demoFileUri);
482
+ // class |DemoClass);
483
+ editor.getControl().setPosition({ lineNumber: 8, column: 7 });
484
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'DemoClass');
485
+
486
+ /** @type {import('@theia/monaco-editor-core/src/vs/editor/contrib/hover/browser/hover').ModesHoverController} */
487
+ const hover = editor.getControl().getContribution('editor.contrib.hover');
488
+
489
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
490
+ assert.isFalse(Boolean(hover['_contentWidget']?.['_widget']?.['_visibleData']));
491
+ await commands.executeCommand('editor.action.showHover');
492
+ let doLog = true;
493
+ await waitForAnimation(() => hover['_contentWidget']?.['_widget']?.['_visibleData']);
494
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
495
+ assert.isTrue(Boolean(hover['_contentWidget']?.['_widget']?.['_visibleData']));
496
+ assert.deepEqual(nodeAsString(hover['_contentWidget']?.['_widget']?.['_hover']?.['contentsDomNode']).trim(), `
497
+ DIV {
498
+ DIV {
499
+ DIV {
500
+ DIV {
501
+ DIV {
502
+ SPAN {
503
+ DIV {
504
+ SPAN {
505
+ "class"
506
+ }
507
+ SPAN {
508
+ " "
509
+ }
510
+ SPAN {
511
+ "DemoClass"
512
+ }
513
+ }
514
+ }
515
+ }
516
+ }
517
+ }
518
+ }
519
+ }`.trim());
520
+ keybindings.dispatchKeyDown('Escape');
521
+ await waitForAnimation(() => !hover['_contentWidget']?.['_widget']?.['_visibleData']);
522
+ assert.isTrue(contextKeyService.match('editorTextFocus'));
523
+ assert.isFalse(Boolean(hover['_contentWidget']?.['_widget']?.['_visibleData']));
524
+ });
525
+
526
+ it('highlight semantic (write) occurrences', async function () {
527
+ const editor = await openEditor(demoFileUri);
528
+ // const |container = new Container();
529
+ const lineNumber = 24;
530
+ const column = 7;
531
+ const endColumn = column + 'demoInstance'.length;
532
+
533
+ const hasWriteDecoration = () => {
534
+ for (const decoration of editor.getControl().getModel().getLineDecorations(lineNumber)) {
535
+ if (decoration.range.startColumn === column && decoration.range.endColumn === endColumn && decoration.options.className === 'wordHighlightStrong') {
536
+ return true;
537
+ }
538
+ }
539
+ return false;
540
+ };
541
+ assert.isFalse(hasWriteDecoration());
542
+
543
+ editor.getControl().setPosition({ lineNumber, column });
544
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'demoInstance');
545
+ // highlight occurrences is not trigged on the explicit position change, so move a cursor as a user
546
+ keybindings.dispatchKeyDown('ArrowRight');
547
+ await waitForAnimation(() => hasWriteDecoration());
548
+
549
+ assert.isTrue(hasWriteDecoration());
550
+ });
551
+
552
+ it('editor.action.goToImplementation', async function () {
553
+ const editor = await openEditor(demoFileUri);
554
+ // const demoInstance = new Demo|Class('demo');
555
+ editor.getControl().setPosition({ lineNumber: 24, column: 30 });
556
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'DemoClass');
557
+
558
+ await commands.executeCommand('editor.action.goToImplementation');
559
+
560
+ const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
561
+ assert.equal(activeEditor.uri.toString(), demoFileUri.toString());
562
+ // class |DemoClass implements DemoInterface {
563
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
564
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 8, column: 7 });
565
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'DemoClass');
566
+ });
567
+
568
+ it('editor.action.goToTypeDefinition', async function () {
569
+ const editor = await openEditor(demoFileUri);
570
+ // const demoVariable = demo|Instance.stringField;
571
+ editor.getControl().setPosition({ lineNumber: 26, column: 26 });
572
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'demoInstance');
573
+
574
+ await commands.executeCommand('editor.action.goToTypeDefinition');
575
+
576
+ const activeEditor = /** @type {MonacoEditor} */ MonacoEditor.get(editorManager.activeEditor);
577
+ assert.equal(activeEditor.uri.toString(), demoFileUri.toString());
578
+ // class |DemoClass implements DemoInterface {
579
+ const { lineNumber, column } = activeEditor.getControl().getPosition();
580
+ assert.deepEqual({ lineNumber, column }, { lineNumber: 8, column: 7 });
581
+ assert.equal(activeEditor.getControl().getModel().getWordAtPosition({ lineNumber, column }).word, 'DemoClass');
582
+ });
583
+
584
+ it('run reference code lens', async function () {
585
+ const preferenceName = 'typescript.referencesCodeLens.enabled';
586
+ const globalValue = preferences.inspect(preferenceName).globalValue;
587
+ toTearDown.push({ dispose: () => preferences.set(preferenceName, globalValue, PreferenceScope.User) });
588
+ await preferences.set(preferenceName, false, PreferenceScope.User);
589
+
590
+ const editor = await openEditor(demoFileUri);
591
+
592
+ /** @type {import('@theia/monaco-editor-core/src/vs/editor/contrib/codelens/browser/codelensController').CodeLensContribution} */
593
+ const codeLens = editor.getControl().getContribution('css.editor.codeLens');
594
+ const codeLensNode = () => codeLens['_lenses'][0]?.['_contentWidget']?.['_domNode'];
595
+ const codeLensNodeVisible = () => {
596
+ const n = codeLensNode();
597
+ return !!n && n.style.visibility !== 'hidden';
598
+ };
599
+
600
+ assert.isFalse(codeLensNodeVisible());
601
+
602
+ // |interface DemoInterface {
603
+ const position = { lineNumber: 2, column: 1 };
604
+ await preferences.set(preferenceName, true, PreferenceScope.User);
605
+
606
+ editor.getControl().revealPosition(position);
607
+ await waitForAnimation(() => codeLensNodeVisible());
608
+
609
+ assert.isTrue(codeLensNodeVisible());
610
+ const node = codeLensNode();
611
+ assert.isDefined(node);
612
+ assert.equal(nodeAsString(node), `
613
+ SPAN {
614
+ A {
615
+ "1 reference"
616
+ }
617
+ }
618
+ `);
619
+ const link = node.getElementsByTagName('a').item(0);
620
+ assert.isDefined(link);
621
+ link.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
622
+ await assertPeekOpened(editor);
623
+ await closePeek(editor);
624
+ });
625
+
626
+ it('editor.action.quickFix', async function () {
627
+ const column = 45;
628
+ const lineNumber = 26;
629
+ const editor = await openEditor(demoFileUri);
630
+ const currentChar = () => editor.getControl().getModel().getLineContent(lineNumber).charAt(column - 1);
631
+
632
+ // const demoVariable = demoInstance.stringField; --> const demoVariable = demoInstance.stringFiel;
633
+ editor.getControl().getModel().applyEdits([{
634
+ range: {
635
+ startLineNumber: lineNumber,
636
+ endLineNumber: lineNumber,
637
+ startColumn: 45,
638
+ endColumn: 46
639
+ },
640
+ forceMoveMarkers: false,
641
+ text: ''
642
+ }]);
643
+ editor.getControl().setPosition({ lineNumber, column });
644
+ editor.getControl().revealPosition({ lineNumber, column });
645
+ assert.equal(currentChar(), ';');
646
+
647
+ /** @type {import('@theia/monaco-editor-core/src/vs/editor/contrib/codeAction/browser/codeActionCommands').CodeActionController} */
648
+ const codeActionController = editor.getControl().getContribution('editor.contrib.codeActionController');
649
+ const lightBulbNode = () => {
650
+ const ui = codeActionController['_ui'].rawValue;
651
+ const lightBulb = ui && ui['_lightBulbWidget'].rawValue;
652
+ return lightBulb && lightBulb['_domNode'];
653
+ };
654
+ const lightBulbVisible = () => {
655
+ const node = lightBulbNode();
656
+ return !!node && node.style.visibility !== 'hidden';
657
+ };
658
+
659
+ assert.isFalse(lightBulbVisible());
660
+ await waitForAnimation(() => lightBulbVisible());
661
+
662
+ await commands.executeCommand('editor.action.quickFix');
663
+ const codeActionSelector = '.codeActionWidget';
664
+ assert.isFalse(!!document.querySelector(codeActionSelector), 'codeActionWidget should not be visible');
665
+
666
+ await waitForAnimation(() => !!document.querySelector(codeActionSelector), 5000);
667
+ await animationFrame();
668
+
669
+ keybindings.dispatchKeyDown('Enter');
670
+
671
+ await waitForAnimation(() => currentChar() === 'd', 5000);
672
+ assert.equal(currentChar(), 'd');
673
+
674
+ await waitForAnimation(() => !lightBulbVisible());
675
+ assert.isFalse(lightBulbVisible());
676
+ });
677
+
678
+ it('editor.action.formatDocument', async function () {
679
+ const lineNumber = 5;
680
+ const editor = await openEditor(demoFileUri);
681
+ const originalLength = editor.getControl().getModel().getLineLength(lineNumber);
682
+
683
+ // doSomething(): number; --> doSomething() : number;
684
+ editor.getControl().getModel().applyEdits([{
685
+ range: Range.fromPositions({ lineNumber, column: 18 }, { lineNumber, column: 18 }),
686
+ forceMoveMarkers: false,
687
+ text: ' '
688
+ }]);
689
+
690
+ assert.equal(editor.getControl().getModel().getLineLength(lineNumber), originalLength + 1);
691
+
692
+ await commands.executeCommand('editor.action.formatDocument');
693
+
694
+ assert.equal(editor.getControl().getModel().getLineLength(lineNumber), originalLength);
695
+ });
696
+
697
+ it('editor.action.formatSelection', async function () {
698
+ // doSomething(): number {
699
+ const lineNumber = 15;
700
+ const editor = await openEditor(demoFileUri);
701
+ const originalLength /* 28 */ = editor.getControl().getModel().getLineLength(lineNumber);
702
+
703
+ // doSomething( ) : number {
704
+ editor.getControl().getModel().applyEdits([{
705
+ range: Range.fromPositions({ lineNumber, column: 17 }, { lineNumber, column: 18 }),
706
+ forceMoveMarkers: false,
707
+ text: ' ) '
708
+ }]);
709
+
710
+ assert.equal(editor.getControl().getModel().getLineLength(lineNumber), originalLength + 4);
711
+
712
+ // [const { Container }] = require('inversify');
713
+ editor.getControl().setSelection({ startLineNumber: lineNumber, startColumn: 1, endLineNumber: lineNumber, endColumn: 32 });
714
+
715
+ await commands.executeCommand('editor.action.formatSelection');
716
+
717
+ // [const { Container }] = require('inversify');
718
+ assert.equal(editor.getControl().getModel().getLineLength(lineNumber), originalLength);
719
+ });
720
+
721
+ for (const referenceViewCommand of ['references-view.find', 'references-view.findImplementations']) {
722
+ it(referenceViewCommand, async function () {
723
+ let steps = 0;
724
+ const editor = await openEditor(demoFileUri);
725
+ // const demo|Instance = new DemoClass('demo');
726
+ editor.getControl().setPosition({ lineNumber: 24, column: 11 });
727
+ assert.equal(editor.getControl().getModel().getWordAtPosition(editor.getControl().getPosition()).word, 'demoInstance');
728
+ const view = await pluginViewRegistry.openView('references-view.tree', { reveal: true });
729
+ assert.isDefined(view);
730
+ assert.isTrue(view.isVisible);
731
+ await commands.executeCommand('references-view.clear');
732
+ const expectedMessage = referenceViewCommand === 'references-view.find' ? '2 results in 1 file' : '1 result in 1 file';
733
+ const getResultText = () => view.node.getElementsByClassName('theia-TreeViewInfo').item(0)?.textContent;
734
+ await commands.executeCommand(referenceViewCommand);
735
+ await waitForAnimation(() => getResultText() === expectedMessage, 5000);
736
+ assert.equal(getResultText(), expectedMessage);
737
+ });
738
+ }
739
+
740
+ it('Can execute code actions', async function () {
741
+ const editor = await openEditor(demoFileUri);
742
+ /** @type {import('@theia/monaco-editor-core/src/vs/editor/contrib/codeAction/browser/codeActionCommands').CodeActionController} */
743
+ const codeActionController = editor.getControl().getContribution('editor.contrib.codeActionController');
744
+ const isActionAvailable = () => {
745
+ const lightbulbVisibility = codeActionController['_ui'].rawValue?.['_lightBulbWidget'].rawValue?.['_domNode'].style.visibility;
746
+ return lightbulbVisibility !== undefined && lightbulbVisibility !== 'hidden';
747
+ }
748
+ assert.isFalse(isActionAvailable());
749
+ // import { DefinedInterface } from "./demo-definitions-file";
750
+ assert.strictEqual(editor.getControl().getModel().getLineContent(30), 'import { DefinedInterface } from "./demo-definitions-file";');
751
+ editor.getControl().revealLine(30);
752
+ editor.getControl().setSelection(new Selection(30, 1, 30, 60));
753
+ await waitForAnimation(() => isActionAvailable(), 5000, 'No code action available. (1)');
754
+ assert.isTrue(isActionAvailable());
755
+
756
+ await commands.executeCommand('editor.action.quickFix');
757
+ await waitForAnimation(() => Boolean(document.querySelector('.context-view-pointerBlock')), 5000, 'No context menu appeared. (1)');
758
+ await animationFrame();
759
+
760
+ keybindings.dispatchKeyDown('Enter');
761
+
762
+ assert.isNotNull(editor.getControl());
763
+ assert.isNotNull(editor.getControl().getModel());
764
+ console.log(`content: ${editor.getControl().getModel().getLineContent(30)}`);
765
+ await waitForAnimation(() => editor.getControl().getModel().getLineContent(30) === 'import * as demoDefinitionsFile from "./demo-definitions-file";', 5000, 'The namespace import did not take effect.');
766
+
767
+ editor.getControl().setSelection(new Selection(30, 1, 30, 64));
768
+ await waitForAnimation(() => isActionAvailable(), 5000, 'No code action available. (2)');
769
+
770
+ // Change it back: https://github.com/eclipse-theia/theia/issues/11059
771
+ await commands.executeCommand('editor.action.quickFix');
772
+ await waitForAnimation(() => Boolean(document.querySelector('.context-view-pointerBlock')), 5000, 'No context menu appeared. (2)');
773
+ await animationFrame();
774
+
775
+ keybindings.dispatchKeyDown('Enter');
776
+
777
+ await waitForAnimation(() => editor.getControl().getModel().getLineContent(30) === 'import { DefinedInterface } from "./demo-definitions-file";', 5000, 'The named import did not take effect.');
778
+ });
779
+ });