@opensumi/playwright 2.21.13 → 2.22.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/app.d.ts +2 -1
- package/lib/app.d.ts.map +1 -1
- package/lib/app.js +21 -9
- package/lib/app.js.map +1 -1
- package/lib/component-editor.d.ts +2 -2
- package/lib/debug-view.d.ts +10 -0
- package/lib/debug-view.d.ts.map +1 -1
- package/lib/debug-view.js +41 -1
- package/lib/debug-view.js.map +1 -1
- package/lib/diff-editor.d.ts +4 -0
- package/lib/diff-editor.d.ts.map +1 -0
- package/lib/diff-editor.js +8 -0
- package/lib/diff-editor.js.map +1 -0
- package/lib/editor.d.ts +6 -5
- package/lib/editor.d.ts.map +1 -1
- package/lib/editor.js +6 -2
- package/lib/editor.js.map +1 -1
- package/lib/explorer-view.d.ts +4 -0
- package/lib/explorer-view.d.ts.map +1 -1
- package/lib/explorer-view.js +36 -10
- package/lib/explorer-view.js.map +1 -1
- package/lib/filetree-view.d.ts +1 -1
- package/lib/filetree-view.js +2 -2
- package/lib/filetree-view.js.map +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -1
- package/lib/index.js.map +1 -1
- package/lib/menu.d.ts +1 -1
- package/lib/opened-editor-view.d.ts +2 -2
- package/lib/opened-editor-view.d.ts.map +1 -1
- package/lib/opened-editor-view.js +4 -4
- package/lib/opened-editor-view.js.map +1 -1
- package/lib/outline-view.d.ts +9 -0
- package/lib/outline-view.d.ts.map +1 -0
- package/lib/outline-view.js +38 -0
- package/lib/outline-view.js.map +1 -0
- package/lib/output-view.d.ts +10 -0
- package/lib/output-view.d.ts.map +1 -0
- package/lib/output-view.js +78 -0
- package/lib/output-view.js.map +1 -0
- package/lib/panel.js +2 -2
- package/lib/panel.js.map +1 -1
- package/lib/scm-view.d.ts +21 -0
- package/lib/scm-view.d.ts.map +1 -0
- package/lib/scm-view.js +65 -0
- package/lib/scm-view.js.map +1 -0
- package/lib/search-view.d.ts +34 -0
- package/lib/search-view.d.ts.map +1 -1
- package/lib/search-view.js +196 -4
- package/lib/search-view.js.map +1 -1
- package/lib/source-control-view.d.ts +8 -0
- package/lib/source-control-view.d.ts.map +1 -0
- package/lib/source-control-view.js +45 -0
- package/lib/source-control-view.js.map +1 -0
- package/lib/terminal-view.d.ts +10 -0
- package/lib/terminal-view.d.ts.map +1 -0
- package/lib/terminal-view.js +50 -0
- package/lib/terminal-view.js.map +1 -0
- package/lib/tests/debug.test.js +49 -0
- package/lib/tests/debug.test.js.map +1 -1
- package/lib/tests/editor/undoRedo.test.d.ts +2 -0
- package/lib/tests/editor/undoRedo.test.d.ts.map +1 -0
- package/lib/tests/editor/undoRedo.test.js +52 -0
- package/lib/tests/editor/undoRedo.test.js.map +1 -0
- package/lib/tests/editor.test.js +0 -2
- package/lib/tests/editor.test.js.map +1 -1
- package/lib/tests/explorer-view.test.js +129 -5
- package/lib/tests/explorer-view.test.js.map +1 -1
- package/lib/tests/output.test.d.ts +2 -0
- package/lib/tests/output.test.d.ts.map +1 -0
- package/lib/tests/output.test.js +76 -0
- package/lib/tests/output.test.js.map +1 -0
- package/lib/tests/scm.test.js +42 -4
- package/lib/tests/scm.test.js.map +1 -1
- package/lib/tests/search-view.test.js +191 -8
- package/lib/tests/search-view.test.js.map +1 -1
- package/lib/tests/settings.test.d.ts +2 -0
- package/lib/tests/settings.test.d.ts.map +1 -0
- package/lib/tests/settings.test.js +95 -0
- package/lib/tests/settings.test.js.map +1 -0
- package/lib/text-editor.d.ts +7 -3
- package/lib/text-editor.d.ts.map +1 -1
- package/lib/text-editor.js +15 -0
- package/lib/text-editor.js.map +1 -1
- package/lib/tree-node.d.ts +3 -1
- package/lib/tree-node.d.ts.map +1 -1
- package/lib/tree-node.js +4 -4
- package/lib/tree-node.js.map +1 -1
- package/lib/utils/key.d.ts +1 -0
- package/lib/utils/key.d.ts.map +1 -1
- package/lib/utils/key.js +6 -1
- package/lib/utils/key.js.map +1 -1
- package/lib/view.js +1 -1
- package/lib/view.js.map +1 -1
- package/package.json +12 -11
- package/src/app.ts +148 -0
- package/src/component-editor.ts +64 -0
- package/src/constans/index.ts +18 -0
- package/src/context-menu.ts +27 -0
- package/src/debug-view.ts +73 -0
- package/src/diff-editor.ts +3 -0
- package/src/editor.ts +135 -0
- package/src/explorer-view.ts +149 -0
- package/src/filetree-view.ts +28 -0
- package/src/index.ts +20 -0
- package/src/menu-item.ts +40 -0
- package/src/menu.ts +69 -0
- package/src/menubar.ts +53 -0
- package/src/opened-editor-view.ts +28 -0
- package/src/outline-view.ts +37 -0
- package/src/output-view.ts +76 -0
- package/src/panel.ts +50 -0
- package/src/quick-command-palette.ts +62 -0
- package/src/quick-open-palette.ts +62 -0
- package/src/scm-view.ts +72 -0
- package/src/search-view.ts +260 -0
- package/src/source-control-view.ts +44 -0
- package/src/terminal-view.ts +50 -0
- package/src/tests/app.test.ts +16 -0
- package/src/tests/debug.test.ts +121 -0
- package/src/tests/editor/undoRedo.test.ts +55 -0
- package/src/tests/editor.test.ts +141 -0
- package/src/tests/explorer-view.test.ts +329 -0
- package/src/tests/hooks/index.ts +13 -0
- package/src/tests/keymaps.test.ts +118 -0
- package/src/tests/language.test.ts +55 -0
- package/src/tests/output.test.ts +87 -0
- package/src/tests/scm.test.ts +84 -0
- package/src/tests/search-view.test.ts +239 -0
- package/src/tests/settings.test.ts +115 -0
- package/src/tests/workspaces/debug/.sumi/launch.json +15 -0
- package/src/tests/workspaces/debug/index.js +18 -0
- package/src/tests/workspaces/default/editor-undo-redo.text +0 -0
- package/src/tests/workspaces/default/editor.js +0 -0
- package/src/tests/workspaces/default/editor2.js +87 -0
- package/src/tests/workspaces/default/editor3.js +0 -0
- package/src/tests/workspaces/default/test/test.js +1 -0
- package/src/tests/workspaces/git-workspace/a.js +0 -0
- package/src/tests/workspaces/language/definition.ts +12 -0
- package/src/tests/workspaces/language/reference.ts +9 -0
- package/src/tests/workspaces/search/index.js +5 -0
- package/src/tests/workspaces/search/index2.js +1 -0
- package/src/text-editor.ts +333 -0
- package/src/tree-node.ts +98 -0
- package/src/utils/element.ts +35 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/key.ts +11 -0
- package/src/view-base.ts +11 -0
- package/src/view.ts +90 -0
- package/src/workspace.ts +36 -0
- package/lib/terminal.d.ts +0 -7
- package/lib/terminal.d.ts.map +0 -1
- package/lib/terminal.js +0 -25
- package/lib/terminal.js.map +0 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { OpenSumiApp } from './app';
|
|
2
|
+
import { OpenSumiFileTreeView } from './filetree-view';
|
|
3
|
+
import { OpenSumiOpenedEditorView } from './opened-editor-view';
|
|
4
|
+
import { OpenSumiOutlineView } from './outline-view';
|
|
5
|
+
import { OpenSumiPanel } from './panel';
|
|
6
|
+
import { OpenSumiTreeNode } from './tree-node';
|
|
7
|
+
|
|
8
|
+
export class OpenSumiExplorerFileStatNode extends OpenSumiTreeNode {
|
|
9
|
+
async getFsPath() {
|
|
10
|
+
return await this.elementHandle.getAttribute('title');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async isFolder() {
|
|
14
|
+
const icon = await this.elementHandle.$("[class*='file_icon___']");
|
|
15
|
+
if (!icon) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
const className = await icon.getAttribute('class');
|
|
19
|
+
return className?.includes('folder-icon');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async getMenuItemByName(name: string) {
|
|
23
|
+
const contextMenu = await this.openContextMenu();
|
|
24
|
+
const menuItem = await contextMenu.menuItemByName(name);
|
|
25
|
+
return menuItem;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async open(preview = true) {
|
|
29
|
+
if (!preview) {
|
|
30
|
+
await this.elementHandle.dblclick();
|
|
31
|
+
} else {
|
|
32
|
+
await this.elementHandle.click();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async isDirty() {
|
|
37
|
+
const classname = await this.elementHandle.getAttribute('class');
|
|
38
|
+
if (classname?.includes('mod_dirty__')) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class OpenSumiExplorerOpenedEditorNode extends OpenSumiTreeNode {
|
|
46
|
+
async getRelativePath() {
|
|
47
|
+
return await (await this.elementHandle.$('[class*="opened_editor_node_description__"]'))?.textContent();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async getFsPath() {
|
|
51
|
+
return await this.elementHandle.getAttribute('title');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async isGroup() {
|
|
55
|
+
const icon = await this.elementHandle.waitForSelector("[class*='file_icon___']");
|
|
56
|
+
const className = await icon.getAttribute('class');
|
|
57
|
+
return className?.includes('folder-icon');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async getMenuItemByName(name: string) {
|
|
61
|
+
const contextMenu = await this.openContextMenu();
|
|
62
|
+
const menuItem = await contextMenu.menuItemByName(name);
|
|
63
|
+
return menuItem;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async open() {
|
|
67
|
+
await this.elementHandle.click();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export class OpenSumiExplorerView extends OpenSumiPanel {
|
|
72
|
+
private _fileTreeView: OpenSumiFileTreeView;
|
|
73
|
+
private _openedEditorView: OpenSumiOpenedEditorView;
|
|
74
|
+
private _outlineView: OpenSumiOutlineView;
|
|
75
|
+
|
|
76
|
+
constructor(app: OpenSumiApp) {
|
|
77
|
+
super(app, 'EXPLORER');
|
|
78
|
+
this._openedEditorView = new OpenSumiOpenedEditorView(this.app);
|
|
79
|
+
this._outlineView = new OpenSumiOutlineView(this.app);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
initFileTreeView(name: string) {
|
|
83
|
+
this._fileTreeView = new OpenSumiFileTreeView(this.app, name);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get fileTreeView() {
|
|
87
|
+
return this._fileTreeView;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get openedEditorView() {
|
|
91
|
+
return this._openedEditorView;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
get outlineView() {
|
|
95
|
+
return this._outlineView;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async getFileStatTreeNodeByPath(path: string) {
|
|
99
|
+
const treeItems = await (await this.fileTreeView.getViewElement())?.$$('[class*="file_tree_node__"]');
|
|
100
|
+
if (!treeItems) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
let node;
|
|
104
|
+
for (const item of treeItems) {
|
|
105
|
+
const title = await item.getAttribute('title');
|
|
106
|
+
if (title?.startsWith('Group')) {
|
|
107
|
+
if (title === path) {
|
|
108
|
+
node = item;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
// The title maybe `~/a.js • Untracked`
|
|
113
|
+
if (title?.split(' ')[0]?.endsWith(path)) {
|
|
114
|
+
node = item;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (node) {
|
|
120
|
+
return new OpenSumiExplorerFileStatNode(node, this.app);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async getOpenedEditorTreeNodeByPath(path: string) {
|
|
125
|
+
const treeItems = await (await this.openedEditorView.getViewElement())?.$$('[class*="opened_editor_node__"]');
|
|
126
|
+
if (!treeItems) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
let node;
|
|
130
|
+
for (const item of treeItems) {
|
|
131
|
+
const title = await item.getAttribute('title');
|
|
132
|
+
if (title?.startsWith('GROUP')) {
|
|
133
|
+
if (title === path) {
|
|
134
|
+
node = item;
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
// The title maybe `~/a.js • Untracked`
|
|
139
|
+
if (title?.split(' ')[0]?.endsWith(path)) {
|
|
140
|
+
node = item;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (node) {
|
|
146
|
+
return new OpenSumiExplorerFileStatNode(node, this.app);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { OpenSumiApp } from './app';
|
|
2
|
+
import { OpenSumiView } from './view';
|
|
3
|
+
|
|
4
|
+
export class OpenSumiFileTreeView extends OpenSumiView {
|
|
5
|
+
constructor(app: OpenSumiApp, workspaceName: string) {
|
|
6
|
+
super(app, {
|
|
7
|
+
viewSelector: '[data-view-id="file-explorer"]',
|
|
8
|
+
tabSelector: '[data-view-id="file-explorer"] [tabindex="0"]',
|
|
9
|
+
name: workspaceName,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async getTitleActionByName(name: string) {
|
|
14
|
+
const header = await this.getTabElement();
|
|
15
|
+
if (!header) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
await header.hover();
|
|
19
|
+
const titleAction = await header.waitForSelector('[class*="titleActions___"]');
|
|
20
|
+
const actions = await titleAction.$$('[class*="iconAction__"]');
|
|
21
|
+
for (const action of actions) {
|
|
22
|
+
const title = await action.getAttribute('title');
|
|
23
|
+
if (name === title) {
|
|
24
|
+
return action;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export * from './app';
|
|
2
|
+
export * from './component-editor';
|
|
3
|
+
export * from './context-menu';
|
|
4
|
+
export * from './editor';
|
|
5
|
+
export * from './explorer-view';
|
|
6
|
+
export * from './filetree-view';
|
|
7
|
+
export * from './menu-item';
|
|
8
|
+
export * from './menu';
|
|
9
|
+
export * from './menubar';
|
|
10
|
+
export * from './opened-editor-view';
|
|
11
|
+
export * from './panel';
|
|
12
|
+
export * from './quick-command-palette';
|
|
13
|
+
export * from './search-view';
|
|
14
|
+
export * from './terminal-view';
|
|
15
|
+
export * from './output-view';
|
|
16
|
+
export * from './text-editor';
|
|
17
|
+
export * from './tree-node';
|
|
18
|
+
export * from './view-base';
|
|
19
|
+
export * from './view';
|
|
20
|
+
export * from './workspace';
|
package/src/menu-item.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ElementHandle } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
import { textContent } from './utils';
|
|
4
|
+
|
|
5
|
+
export class OpenSumiMenuItem {
|
|
6
|
+
constructor(protected element: ElementHandle<SVGElement | HTMLElement>) {}
|
|
7
|
+
|
|
8
|
+
protected labelElementHandle() {
|
|
9
|
+
return this.element.$("[class*='label__']");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
protected shortCutElementHandle() {
|
|
13
|
+
return this.element.$("[class*='shortcut__']");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async label() {
|
|
17
|
+
return textContent(this.labelElementHandle());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async shortCut() {
|
|
21
|
+
return textContent(this.shortCutElementHandle());
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async isEnabled() {
|
|
25
|
+
const classAttribute = await this.element.getAttribute('class');
|
|
26
|
+
if (classAttribute === undefined || classAttribute === null) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return !classAttribute.includes('kt-inner-menu-item-disabled');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async click() {
|
|
33
|
+
const action = await this.element.waitForSelector("[class*='menuAction__']");
|
|
34
|
+
await action?.click({ position: { x: 10, y: 10 } });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async hover(): Promise<void> {
|
|
38
|
+
return this.element.hover();
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/menu.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { OpenSumiMenuItem } from './menu-item';
|
|
2
|
+
import { OpenSumiViewBase } from './view-base';
|
|
3
|
+
|
|
4
|
+
export class OpenSumiMenu extends OpenSumiViewBase {
|
|
5
|
+
selector = '.rc-trigger-popup .kt-inner-menu';
|
|
6
|
+
|
|
7
|
+
protected async menuElementHandle() {
|
|
8
|
+
return this.page.waitForSelector(this.selector);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async waitForVisible() {
|
|
12
|
+
await this.page.waitForSelector(this.selector, { state: 'visible' });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async isOpen() {
|
|
16
|
+
const menu = await this.menuElementHandle();
|
|
17
|
+
return !!menu && menu.isVisible();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async close() {
|
|
21
|
+
if (!(await this.isOpen())) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
await this.page.mouse.click(0, 0);
|
|
25
|
+
await this.page.waitForSelector(this.selector, { state: 'detached' });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async menuItems() {
|
|
29
|
+
const menuHandle = await this.menuElementHandle();
|
|
30
|
+
if (!menuHandle) {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
const items = await menuHandle.$$('.kt-inner-menu-item');
|
|
34
|
+
return items.map((element) => new OpenSumiMenuItem(element));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async clickMenuItem(name: string) {
|
|
38
|
+
return (await this.page.waitForSelector(this.menuItemSelector(name))).click();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async menuItemByName(name: string) {
|
|
42
|
+
const menuItems = await this.menuItems();
|
|
43
|
+
for (const item of menuItems) {
|
|
44
|
+
const label = await item.label();
|
|
45
|
+
if (label === name) {
|
|
46
|
+
return item;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async menuItemByIndex(index: number) {
|
|
53
|
+
const menuItems = await this.menuItems();
|
|
54
|
+
if (menuItems.length > index) {
|
|
55
|
+
return menuItems[index];
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
protected menuItemSelector(label = '') {
|
|
61
|
+
return `.kt-inner-menu-item [class*='label___'] >> text=${label}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async visibleMenuItems() {
|
|
65
|
+
const menuItems = await this.menuItems();
|
|
66
|
+
const labels = await Promise.all(menuItems.map((item) => item.label()));
|
|
67
|
+
return labels.filter((label) => !!label);
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/menubar.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ElementHandle } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
import { OpenSumiApp } from './app';
|
|
4
|
+
import { OPENSUMI_VIEW_CONTAINERS } from './constans';
|
|
5
|
+
import { textContent } from './utils';
|
|
6
|
+
import { OpenSumiViewBase } from './view-base';
|
|
7
|
+
|
|
8
|
+
export class OpenSumiMenubar extends OpenSumiViewBase {
|
|
9
|
+
static USER_KEY_TYPING_DELAY = 100;
|
|
10
|
+
|
|
11
|
+
private _menuItems: ElementHandle[];
|
|
12
|
+
|
|
13
|
+
selector = `#${OPENSUMI_VIEW_CONTAINERS.MENUBAR}`;
|
|
14
|
+
|
|
15
|
+
constructor(app: OpenSumiApp) {
|
|
16
|
+
super(app);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async getMenubar() {
|
|
20
|
+
return await this.page.waitForSelector(this.selector);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async trigger(group: string, command: string) {
|
|
24
|
+
// group maybe on of this: File, Edit, Selection, View, Go, Terminal, Help
|
|
25
|
+
const sections = await this.getMenuItems();
|
|
26
|
+
let item;
|
|
27
|
+
for (const section of sections) {
|
|
28
|
+
if ((await section.textContent()) === group) {
|
|
29
|
+
item = section;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (!item) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
await item.click();
|
|
36
|
+
const menu = await this.page.waitForSelector('.kt-dropdown:not(.kt-dropdown-hidden)');
|
|
37
|
+
const menuItems = await menu.$$('.kt-inner-menu-item');
|
|
38
|
+
for (const menu of menuItems) {
|
|
39
|
+
const label = await textContent(menu.$("[class*='label__']"));
|
|
40
|
+
if (command === label) {
|
|
41
|
+
menu.click();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async getMenuItems() {
|
|
48
|
+
if (!this._menuItems) {
|
|
49
|
+
return await (await this.getMenubar())?.$$("[class^='menubar___']");
|
|
50
|
+
}
|
|
51
|
+
return this._menuItems;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { OpenSumiApp } from './app';
|
|
2
|
+
import { OpenSumiView } from './view';
|
|
3
|
+
|
|
4
|
+
export class OpenSumiOpenedEditorView extends OpenSumiView {
|
|
5
|
+
constructor(app: OpenSumiApp) {
|
|
6
|
+
super(app, {
|
|
7
|
+
viewSelector: '[data-view-id="file-opened-editor"]',
|
|
8
|
+
tabSelector: '[data-view-id="file-opened-editor"] [tabindex="0"]',
|
|
9
|
+
name: 'OPENED EDITORS',
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async getTitleActionByName(name: string) {
|
|
14
|
+
const header = await this.getTabElement();
|
|
15
|
+
if (!header) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
await header.hover();
|
|
19
|
+
const titleAction = await header.waitForSelector('[class*="titleActions___"]');
|
|
20
|
+
const actions = await titleAction.$$('[class*="iconAction__"]');
|
|
21
|
+
for (const action of actions) {
|
|
22
|
+
const title = await action.getAttribute('title');
|
|
23
|
+
if (name === title) {
|
|
24
|
+
return action;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { OpenSumiApp } from './app';
|
|
2
|
+
import { OpenSumiContextMenu } from './context-menu';
|
|
3
|
+
import { OpenSumiView } from './view';
|
|
4
|
+
|
|
5
|
+
export class OpenSumiOutlineView extends OpenSumiView {
|
|
6
|
+
constructor(app: OpenSumiApp) {
|
|
7
|
+
super(app, {
|
|
8
|
+
viewSelector: '[data-view-id="outline-view"]',
|
|
9
|
+
tabSelector: '[data-view-id="outline-view"] [tabindex="0"]',
|
|
10
|
+
name: 'OUTLINE',
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async getTitleActionByName(name: string) {
|
|
15
|
+
const header = await this.getTabElement();
|
|
16
|
+
if (!header) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
await header.hover();
|
|
20
|
+
const titleAction = await header.waitForSelector('[class*="titleActions___"]');
|
|
21
|
+
const actions = await titleAction.$$('[class*="iconAction__"]');
|
|
22
|
+
for (const action of actions) {
|
|
23
|
+
const title = await action.getAttribute('title');
|
|
24
|
+
if (name === title) {
|
|
25
|
+
return action;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async openTabContextMenu() {
|
|
31
|
+
const header = await this.getTabElement();
|
|
32
|
+
if (!header) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
return OpenSumiContextMenu.open(this.app, async () => header);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { OpenSumiApp } from './app';
|
|
2
|
+
import { OpenSumiPanel } from './panel';
|
|
3
|
+
|
|
4
|
+
export class OpenSumiOutputView extends OpenSumiPanel {
|
|
5
|
+
constructor(app: OpenSumiApp) {
|
|
6
|
+
super(app, 'OUTPUT');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async setChannel(type: string) {
|
|
10
|
+
const visible = await this.isVisible();
|
|
11
|
+
if (!visible) {
|
|
12
|
+
await this.open();
|
|
13
|
+
}
|
|
14
|
+
const titleBar = await this.view?.$('[class*="panel_title_bar___"]');
|
|
15
|
+
const select = await titleBar?.$('.kt-select-option');
|
|
16
|
+
if (select) {
|
|
17
|
+
await select.click();
|
|
18
|
+
const wrapper = await titleBar?.$('.kt-select-options');
|
|
19
|
+
const options = await wrapper?.$$('span');
|
|
20
|
+
if (!options) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
for (const option of options) {
|
|
24
|
+
const text = await option.textContent();
|
|
25
|
+
if (text === type) {
|
|
26
|
+
await option.click();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async getCurrentContent() {
|
|
34
|
+
const visible = await this.isVisible();
|
|
35
|
+
if (!visible) {
|
|
36
|
+
await this.open();
|
|
37
|
+
}
|
|
38
|
+
const content = await this.view?.$('[class*="output___"]');
|
|
39
|
+
const lines = await content?.$$('.view-line');
|
|
40
|
+
if (!lines) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
let text = '';
|
|
44
|
+
for (const line of lines) {
|
|
45
|
+
const lineText = await line.textContent();
|
|
46
|
+
text += lineText + '\n';
|
|
47
|
+
}
|
|
48
|
+
return text;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async clean() {
|
|
52
|
+
const button = await this.getAction('Clear Output Panel');
|
|
53
|
+
await button?.click();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async getAction(name: string) {
|
|
57
|
+
const visible = await this.isVisible();
|
|
58
|
+
if (!visible) {
|
|
59
|
+
await this.open();
|
|
60
|
+
}
|
|
61
|
+
const titleBar = await this.view?.$('[class*="panel_title_bar___"]');
|
|
62
|
+
const actions = await titleBar?.$$('[class*="titleActions___"]');
|
|
63
|
+
if (!actions) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
for (const action of actions) {
|
|
67
|
+
const items = await action.$$('[class*="iconAction__"]');
|
|
68
|
+
for (const item of items) {
|
|
69
|
+
const title = await item.getAttribute('title');
|
|
70
|
+
if (title === name) {
|
|
71
|
+
return item;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
package/src/panel.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ElementHandle } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
import { OpenSumiApp } from './app';
|
|
4
|
+
import { OpenSumiViewBase } from './view-base';
|
|
5
|
+
|
|
6
|
+
export abstract class OpenSumiPanel extends OpenSumiViewBase {
|
|
7
|
+
public view: ElementHandle<HTMLElement | SVGElement> | null;
|
|
8
|
+
private whenReady: Promise<void>;
|
|
9
|
+
|
|
10
|
+
constructor(app: OpenSumiApp, private viewId: string) {
|
|
11
|
+
super(app);
|
|
12
|
+
this.whenReady = this.init();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get viewSelector() {
|
|
16
|
+
return `[data-viewlet-id="${this.viewId.toLocaleLowerCase()}"]`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async init() {
|
|
20
|
+
this.view = await this.page.$(this.viewSelector);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async isVisible() {
|
|
24
|
+
await this.whenReady;
|
|
25
|
+
return this.view?.isVisible();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async open() {
|
|
29
|
+
if (!this.viewId) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
await this.app.quickOpenPalette.type('view ');
|
|
33
|
+
await this.app.quickOpenPalette.trigger(this.viewId);
|
|
34
|
+
await this.waitForVisible();
|
|
35
|
+
this.view = await this.page.$(this.viewSelector);
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async focus() {
|
|
40
|
+
const visible = await this.isVisible();
|
|
41
|
+
if (!visible) {
|
|
42
|
+
await this.open();
|
|
43
|
+
}
|
|
44
|
+
await this.view?.focus();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async waitForVisible() {
|
|
48
|
+
await this.page.waitForSelector(this.viewSelector, { state: 'visible' });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ElementHandle } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
import { isMacintosh } from '@opensumi/ide-utils';
|
|
4
|
+
|
|
5
|
+
import { OPENSUMI_VIEW_CONTAINERS } from './constans';
|
|
6
|
+
import { OpenSumiViewBase } from './view-base';
|
|
7
|
+
|
|
8
|
+
export class OpenSumiCommandPalette extends OpenSumiViewBase {
|
|
9
|
+
static USER_KEY_TYPING_DELAY = 200;
|
|
10
|
+
|
|
11
|
+
async open() {
|
|
12
|
+
await this.page.keyboard.press(isMacintosh ? 'Meta+Shift+p' : 'Control+Shift+p');
|
|
13
|
+
await this.page.waitForSelector(`#${OPENSUMI_VIEW_CONTAINERS.QUICKPICK}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async isOpen(): Promise<boolean> {
|
|
17
|
+
try {
|
|
18
|
+
await this.page.waitForSelector(`#${OPENSUMI_VIEW_CONTAINERS.QUICKPICK}`, { timeout: 5000 });
|
|
19
|
+
} catch (err) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async trigger(...commandName: string[]): Promise<void> {
|
|
26
|
+
for (const command of commandName) {
|
|
27
|
+
await this.triggerSingleCommand(command);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
protected async triggerSingleCommand(commandName: string): Promise<void> {
|
|
32
|
+
if (!(await this.isOpen())) {
|
|
33
|
+
await this.open();
|
|
34
|
+
}
|
|
35
|
+
let selected = await this.selectedCommand();
|
|
36
|
+
while (!((await selected?.getAttribute('aria-label')) === commandName)) {
|
|
37
|
+
await this.page.keyboard.press('ArrowDown');
|
|
38
|
+
selected = await this.selectedCommand();
|
|
39
|
+
}
|
|
40
|
+
await this.page.keyboard.press('Enter');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async type(command: string): Promise<void> {
|
|
44
|
+
if (!(await this.isOpen())) {
|
|
45
|
+
await this.open();
|
|
46
|
+
}
|
|
47
|
+
const input = await this.page.waitForSelector(`#${OPENSUMI_VIEW_CONTAINERS.QUICKPICK_INPUT}`);
|
|
48
|
+
if (input != null) {
|
|
49
|
+
await input.focus();
|
|
50
|
+
await input.type(command, { delay: OpenSumiCommandPalette.USER_KEY_TYPING_DELAY });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
protected async selectedCommand(): Promise<ElementHandle<SVGElement | HTMLElement> | null> {
|
|
55
|
+
const command = await this.page.waitForSelector(`#${OPENSUMI_VIEW_CONTAINERS.QUICKPICK}`);
|
|
56
|
+
if (!command) {
|
|
57
|
+
throw new Error('No selected command found!');
|
|
58
|
+
}
|
|
59
|
+
const item = await command.$("[class*='item_selected']");
|
|
60
|
+
return item;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ElementHandle } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
import { isMacintosh } from '@opensumi/ide-utils';
|
|
4
|
+
|
|
5
|
+
import { OPENSUMI_VIEW_CONTAINERS } from './constans';
|
|
6
|
+
import { OpenSumiViewBase } from './view-base';
|
|
7
|
+
|
|
8
|
+
export class OpenSumiQuickOpenPalette extends OpenSumiViewBase {
|
|
9
|
+
static USER_KEY_TYPING_DELAY = 200;
|
|
10
|
+
|
|
11
|
+
async open() {
|
|
12
|
+
await this.page.keyboard.press(isMacintosh ? 'Meta+p' : 'Control+p');
|
|
13
|
+
await this.page.waitForSelector(`#${OPENSUMI_VIEW_CONTAINERS.QUICKPICK}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async isOpen(): Promise<boolean> {
|
|
17
|
+
try {
|
|
18
|
+
await this.page.waitForSelector(`#${OPENSUMI_VIEW_CONTAINERS.QUICKPICK}`, { timeout: 5000 });
|
|
19
|
+
} catch (err) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async trigger(...commandName: string[]): Promise<void> {
|
|
26
|
+
for (const command of commandName) {
|
|
27
|
+
await this.triggerSingleCommand(command);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
protected async triggerSingleCommand(commandName: string): Promise<void> {
|
|
32
|
+
if (!(await this.isOpen())) {
|
|
33
|
+
await this.open();
|
|
34
|
+
}
|
|
35
|
+
let selected = await this.selectedCommand();
|
|
36
|
+
while (!((await selected?.getAttribute('aria-label')) === commandName)) {
|
|
37
|
+
await this.page.keyboard.press('ArrowDown');
|
|
38
|
+
selected = await this.selectedCommand();
|
|
39
|
+
}
|
|
40
|
+
await this.page.keyboard.press('Enter');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async type(command: string): Promise<void> {
|
|
44
|
+
if (!(await this.isOpen())) {
|
|
45
|
+
await this.open();
|
|
46
|
+
}
|
|
47
|
+
const input = await this.page.waitForSelector(`#${OPENSUMI_VIEW_CONTAINERS.QUICKPICK_INPUT}`);
|
|
48
|
+
if (input != null) {
|
|
49
|
+
await input.focus();
|
|
50
|
+
await input.type(command, { delay: OpenSumiQuickOpenPalette.USER_KEY_TYPING_DELAY });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
protected async selectedCommand(): Promise<ElementHandle<SVGElement | HTMLElement> | null> {
|
|
55
|
+
const command = await this.page.waitForSelector(`#${OPENSUMI_VIEW_CONTAINERS.QUICKPICK}`);
|
|
56
|
+
if (!command) {
|
|
57
|
+
throw new Error('No selected command found!');
|
|
58
|
+
}
|
|
59
|
+
const item = await command.$("[class*='item_selected']");
|
|
60
|
+
return item;
|
|
61
|
+
}
|
|
62
|
+
}
|