@theia/playwright 1.37.0-next.8 → 1.37.0
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/lib/index.d.ts +4 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -0
- package/lib/index.js.map +1 -1
- package/lib/tests/theia-explorer-view.test.js +24 -5
- package/lib/tests/theia-explorer-view.test.js.map +1 -1
- package/lib/tests/theia-output-view.test.d.ts +2 -0
- package/lib/tests/theia-output-view.test.d.ts.map +1 -0
- package/lib/tests/theia-output-view.test.js +77 -0
- package/lib/tests/theia-output-view.test.js.map +1 -0
- package/lib/tests/theia-quick-command.test.js +14 -0
- package/lib/tests/theia-quick-command.test.js.map +1 -1
- package/lib/tests/theia-sample-app.test.js +3 -30
- package/lib/tests/theia-sample-app.test.js.map +1 -1
- package/lib/tests/theia-terminal-view.test.d.ts +2 -0
- package/lib/tests/theia-terminal-view.test.d.ts.map +1 -0
- package/lib/tests/theia-terminal-view.test.js +76 -0
- package/lib/tests/theia-terminal-view.test.js.map +1 -0
- package/lib/tests/theia-toolbar.test.d.ts +2 -0
- package/lib/tests/theia-toolbar.test.d.ts.map +1 -0
- package/lib/tests/theia-toolbar.test.js +60 -0
- package/lib/tests/theia-toolbar.test.js.map +1 -0
- package/lib/tests/theia-workspace.test.js +5 -3
- package/lib/tests/theia-workspace.test.js.map +1 -1
- package/lib/theia-app.d.ts +7 -0
- package/lib/theia-app.d.ts.map +1 -1
- package/lib/theia-app.js +30 -1
- package/lib/theia-app.js.map +1 -1
- package/lib/theia-explorer-view.d.ts +6 -4
- package/lib/theia-explorer-view.d.ts.map +1 -1
- package/lib/theia-explorer-view.js +38 -6
- package/lib/theia-explorer-view.js.map +1 -1
- package/lib/theia-monaco-editor.d.ts +16 -0
- package/lib/theia-monaco-editor.d.ts.map +1 -0
- package/lib/theia-monaco-editor.js +76 -0
- package/lib/theia-monaco-editor.js.map +1 -0
- package/lib/theia-output-channel.d.ts +25 -0
- package/lib/theia-output-channel.d.ts.map +1 -0
- package/lib/theia-output-channel.js +72 -0
- package/lib/theia-output-channel.js.map +1 -0
- package/lib/theia-output-view.d.ts +10 -0
- package/lib/theia-output-view.d.ts.map +1 -0
- package/lib/theia-output-view.js +82 -0
- package/lib/theia-output-view.js.map +1 -0
- package/lib/theia-quick-command-palette.d.ts +2 -1
- package/lib/theia-quick-command-palette.d.ts.map +1 -1
- package/lib/theia-quick-command-palette.js +9 -2
- package/lib/theia-quick-command-palette.js.map +1 -1
- package/lib/theia-terminal.d.ts +14 -0
- package/lib/theia-terminal.d.ts.map +1 -0
- package/lib/theia-terminal.js +60 -0
- package/lib/theia-terminal.js.map +1 -0
- package/lib/theia-text-editor.d.ts +2 -3
- package/lib/theia-text-editor.d.ts.map +1 -1
- package/lib/theia-text-editor.js +11 -40
- package/lib/theia-text-editor.js.map +1 -1
- package/lib/theia-toolbar-item.d.ts +11 -0
- package/lib/theia-toolbar-item.d.ts.map +1 -0
- package/lib/theia-toolbar-item.js +40 -0
- package/lib/theia-toolbar-item.js.map +1 -0
- package/lib/theia-toolbar.d.ts +20 -0
- package/lib/theia-toolbar.d.ts.map +1 -0
- package/lib/theia-toolbar.js +91 -0
- package/lib/theia-toolbar.js.map +1 -0
- package/lib/theia-tree-node.d.ts +1 -0
- package/lib/theia-tree-node.d.ts.map +1 -1
- package/lib/theia-tree-node.js +8 -0
- package/lib/theia-tree-node.js.map +1 -1
- package/package.json +13 -10
- package/src/index.ts +4 -0
- package/src/tests/resources/sample-files1/sampleFolderCompact/nestedFolder1/nestedFolder2/sampleFile1-1.txt +0 -0
- package/src/tests/theia-explorer-view.test.ts +25 -6
- package/src/tests/theia-output-view.test.ts +85 -0
- package/src/tests/theia-quick-command.test.ts +15 -0
- package/src/tests/theia-sample-app.test.ts +3 -34
- package/src/tests/theia-terminal-view.test.ts +85 -0
- package/src/tests/theia-toolbar.test.ts +65 -0
- package/src/tests/theia-workspace.test.ts +5 -3
- package/src/theia-app.ts +37 -1
- package/src/theia-explorer-view.ts +42 -7
- package/src/theia-monaco-editor.ts +83 -0
- package/src/theia-output-channel.ts +88 -0
- package/src/theia-output-view.ts +87 -0
- package/src/theia-quick-command-palette.ts +10 -2
- package/src/theia-terminal.ts +69 -0
- package/src/theia-text-editor.ts +13 -44
- package/src/theia-toolbar-item.ts +41 -0
- package/src/theia-toolbar.ts +99 -0
- package/src/theia-tree-node.ts +10 -1
- package/LICENSE +0 -642
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 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 WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { ElementHandle } from '@playwright/test';
|
|
18
|
+
import { TheiaOutputView } from './theia-output-view';
|
|
19
|
+
import { TheiaPageObject } from './theia-page-object';
|
|
20
|
+
import { isElementVisible } from './util';
|
|
21
|
+
import { TheiaMonacoEditor } from './theia-monaco-editor';
|
|
22
|
+
|
|
23
|
+
export interface TheiaOutputViewChannelData {
|
|
24
|
+
viewSelector: string;
|
|
25
|
+
dataUri: string;
|
|
26
|
+
channelName: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class TheiaOutputViewChannel extends TheiaPageObject {
|
|
30
|
+
|
|
31
|
+
protected monacoEditor: TheiaMonacoEditor;
|
|
32
|
+
|
|
33
|
+
constructor(protected readonly data: TheiaOutputViewChannelData, protected readonly outputView: TheiaOutputView) {
|
|
34
|
+
super(outputView.app);
|
|
35
|
+
this.monacoEditor = new TheiaMonacoEditor(this.viewSelector, outputView.app);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
protected get viewSelector(): string {
|
|
39
|
+
return this.data.viewSelector;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
protected get dataUri(): string | undefined {
|
|
43
|
+
return this.data.dataUri;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
protected get channelName(): string | undefined {
|
|
47
|
+
return this.data.channelName;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async waitForVisible(): Promise<void> {
|
|
51
|
+
await this.page.waitForSelector(this.viewSelector, { state: 'visible' });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async isDisplayed(): Promise<boolean> {
|
|
55
|
+
return isElementVisible(this.viewElement());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
protected viewElement(): Promise<ElementHandle<SVGElement | HTMLElement> | null> {
|
|
59
|
+
return this.page.$(this.viewSelector);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async numberOfLines(): Promise<number | undefined> {
|
|
63
|
+
await this.waitForVisible();
|
|
64
|
+
return this.monacoEditor.numberOfLines();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async maxSeverityOfLineByLineNumber(lineNumber: number): Promise<'error' | 'warning' | 'info'> {
|
|
68
|
+
await this.waitForVisible();
|
|
69
|
+
const lineElement = await this.monacoEditor.lineByLineNumber(lineNumber);
|
|
70
|
+
const contents = await lineElement?.$$('span > span.mtk1');
|
|
71
|
+
if (!contents || contents.length < 1) {
|
|
72
|
+
throw new Error(`Could not find contents of line number ${lineNumber}!`);
|
|
73
|
+
}
|
|
74
|
+
const severityClassNames = await Promise.all(contents.map(
|
|
75
|
+
async content => (await content.getAttribute('class'))?.split(' ')[1]));
|
|
76
|
+
|
|
77
|
+
if (severityClassNames.includes('theia-output-error')) {
|
|
78
|
+
return 'error';
|
|
79
|
+
} else if (severityClassNames.includes('theia-output-warning')) {
|
|
80
|
+
return 'warning';
|
|
81
|
+
}
|
|
82
|
+
return 'info';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async textContentOfLineByLineNumber(lineNumber: number): Promise<string | undefined> {
|
|
86
|
+
return this.monacoEditor.textContentOfLineByLineNumber(lineNumber);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 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 WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { TheiaApp } from './theia-app';
|
|
18
|
+
import { TheiaOutputViewChannel } from './theia-output-channel';
|
|
19
|
+
import { TheiaView } from './theia-view';
|
|
20
|
+
import { normalizeId } from './util';
|
|
21
|
+
|
|
22
|
+
const TheiaOutputViewData = {
|
|
23
|
+
tabSelector: '#shell-tab-outputView',
|
|
24
|
+
viewSelector: '#outputView',
|
|
25
|
+
viewName: 'Output'
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export class TheiaOutputView extends TheiaView {
|
|
29
|
+
constructor(app: TheiaApp) {
|
|
30
|
+
super(TheiaOutputViewData, app);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async isOutputChannelSelected(outputChannelName: string): Promise<boolean> {
|
|
34
|
+
await this.activate();
|
|
35
|
+
const contentPanel = await this.page.$('#theia-bottom-content-panel');
|
|
36
|
+
if (contentPanel && (await contentPanel.isVisible())) {
|
|
37
|
+
const channelList = await contentPanel.$('#outputChannelList');
|
|
38
|
+
const selectedChannel = await channelList?.$('div.theia-select-component-label');
|
|
39
|
+
if (selectedChannel && (await selectedChannel.textContent()) === outputChannelName) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async getOutputChannel(outputChannelName: string): Promise<TheiaOutputViewChannel | undefined> {
|
|
47
|
+
await this.activate();
|
|
48
|
+
const channel = new TheiaOutputViewChannel(
|
|
49
|
+
{
|
|
50
|
+
viewSelector: 'div.p-Widget.theia-editor.p-DockPanel-widget > div.monaco-editor',
|
|
51
|
+
dataUri: normalizeId(`output:/${encodeURIComponent(outputChannelName)}`),
|
|
52
|
+
channelName: outputChannelName
|
|
53
|
+
},
|
|
54
|
+
this
|
|
55
|
+
);
|
|
56
|
+
await channel.waitForVisible();
|
|
57
|
+
if (await channel.isDisplayed()) {
|
|
58
|
+
return channel;
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async selectOutputChannel(outputChannelName: string): Promise<boolean> {
|
|
64
|
+
await this.activate();
|
|
65
|
+
const contentPanel = await this.page.$('#theia-bottom-content-panel');
|
|
66
|
+
if (contentPanel && (await contentPanel.isVisible())) {
|
|
67
|
+
const channelSelectComponent = await contentPanel.$('#outputChannelList');
|
|
68
|
+
if (!channelSelectComponent) {
|
|
69
|
+
throw Error('Output Channel List not visible.');
|
|
70
|
+
}
|
|
71
|
+
// open output channel list component
|
|
72
|
+
await channelSelectComponent.click();
|
|
73
|
+
const channelContainer = await this.page.waitForSelector('#select-component-container > div.theia-select-component-dropdown');
|
|
74
|
+
if (!channelContainer) {
|
|
75
|
+
throw Error('Output Channel List could not be opened.');
|
|
76
|
+
}
|
|
77
|
+
const channels = await channelContainer.$$('div.theia-select-component-option-value');
|
|
78
|
+
for (const channel of channels) {
|
|
79
|
+
if (await channel.textContent() === outputChannelName) {
|
|
80
|
+
await channel.click();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return this.isOutputChannelSelected(outputChannelName);
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -27,6 +27,11 @@ export class TheiaQuickCommandPalette extends TheiaPageObject {
|
|
|
27
27
|
await this.page.waitForSelector(this.selector);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
async hide(): Promise<void> {
|
|
31
|
+
await this.page.keyboard.press('Escape');
|
|
32
|
+
await this.page.waitForSelector(this.selector, { state: 'hidden' });
|
|
33
|
+
}
|
|
34
|
+
|
|
30
35
|
async isOpen(): Promise<boolean> {
|
|
31
36
|
try {
|
|
32
37
|
await this.page.waitForSelector(this.selector, { timeout: 5000 });
|
|
@@ -54,14 +59,17 @@ export class TheiaQuickCommandPalette extends TheiaPageObject {
|
|
|
54
59
|
await this.page.keyboard.press('Enter');
|
|
55
60
|
}
|
|
56
61
|
|
|
57
|
-
async type(
|
|
62
|
+
async type(value: string, confirm = false): Promise<void> {
|
|
58
63
|
if (!await this.isOpen()) {
|
|
59
64
|
this.open();
|
|
60
65
|
}
|
|
61
66
|
const input = await this.page.waitForSelector(`${this.selector} .monaco-inputbox .input`);
|
|
62
67
|
if (input != null) {
|
|
63
68
|
await input.focus();
|
|
64
|
-
await input.type(
|
|
69
|
+
await input.type(value, { delay: USER_KEY_TYPING_DELAY });
|
|
70
|
+
if (confirm) {
|
|
71
|
+
await this.page.keyboard.press('Enter');
|
|
72
|
+
}
|
|
65
73
|
}
|
|
66
74
|
}
|
|
67
75
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 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 WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { ElementHandle } from '@playwright/test';
|
|
18
|
+
import { TheiaApp } from './theia-app';
|
|
19
|
+
import { TheiaContextMenu } from './theia-context-menu';
|
|
20
|
+
import { TheiaMenu } from './theia-menu';
|
|
21
|
+
import { TheiaView } from './theia-view';
|
|
22
|
+
|
|
23
|
+
export class TheiaTerminal extends TheiaView {
|
|
24
|
+
|
|
25
|
+
constructor(tabId: string, app: TheiaApp) {
|
|
26
|
+
super({
|
|
27
|
+
tabSelector: `#shell-tab-terminal-${getTerminalId(tabId)}`,
|
|
28
|
+
viewSelector: `#terminal-${getTerminalId(tabId)}`
|
|
29
|
+
}, app);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async submit(text: string): Promise<void> {
|
|
33
|
+
await this.write(text);
|
|
34
|
+
const input = await this.waitForInputArea();
|
|
35
|
+
await input.press('Enter');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async write(text: string): Promise<void> {
|
|
39
|
+
await this.activate();
|
|
40
|
+
const input = await this.waitForInputArea();
|
|
41
|
+
await input.type(text);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async contents(): Promise<string> {
|
|
45
|
+
await this.activate();
|
|
46
|
+
await (await this.openContextMenu()).clickMenuItem('Select All');
|
|
47
|
+
await (await this.openContextMenu()).clickMenuItem('Copy');
|
|
48
|
+
return this.page.evaluate('navigator.clipboard.readText()');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
protected async openContextMenu(): Promise<TheiaMenu> {
|
|
52
|
+
await this.activate();
|
|
53
|
+
return TheiaContextMenu.open(this.app, () => this.waitForVisibleView());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
protected async waitForInputArea(): Promise<ElementHandle<SVGElement | HTMLElement>> {
|
|
57
|
+
const view = await this.waitForVisibleView();
|
|
58
|
+
return view.waitForSelector('.xterm-helper-textarea');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
protected async waitForVisibleView(): Promise<ElementHandle<SVGElement | HTMLElement>> {
|
|
62
|
+
return this.page.waitForSelector(this.viewSelector, { state: 'visible' });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function getTerminalId(tabId: string): string {
|
|
68
|
+
return tabId.substring(tabId.lastIndexOf('-') + 1);
|
|
69
|
+
}
|
package/src/theia-text-editor.ts
CHANGED
|
@@ -20,9 +20,12 @@ import { join } from 'path';
|
|
|
20
20
|
import { TheiaApp } from './theia-app';
|
|
21
21
|
import { TheiaEditor } from './theia-editor';
|
|
22
22
|
import { normalizeId, OSUtil, urlEncodePath } from './util';
|
|
23
|
+
import { TheiaMonacoEditor } from './theia-monaco-editor';
|
|
23
24
|
|
|
24
25
|
export class TheiaTextEditor extends TheiaEditor {
|
|
25
26
|
|
|
27
|
+
protected monacoEditor: TheiaMonacoEditor;
|
|
28
|
+
|
|
26
29
|
constructor(filePath: string, app: TheiaApp) {
|
|
27
30
|
// shell-tab-code-editor-opener:file:///c%3A/Users/user/AppData/Local/Temp/cloud-ws-JBUhb6/sample.txt:1
|
|
28
31
|
// code-editor-opener:file:///c%3A/Users/user/AppData/Local/Temp/cloud-ws-JBUhb6/sample.txt:1
|
|
@@ -30,19 +33,16 @@ export class TheiaTextEditor extends TheiaEditor {
|
|
|
30
33
|
tabSelector: normalizeId(`#shell-tab-code-editor-opener:file://${urlEncodePath(join(app.workspace.escapedPath, OSUtil.fileSeparator, filePath))}:1`),
|
|
31
34
|
viewSelector: normalizeId(`#code-editor-opener:file://${urlEncodePath(join(app.workspace.escapedPath, OSUtil.fileSeparator, filePath))}:1`) + '.theia-editor'
|
|
32
35
|
}, app);
|
|
36
|
+
this.monacoEditor = new TheiaMonacoEditor(this.viewSelector, app);
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
async numberOfLines(): Promise<number | undefined> {
|
|
36
40
|
await this.activate();
|
|
37
|
-
|
|
38
|
-
const lineElements = await viewElement?.$$('.view-lines .view-line');
|
|
39
|
-
return lineElements?.length;
|
|
41
|
+
return this.monacoEditor.numberOfLines();
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
async textContentOfLineByLineNumber(lineNumber: number): Promise<string | undefined> {
|
|
43
|
-
|
|
44
|
-
const content = await lineElement?.textContent();
|
|
45
|
-
return content ? this.replaceEditorSymbolsWithSpace(content) : undefined;
|
|
45
|
+
return this.monacoEditor.textContentOfLineByLineNumber(lineNumber);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
async replaceLineWithLineNumber(text: string, lineNumber: number): Promise<void> {
|
|
@@ -57,14 +57,14 @@ export class TheiaTextEditor extends TheiaEditor {
|
|
|
57
57
|
|
|
58
58
|
async selectLineWithLineNumber(lineNumber: number): Promise<ElementHandle<SVGElement | HTMLElement> | undefined> {
|
|
59
59
|
await this.activate();
|
|
60
|
-
const lineElement = await this.lineByLineNumber(lineNumber);
|
|
60
|
+
const lineElement = await this.monacoEditor.lineByLineNumber(lineNumber);
|
|
61
61
|
await this.selectLine(lineElement);
|
|
62
62
|
return lineElement;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
async placeCursorInLineWithLineNumber(lineNumber: number): Promise<ElementHandle<SVGElement | HTMLElement> | undefined> {
|
|
66
66
|
await this.activate();
|
|
67
|
-
const lineElement = await this.lineByLineNumber(lineNumber);
|
|
67
|
+
const lineElement = await this.monacoEditor.lineByLineNumber(lineNumber);
|
|
68
68
|
await this.placeCursorInLine(lineElement);
|
|
69
69
|
return lineElement;
|
|
70
70
|
}
|
|
@@ -74,28 +74,9 @@ export class TheiaTextEditor extends TheiaEditor {
|
|
|
74
74
|
await this.page.keyboard.press('Backspace');
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
protected async lineByLineNumber(lineNumber: number): Promise<ElementHandle<SVGElement | HTMLElement> | undefined> {
|
|
78
|
-
await this.activate();
|
|
79
|
-
const viewElement = await this.viewElement();
|
|
80
|
-
const lines = await viewElement?.$$('.view-lines .view-line');
|
|
81
|
-
if (!lines) {
|
|
82
|
-
throw new Error(`Couldn't retrieve lines of text editor ${this.tabSelector}`);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const linesWithXCoordinates = [];
|
|
86
|
-
for (const lineElement of lines) {
|
|
87
|
-
const box = await lineElement.boundingBox();
|
|
88
|
-
linesWithXCoordinates.push({ x: box ? box.x : Number.MAX_VALUE, lineElement });
|
|
89
|
-
}
|
|
90
|
-
linesWithXCoordinates.sort((a, b) => a.x.toString().localeCompare(b.x.toString()));
|
|
91
|
-
return linesWithXCoordinates[lineNumber - 1].lineElement;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
77
|
async textContentOfLineContainingText(text: string): Promise<string | undefined> {
|
|
95
78
|
await this.activate();
|
|
96
|
-
|
|
97
|
-
const content = await lineElement?.textContent();
|
|
98
|
-
return content ? this.replaceEditorSymbolsWithSpace(content) : undefined;
|
|
79
|
+
return this.monacoEditor.textContentOfLineContainingText(text);
|
|
99
80
|
}
|
|
100
81
|
|
|
101
82
|
async replaceLineContainingText(newText: string, oldText: string): Promise<void> {
|
|
@@ -105,14 +86,14 @@ export class TheiaTextEditor extends TheiaEditor {
|
|
|
105
86
|
|
|
106
87
|
async selectLineContainingText(text: string): Promise<ElementHandle<SVGElement | HTMLElement> | undefined> {
|
|
107
88
|
await this.activate();
|
|
108
|
-
const lineElement = await this.lineContainingText(text);
|
|
89
|
+
const lineElement = await this.monacoEditor.lineContainingText(text);
|
|
109
90
|
await this.selectLine(lineElement);
|
|
110
91
|
return lineElement;
|
|
111
92
|
}
|
|
112
93
|
|
|
113
94
|
async placeCursorInLineContainingText(text: string): Promise<ElementHandle<SVGElement | HTMLElement> | undefined> {
|
|
114
95
|
await this.activate();
|
|
115
|
-
const lineElement = await this.lineContainingText(text);
|
|
96
|
+
const lineElement = await this.monacoEditor.lineContainingText(text);
|
|
116
97
|
await this.placeCursorInLine(lineElement);
|
|
117
98
|
return lineElement;
|
|
118
99
|
}
|
|
@@ -123,7 +104,7 @@ export class TheiaTextEditor extends TheiaEditor {
|
|
|
123
104
|
}
|
|
124
105
|
|
|
125
106
|
async addTextToNewLineAfterLineContainingText(textContainedByExistingLine: string, newText: string): Promise<void> {
|
|
126
|
-
const existingLine = await this.lineContainingText(textContainedByExistingLine);
|
|
107
|
+
const existingLine = await this.monacoEditor.lineContainingText(textContainedByExistingLine);
|
|
127
108
|
await this.placeCursorInLine(existingLine);
|
|
128
109
|
await this.page.keyboard.press('End');
|
|
129
110
|
await this.page.keyboard.press('Enter');
|
|
@@ -131,18 +112,13 @@ export class TheiaTextEditor extends TheiaEditor {
|
|
|
131
112
|
}
|
|
132
113
|
|
|
133
114
|
async addTextToNewLineAfterLineByLineNumber(lineNumber: number, newText: string): Promise<void> {
|
|
134
|
-
const existingLine = await this.lineByLineNumber(lineNumber);
|
|
115
|
+
const existingLine = await this.monacoEditor.lineByLineNumber(lineNumber);
|
|
135
116
|
await this.placeCursorInLine(existingLine);
|
|
136
117
|
await this.page.keyboard.press('End');
|
|
137
118
|
await this.page.keyboard.press('Enter');
|
|
138
119
|
await this.page.keyboard.type(newText);
|
|
139
120
|
}
|
|
140
121
|
|
|
141
|
-
protected async lineContainingText(text: string): Promise<ElementHandle<SVGElement | HTMLElement> | undefined> {
|
|
142
|
-
const viewElement = await this.viewElement();
|
|
143
|
-
return viewElement?.waitForSelector(`.view-lines .view-line:has-text("${text}")`);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
122
|
protected async selectLine(lineElement: ElementHandle<SVGElement | HTMLElement> | undefined): Promise<void> {
|
|
147
123
|
await lineElement?.click({ clickCount: 3 });
|
|
148
124
|
}
|
|
@@ -151,13 +127,6 @@ export class TheiaTextEditor extends TheiaEditor {
|
|
|
151
127
|
await lineElement?.click();
|
|
152
128
|
}
|
|
153
129
|
|
|
154
|
-
protected replaceEditorSymbolsWithSpace(content: string): string | Promise<string | undefined> {
|
|
155
|
-
// [ ] => \u00a0 -- NO-BREAK SPACE
|
|
156
|
-
// [·] · => \u00b7 -- MIDDLE DOT
|
|
157
|
-
// [] ‌ => \u200c -- ZERO WIDTH NON-JOINER
|
|
158
|
-
return content.replace(/[\u00a0\u00b7]/g, ' ').replace(/[\u200c]/g, '');
|
|
159
|
-
}
|
|
160
|
-
|
|
161
130
|
protected async selectedSuggestion(): Promise<ElementHandle<SVGElement | HTMLElement>> {
|
|
162
131
|
return this.page.waitForSelector(this.viewSelector + ' .monaco-list-row.show-file-icons.focused');
|
|
163
132
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 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 WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { ElementHandle } from '@playwright/test';
|
|
18
|
+
import { TheiaApp } from './theia-app';
|
|
19
|
+
import { TheiaPageObject } from './theia-page-object';
|
|
20
|
+
|
|
21
|
+
export class TheiaToolbarItem extends TheiaPageObject {
|
|
22
|
+
constructor(app: TheiaApp, protected element: ElementHandle<SVGElement | HTMLElement>) {
|
|
23
|
+
super(app);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async commandId(): Promise<string | null> {
|
|
27
|
+
return this.element.getAttribute('id');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async isEnabled(): Promise<boolean> {
|
|
31
|
+
const classAttribute = await this.element.getAttribute('class');
|
|
32
|
+
if (classAttribute === undefined || classAttribute === null) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return classAttribute.includes('enabled');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async trigger(): Promise<void> {
|
|
39
|
+
await this.element.click();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2023 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 WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { ElementHandle } from '@playwright/test';
|
|
18
|
+
import { TheiaPageObject } from './theia-page-object';
|
|
19
|
+
import { TheiaToolbarItem } from './theia-toolbar-item';
|
|
20
|
+
|
|
21
|
+
export class TheiaToolbar extends TheiaPageObject {
|
|
22
|
+
selector = 'div#main-toolbar.p-TabBar-toolbar';
|
|
23
|
+
|
|
24
|
+
protected async toolbarElementHandle(): Promise<ElementHandle<SVGElement | HTMLElement> | null> {
|
|
25
|
+
return this.page.$(this.selector);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async waitForVisible(): Promise<void> {
|
|
29
|
+
await this.page.waitForSelector(this.selector, { state: 'visible' });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async isShown(): Promise<boolean> {
|
|
33
|
+
const statusBar = await this.toolbarElementHandle();
|
|
34
|
+
return !!statusBar && statusBar.isVisible();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async show(): Promise<void> {
|
|
38
|
+
if (!await this.isShown()) {
|
|
39
|
+
await this.toggle();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async hide(): Promise<void> {
|
|
44
|
+
if (await this.isShown()) {
|
|
45
|
+
await this.toggle();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async toggle(): Promise<void> {
|
|
50
|
+
const isShown = await this.isShown();
|
|
51
|
+
const viewMenu = await this.app.menuBar.openMenu('View');
|
|
52
|
+
await viewMenu.clickMenuItem('Toggle Toolbar');
|
|
53
|
+
isShown ? await this.waitUntilHidden() : await this.waitUntilShown();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async waitUntilHidden(): Promise<void> {
|
|
57
|
+
await this.page.waitForSelector(this.selector, { state: 'hidden' });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async waitUntilShown(): Promise<void> {
|
|
61
|
+
await this.page.waitForSelector(this.selector, { state: 'visible' });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async toolbarItems(): Promise<TheiaToolbarItem[]> {
|
|
65
|
+
const toolbarHandle = await this.toolbarElementHandle();
|
|
66
|
+
if (!toolbarHandle) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
const items = await toolbarHandle.$$(this.toolBarItemSelector());
|
|
70
|
+
return items.map(element => new TheiaToolbarItem(this.app, element));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async toolbarItemIds(): Promise<string[]> {
|
|
74
|
+
const items = await this.toolbarItems();
|
|
75
|
+
return this.toCommandIdArray(items);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async toolBarItem(commandId: string): Promise<TheiaToolbarItem | undefined> {
|
|
79
|
+
const toolbarHandle = await this.toolbarElementHandle();
|
|
80
|
+
if (!toolbarHandle) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
const item = await toolbarHandle.$(this.toolBarItemSelector(commandId));
|
|
84
|
+
if (item) {
|
|
85
|
+
return new TheiaToolbarItem(this.app, item);
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
protected toolBarItemSelector(toolbarItemId = ''): string {
|
|
91
|
+
return `div.toolbar-item${toolbarItemId ? `[id="${toolbarItemId}"]` : ''}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
protected async toCommandIdArray(items: TheiaToolbarItem[]): Promise<string[]> {
|
|
95
|
+
const contents = items.map(item => item.commandId());
|
|
96
|
+
const resolvedContents = await Promise.all(contents);
|
|
97
|
+
return resolvedContents.filter(id => id !== undefined) as string[];
|
|
98
|
+
}
|
|
99
|
+
}
|
package/src/theia-tree-node.ts
CHANGED
|
@@ -45,7 +45,7 @@ export class TheiaTreeNode {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
async expand(): Promise<void> {
|
|
48
|
-
if (!
|
|
48
|
+
if (!await this.isCollapsed()) {
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
51
|
const expansionToggle = await this.elementHandle.waitForSelector(this.expansionToggleCssClass);
|
|
@@ -53,6 +53,15 @@ export class TheiaTreeNode {
|
|
|
53
53
|
await this.elementHandle.waitForSelector(`${this.expansionToggleCssClass}:not(${this.collapsedCssClass})`);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
async collapse(): Promise<void> {
|
|
57
|
+
if (await this.isCollapsed()) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const expansionToggle = await this.elementHandle.waitForSelector(this.expansionToggleCssClass);
|
|
61
|
+
await expansionToggle.click();
|
|
62
|
+
await this.elementHandle.waitForSelector(`${this.expansionToggleCssClass}${this.collapsedCssClass}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
56
65
|
async openContextMenu(): Promise<TheiaMenu> {
|
|
57
66
|
return TheiaContextMenu.open(this.app, () => this.elementHandle.waitForSelector(this.labelElementCssClass));
|
|
58
67
|
}
|