@theia/api-tests 1.77.0-next.28 → 1.77.0-next.37
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.
- package/package.json +3 -3
- package/src/output-focus.spec.js +182 -0
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theia/api-tests",
|
|
3
|
-
"version": "1.77.0-next.
|
|
3
|
+
"version": "1.77.0-next.37+29045502f",
|
|
4
4
|
"description": "Theia API tests",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@theia/core": "1.77.0-next.
|
|
6
|
+
"@theia/core": "1.77.0-next.37+29045502f"
|
|
7
7
|
},
|
|
8
8
|
"license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0",
|
|
9
9
|
"repository": {
|
|
@@ -20,5 +20,5 @@
|
|
|
20
20
|
"publishConfig": {
|
|
21
21
|
"access": "public"
|
|
22
22
|
},
|
|
23
|
-
"gitHead": "
|
|
23
|
+
"gitHead": "29045502ffe2bd1ecd5c655b40f37f0d0bc0c4ad"
|
|
24
24
|
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource 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-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
// @ts-check
|
|
18
|
+
describe('Output and editor focus', function () {
|
|
19
|
+
this.timeout(15000);
|
|
20
|
+
|
|
21
|
+
const { assert } = chai;
|
|
22
|
+
|
|
23
|
+
const { timeout } = require('@theia/core/lib/common/promise-util');
|
|
24
|
+
const { ApplicationShell } = require('@theia/core/lib/browser/shell/application-shell');
|
|
25
|
+
const { EditorManager } = require('@theia/editor/lib/browser/editor-manager');
|
|
26
|
+
const { WorkspaceService } = require('@theia/workspace/lib/browser/workspace-service');
|
|
27
|
+
const { MonacoEditor } = require('@theia/monaco/lib/browser/monaco-editor');
|
|
28
|
+
const { OutputChannelManager } = require('@theia/output/lib/browser/output-channel');
|
|
29
|
+
const { OutputContribution } = require('@theia/output/lib/browser/output-contribution');
|
|
30
|
+
|
|
31
|
+
const container = window.theia.container;
|
|
32
|
+
const shell = container.get(ApplicationShell);
|
|
33
|
+
const editorManager = container.get(EditorManager);
|
|
34
|
+
const workspaceService = container.get(WorkspaceService);
|
|
35
|
+
const outputChannelManager = container.get(OutputChannelManager);
|
|
36
|
+
const outputContribution = container.get(OutputContribution);
|
|
37
|
+
|
|
38
|
+
const rootUri = workspaceService.tryGetRoots()[0].resource;
|
|
39
|
+
const firstFileUri = rootUri.resolve('package.json');
|
|
40
|
+
const secondFileUri = rootUri.resolve('esbuild.mjs');
|
|
41
|
+
|
|
42
|
+
const firstChannelName = 'Output Focus Test 1';
|
|
43
|
+
const secondChannelName = 'Output Focus Test 2';
|
|
44
|
+
|
|
45
|
+
/** @type {import('@theia/editor/lib/browser/editor-widget').EditorWidget} */
|
|
46
|
+
let focusedEditor;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {() => unknown} condition
|
|
50
|
+
* @param {string} message
|
|
51
|
+
* @returns {Promise<void>}
|
|
52
|
+
*/
|
|
53
|
+
async function waitFor(condition, message) {
|
|
54
|
+
const endTime = Date.now() + 5000;
|
|
55
|
+
while (!condition()) {
|
|
56
|
+
if (Date.now() > endTime) {
|
|
57
|
+
throw new Error(`Timed out waiting for: ${message}`);
|
|
58
|
+
}
|
|
59
|
+
await timeout(50);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {import('@theia/core/lib/browser').Widget | undefined} widget
|
|
65
|
+
* @returns {boolean}
|
|
66
|
+
*/
|
|
67
|
+
function hasFocus(widget) {
|
|
68
|
+
return !!widget && widget.node.contains(document.activeElement);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Resolves once the Output view is visible and displays the given channel. The widget activates itself, if
|
|
73
|
+
* it does, synchronously after it has set the channel's model, so focus can be checked right after this.
|
|
74
|
+
* @param {string} channelName
|
|
75
|
+
*/
|
|
76
|
+
async function waitForOutputToShow(channelName) {
|
|
77
|
+
const channelUri = outputChannelManager.getChannel(channelName).uri.toString();
|
|
78
|
+
await waitFor(() => {
|
|
79
|
+
const outputWidget = outputContribution.tryGetWidget();
|
|
80
|
+
const model = outputWidget && outputWidget['editor']?.getControl().getModel();
|
|
81
|
+
return outputWidget?.isVisible && model?.uri.toString() === channelUri;
|
|
82
|
+
}, `the Output view to show '${channelName}'`);
|
|
83
|
+
await timeout(100);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function assertEditorKeepsFocus() {
|
|
87
|
+
assert.isTrue(hasFocus(focusedEditor), 'the editor should keep the focus');
|
|
88
|
+
assert.isTrue(shell.activeWidget === focusedEditor, 'the editor should stay the active widget');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
beforeEach(async () => {
|
|
92
|
+
await editorManager.closeAll({ save: false });
|
|
93
|
+
await outputContribution.closeView();
|
|
94
|
+
focusedEditor = await editorManager.open(firstFileUri, { mode: 'activate' });
|
|
95
|
+
await waitFor(() => hasFocus(focusedEditor), 'the editor to take the focus');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
afterEach(async () => {
|
|
99
|
+
outputChannelManager.deleteChannel(firstChannelName);
|
|
100
|
+
outputChannelManager.deleteChannel(secondChannelName);
|
|
101
|
+
await outputContribution.closeView();
|
|
102
|
+
await editorManager.closeAll({ save: false });
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('OutputChannel.show', () => {
|
|
106
|
+
|
|
107
|
+
it('should reveal the Output view without taking the focus if preserveFocus is true', async () => {
|
|
108
|
+
const channel = outputChannelManager.getChannel(firstChannelName);
|
|
109
|
+
channel.appendLine('hello');
|
|
110
|
+
channel.show({ preserveFocus: true });
|
|
111
|
+
await waitForOutputToShow(firstChannelName);
|
|
112
|
+
assertEditorKeepsFocus();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should activate the Output view if preserveFocus is not set', async () => {
|
|
116
|
+
const channel = outputChannelManager.getChannel(firstChannelName);
|
|
117
|
+
channel.appendLine('hello');
|
|
118
|
+
channel.show();
|
|
119
|
+
await waitForOutputToShow(firstChannelName);
|
|
120
|
+
const outputWidget = outputContribution.tryGetWidget();
|
|
121
|
+
await waitFor(() => hasFocus(outputWidget), 'the Output view to take the focus');
|
|
122
|
+
assert.isTrue(shell.activeWidget === outputWidget, 'the Output view should be the active widget');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('channel changes in a visible Output view', () => {
|
|
128
|
+
|
|
129
|
+
beforeEach(async () => {
|
|
130
|
+
outputChannelManager.getChannel(firstChannelName).show({ preserveFocus: true });
|
|
131
|
+
await waitForOutputToShow(firstChannelName);
|
|
132
|
+
assertEditorKeepsFocus();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should not take the focus when a channel is selected', async () => {
|
|
136
|
+
const secondChannel = outputChannelManager.getChannel(secondChannelName);
|
|
137
|
+
outputChannelManager.selectedChannel = secondChannel;
|
|
138
|
+
await waitForOutputToShow(secondChannelName);
|
|
139
|
+
assertEditorKeepsFocus();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should not take the focus when a channel is added', async () => {
|
|
143
|
+
outputChannelManager.getChannel(secondChannelName);
|
|
144
|
+
await timeout(200);
|
|
145
|
+
assertEditorKeepsFocus();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should not take the focus when the selected channel is deleted', async () => {
|
|
149
|
+
const secondChannel = outputChannelManager.getChannel(secondChannelName);
|
|
150
|
+
outputChannelManager.selectedChannel = secondChannel;
|
|
151
|
+
await waitForOutputToShow(secondChannelName);
|
|
152
|
+
outputChannelManager.deleteChannel(secondChannelName);
|
|
153
|
+
await waitFor(() => outputChannelManager.selectedChannel?.name !== secondChannelName, 'another channel to be selected');
|
|
154
|
+
await timeout(200);
|
|
155
|
+
assertEditorKeepsFocus();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('MonacoEditor', () => {
|
|
161
|
+
|
|
162
|
+
it('should not take the focus when it is revealed', async () => {
|
|
163
|
+
const revealedEditor = await editorManager.open(secondFileUri, { mode: 'reveal', widgetOptions: { mode: 'split-right' } });
|
|
164
|
+
await waitFor(() => revealedEditor.isVisible, 'the revealed editor to become visible');
|
|
165
|
+
await timeout(100);
|
|
166
|
+
assert.isFalse(MonacoEditor.get(revealedEditor)?.isFocused(), 'the revealed editor should not be focused');
|
|
167
|
+
assertEditorKeepsFocus();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('should take the focus when it is activated', async () => {
|
|
171
|
+
const otherEditor = await editorManager.open(secondFileUri, { mode: 'activate' });
|
|
172
|
+
await waitFor(() => hasFocus(otherEditor), 'the second editor to take the focus');
|
|
173
|
+
assert.isFalse(focusedEditor.isVisible, 'the first editor should be hidden behind the second one');
|
|
174
|
+
|
|
175
|
+
await editorManager.open(firstFileUri, { mode: 'activate' });
|
|
176
|
+
await waitFor(() => hasFocus(focusedEditor), 'the first editor to take the focus back');
|
|
177
|
+
assert.isTrue(MonacoEditor.get(focusedEditor)?.isFocused(), 'the first editor should have text focus');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
});
|