@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.
Files changed (90) hide show
  1. package/lib/index.d.ts +4 -0
  2. package/lib/index.d.ts.map +1 -1
  3. package/lib/index.js +4 -0
  4. package/lib/index.js.map +1 -1
  5. package/lib/tests/theia-explorer-view.test.js +24 -5
  6. package/lib/tests/theia-explorer-view.test.js.map +1 -1
  7. package/lib/tests/theia-output-view.test.d.ts +2 -0
  8. package/lib/tests/theia-output-view.test.d.ts.map +1 -0
  9. package/lib/tests/theia-output-view.test.js +77 -0
  10. package/lib/tests/theia-output-view.test.js.map +1 -0
  11. package/lib/tests/theia-quick-command.test.js +14 -0
  12. package/lib/tests/theia-quick-command.test.js.map +1 -1
  13. package/lib/tests/theia-sample-app.test.js +3 -30
  14. package/lib/tests/theia-sample-app.test.js.map +1 -1
  15. package/lib/tests/theia-terminal-view.test.d.ts +2 -0
  16. package/lib/tests/theia-terminal-view.test.d.ts.map +1 -0
  17. package/lib/tests/theia-terminal-view.test.js +76 -0
  18. package/lib/tests/theia-terminal-view.test.js.map +1 -0
  19. package/lib/tests/theia-toolbar.test.d.ts +2 -0
  20. package/lib/tests/theia-toolbar.test.d.ts.map +1 -0
  21. package/lib/tests/theia-toolbar.test.js +60 -0
  22. package/lib/tests/theia-toolbar.test.js.map +1 -0
  23. package/lib/tests/theia-workspace.test.js +5 -3
  24. package/lib/tests/theia-workspace.test.js.map +1 -1
  25. package/lib/theia-app.d.ts +7 -0
  26. package/lib/theia-app.d.ts.map +1 -1
  27. package/lib/theia-app.js +30 -1
  28. package/lib/theia-app.js.map +1 -1
  29. package/lib/theia-explorer-view.d.ts +6 -4
  30. package/lib/theia-explorer-view.d.ts.map +1 -1
  31. package/lib/theia-explorer-view.js +38 -6
  32. package/lib/theia-explorer-view.js.map +1 -1
  33. package/lib/theia-monaco-editor.d.ts +16 -0
  34. package/lib/theia-monaco-editor.d.ts.map +1 -0
  35. package/lib/theia-monaco-editor.js +76 -0
  36. package/lib/theia-monaco-editor.js.map +1 -0
  37. package/lib/theia-output-channel.d.ts +25 -0
  38. package/lib/theia-output-channel.d.ts.map +1 -0
  39. package/lib/theia-output-channel.js +72 -0
  40. package/lib/theia-output-channel.js.map +1 -0
  41. package/lib/theia-output-view.d.ts +10 -0
  42. package/lib/theia-output-view.d.ts.map +1 -0
  43. package/lib/theia-output-view.js +82 -0
  44. package/lib/theia-output-view.js.map +1 -0
  45. package/lib/theia-quick-command-palette.d.ts +2 -1
  46. package/lib/theia-quick-command-palette.d.ts.map +1 -1
  47. package/lib/theia-quick-command-palette.js +9 -2
  48. package/lib/theia-quick-command-palette.js.map +1 -1
  49. package/lib/theia-terminal.d.ts +14 -0
  50. package/lib/theia-terminal.d.ts.map +1 -0
  51. package/lib/theia-terminal.js +60 -0
  52. package/lib/theia-terminal.js.map +1 -0
  53. package/lib/theia-text-editor.d.ts +2 -3
  54. package/lib/theia-text-editor.d.ts.map +1 -1
  55. package/lib/theia-text-editor.js +11 -40
  56. package/lib/theia-text-editor.js.map +1 -1
  57. package/lib/theia-toolbar-item.d.ts +11 -0
  58. package/lib/theia-toolbar-item.d.ts.map +1 -0
  59. package/lib/theia-toolbar-item.js +40 -0
  60. package/lib/theia-toolbar-item.js.map +1 -0
  61. package/lib/theia-toolbar.d.ts +20 -0
  62. package/lib/theia-toolbar.d.ts.map +1 -0
  63. package/lib/theia-toolbar.js +91 -0
  64. package/lib/theia-toolbar.js.map +1 -0
  65. package/lib/theia-tree-node.d.ts +1 -0
  66. package/lib/theia-tree-node.d.ts.map +1 -1
  67. package/lib/theia-tree-node.js +8 -0
  68. package/lib/theia-tree-node.js.map +1 -1
  69. package/package.json +13 -10
  70. package/src/index.ts +4 -0
  71. package/src/tests/resources/sample-files1/sampleFolderCompact/nestedFolder1/nestedFolder2/sampleFile1-1.txt +0 -0
  72. package/src/tests/theia-explorer-view.test.ts +25 -6
  73. package/src/tests/theia-output-view.test.ts +85 -0
  74. package/src/tests/theia-quick-command.test.ts +15 -0
  75. package/src/tests/theia-sample-app.test.ts +3 -34
  76. package/src/tests/theia-terminal-view.test.ts +85 -0
  77. package/src/tests/theia-toolbar.test.ts +65 -0
  78. package/src/tests/theia-workspace.test.ts +5 -3
  79. package/src/theia-app.ts +37 -1
  80. package/src/theia-explorer-view.ts +42 -7
  81. package/src/theia-monaco-editor.ts +83 -0
  82. package/src/theia-output-channel.ts +88 -0
  83. package/src/theia-output-view.ts +87 -0
  84. package/src/theia-quick-command-palette.ts +10 -2
  85. package/src/theia-terminal.ts +69 -0
  86. package/src/theia-text-editor.ts +13 -44
  87. package/src/theia-toolbar-item.ts +41 -0
  88. package/src/theia-toolbar.ts +99 -0
  89. package/src/theia-tree-node.ts +10 -1
  90. 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(command: string): Promise<void> {
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(command, { delay: USER_KEY_TYPING_DELAY });
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
+ }
@@ -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
- const viewElement = await this.viewElement();
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
- const lineElement = await this.lineByLineNumber(lineNumber);
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
- const lineElement = await this.lineContainingText(text);
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
- // [ ] &nbsp; => \u00a0 -- NO-BREAK SPACE
156
- // [·] &middot; => \u00b7 -- MIDDLE DOT
157
- // [] &zwnj; => \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
+ }
@@ -45,7 +45,7 @@ export class TheiaTreeNode {
45
45
  }
46
46
 
47
47
  async expand(): Promise<void> {
48
- if (! await this.isCollapsed()) {
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
  }