@opensumi/playwright 2.21.4 → 2.21.5-next-1668665440.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/debug-view.js +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 +2 -2
- package/lib/editor.d.ts.map +1 -1
- package/lib/editor.js.map +1 -1
- package/lib/explorer-view.d.ts +1 -0
- package/lib/explorer-view.d.ts.map +1 -1
- package/lib/explorer-view.js +30 -9
- package/lib/explorer-view.js.map +1 -1
- package/lib/filetree-view.js +2 -2
- package/lib/filetree-view.js.map +1 -1
- package/lib/opened-editor-view.js +2 -2
- package/lib/opened-editor-view.js.map +1 -1
- 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.js +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/tests/scm.test.js +41 -3
- package/lib/tests/scm.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 +83 -0
- package/lib/tests/settings.test.js.map +1 -0
- package/lib/text-editor.d.ts +2 -2
- package/lib/text-editor.d.ts.map +1 -1
- 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 +3 -3
- package/lib/tree-node.js.map +1 -1
- package/package.json +4 -3
- package/src/app.ts +135 -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 +24 -0
- package/src/diff-editor.ts +3 -0
- package/src/editor.ts +130 -0
- package/src/explorer-view.ts +142 -0
- package/src/filetree-view.ts +28 -0
- package/src/index.ts +19 -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/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 +73 -0
- package/src/search-view.ts +44 -0
- package/src/source-control-view.ts +44 -0
- package/src/terminal.ts +22 -0
- package/src/tests/app.test.ts +16 -0
- package/src/tests/debug.test.ts +68 -0
- package/src/tests/editor.test.ts +143 -0
- package/src/tests/explorer-view.test.ts +202 -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/scm.test.ts +84 -0
- package/src/tests/search-view.test.ts +32 -0
- package/src/tests/settings.test.ts +103 -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.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/text-editor.ts +318 -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 +6 -0
- package/src/view-base.ts +11 -0
- package/src/view.ts +90 -0
- package/src/workspace.ts +36 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { OpenSumiApp } from './app';
|
|
2
|
+
import { OpenSumiView } from './view';
|
|
3
|
+
|
|
4
|
+
export class OpenSumiSourceControlView extends OpenSumiView {
|
|
5
|
+
constructor(app: OpenSumiApp, name: string) {
|
|
6
|
+
super(app, {
|
|
7
|
+
viewSelector: '[data-view-id="scm_view"]',
|
|
8
|
+
tabSelector: '[data-view-id="scm_view"] [tabindex="0"]',
|
|
9
|
+
name,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async getTitleActionByName(name: string) {
|
|
14
|
+
const header = await this.page.$('.scm [class*="titlebar___"]');
|
|
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
|
+
|
|
29
|
+
async getTitleActionById(id: string) {
|
|
30
|
+
const header = await this.page.$('.scm [class*="titlebar___"]');
|
|
31
|
+
if (!header) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
await header.hover();
|
|
35
|
+
const titleAction = await header.waitForSelector('[class*="titleActions___"]');
|
|
36
|
+
const actions = await titleAction.$$('[class*="iconAction__"]');
|
|
37
|
+
for (const action of actions) {
|
|
38
|
+
const itemId = await action.getAttribute('id');
|
|
39
|
+
if (id === itemId) {
|
|
40
|
+
return action;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
package/src/terminal.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { OpenSumiApp } from './app';
|
|
2
|
+
import { OpenSumiPanel } from './panel';
|
|
3
|
+
|
|
4
|
+
export class OpenSumiTerminal extends OpenSumiPanel {
|
|
5
|
+
constructor(app: OpenSumiApp) {
|
|
6
|
+
super(app, 'terminal');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async sendText(text: string) {
|
|
10
|
+
const visible = await this.isVisible();
|
|
11
|
+
if (!visible) {
|
|
12
|
+
await this.open();
|
|
13
|
+
}
|
|
14
|
+
await this.focus();
|
|
15
|
+
const box = await this.view?.boundingBox();
|
|
16
|
+
if (box) {
|
|
17
|
+
await this.app.page.mouse.click(box.x + box?.width / 2, box.y + box?.height / 2);
|
|
18
|
+
}
|
|
19
|
+
await this.page.keyboard.type(text);
|
|
20
|
+
await this.app.page.keyboard.press('Enter');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
import { expect } from '@playwright/test';
|
|
4
|
+
|
|
5
|
+
import { OpenSumiApp } from '../app';
|
|
6
|
+
import { OpenSumiWorkspace } from '../workspace';
|
|
7
|
+
|
|
8
|
+
import test, { page } from './hooks';
|
|
9
|
+
|
|
10
|
+
test.describe('Application', () => {
|
|
11
|
+
test('should show main layout', async () => {
|
|
12
|
+
const workspace = new OpenSumiWorkspace([path.resolve('./src/tests/workspaces/default')]);
|
|
13
|
+
const app = await OpenSumiApp.load(page, workspace);
|
|
14
|
+
expect(await app.isMainLayoutVisible()).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
import { expect } from '@playwright/test';
|
|
4
|
+
|
|
5
|
+
import { OpenSumiApp } from '../app';
|
|
6
|
+
import { OpenSumiDebugView } from '../debug-view';
|
|
7
|
+
import { OpenSumiExplorerView } from '../explorer-view';
|
|
8
|
+
import { OpenSumiTextEditor } from '../text-editor';
|
|
9
|
+
import { OpenSumiWorkspace } from '../workspace';
|
|
10
|
+
|
|
11
|
+
import test, { page } from './hooks';
|
|
12
|
+
|
|
13
|
+
let app: OpenSumiApp;
|
|
14
|
+
let explorer: OpenSumiExplorerView;
|
|
15
|
+
let debugView: OpenSumiDebugView;
|
|
16
|
+
let editor: OpenSumiTextEditor;
|
|
17
|
+
let workspace: OpenSumiWorkspace;
|
|
18
|
+
|
|
19
|
+
test.describe('OpenSumi Debug', () => {
|
|
20
|
+
test.beforeAll(async () => {
|
|
21
|
+
workspace = new OpenSumiWorkspace([path.resolve('./src/tests/workspaces/debug')]);
|
|
22
|
+
app = await OpenSumiApp.load(page, workspace);
|
|
23
|
+
explorer = await app.open(OpenSumiExplorerView);
|
|
24
|
+
explorer.initFileTreeView(workspace.workspace.displayName);
|
|
25
|
+
await explorer.fileTreeView.open();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test.afterAll(() => {
|
|
29
|
+
app.dispose();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('Debug breakpoint editor glyph margin should be worked', async () => {
|
|
33
|
+
editor = await app.openEditor(OpenSumiTextEditor, explorer, 'index.js', false);
|
|
34
|
+
const glyphMarginModel = await editor.getGlyphMarginModel();
|
|
35
|
+
let overlay = await glyphMarginModel.getOverlay(6);
|
|
36
|
+
await overlay?.click({ position: { x: 9, y: 9 }, force: true });
|
|
37
|
+
// 此时元素 dom 结构已经改变,需要重新获取
|
|
38
|
+
overlay = await glyphMarginModel.getOverlay(6);
|
|
39
|
+
expect(await glyphMarginModel.hasBreakpoint(overlay!)).toBeTruthy();
|
|
40
|
+
await editor.close();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('Run Debug should be worked', async () => {
|
|
44
|
+
editor = await app.openEditor(OpenSumiTextEditor, explorer, 'index.js', false);
|
|
45
|
+
await app.page.waitForTimeout(1000);
|
|
46
|
+
|
|
47
|
+
debugView = await app.open(OpenSumiDebugView);
|
|
48
|
+
const glyphMarginModel = await editor.getGlyphMarginModel();
|
|
49
|
+
let glyphOverlay = await glyphMarginModel.getOverlay(6);
|
|
50
|
+
const isClicked = await glyphMarginModel.hasBreakpoint(glyphOverlay!);
|
|
51
|
+
if (!isClicked) {
|
|
52
|
+
await glyphOverlay?.click({ position: { x: 9, y: 9 }, force: true });
|
|
53
|
+
await app.page.waitForTimeout(1000);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await debugView.start();
|
|
57
|
+
await app.page.waitForTimeout(2000);
|
|
58
|
+
|
|
59
|
+
glyphOverlay = await glyphMarginModel.getOverlay(6);
|
|
60
|
+
|
|
61
|
+
expect(await glyphMarginModel.hasTopStackFrame(glyphOverlay!)).toBeTruthy();
|
|
62
|
+
|
|
63
|
+
const overlaysModel = await editor.getOverlaysModel();
|
|
64
|
+
const viewOverlay = await overlaysModel.getOverlay(6);
|
|
65
|
+
expect(await glyphMarginModel.hasTopStackFrameLine(viewOverlay!)).toBeTruthy();
|
|
66
|
+
await editor.close();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
import { expect } from '@playwright/test';
|
|
4
|
+
|
|
5
|
+
import { OpenSumiApp } from '../app';
|
|
6
|
+
import { OPENSUMI_VIEW_CONTAINERS } from '../constans';
|
|
7
|
+
import { OpenSumiExplorerView } from '../explorer-view';
|
|
8
|
+
import { OpenSumiTextEditor } from '../text-editor';
|
|
9
|
+
import { OpenSumiWorkspace } from '../workspace';
|
|
10
|
+
|
|
11
|
+
import test, { page } from './hooks';
|
|
12
|
+
|
|
13
|
+
let app: OpenSumiApp;
|
|
14
|
+
let explorer: OpenSumiExplorerView;
|
|
15
|
+
let editor: OpenSumiTextEditor;
|
|
16
|
+
let workspace: OpenSumiWorkspace;
|
|
17
|
+
|
|
18
|
+
test.describe('OpenSumi Editor', () => {
|
|
19
|
+
test.beforeAll(async () => {
|
|
20
|
+
workspace = new OpenSumiWorkspace([path.resolve('./src/tests/workspaces/default')]);
|
|
21
|
+
app = await OpenSumiApp.load(page, workspace);
|
|
22
|
+
explorer = await app.open(OpenSumiExplorerView);
|
|
23
|
+
explorer.initFileTreeView(workspace.workspace.displayName);
|
|
24
|
+
await explorer.fileTreeView.open();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test.afterAll(() => {
|
|
28
|
+
app.dispose();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('open editor.js on the editor with preview', async () => {
|
|
32
|
+
editor = await app.openEditor(OpenSumiTextEditor, explorer, 'editor.js');
|
|
33
|
+
const isPreview = await editor.isPreview();
|
|
34
|
+
expect(isPreview).toBeTruthy();
|
|
35
|
+
await editor.close();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('open editor.js on the editor without preview', async () => {
|
|
39
|
+
editor = await app.openEditor(OpenSumiTextEditor, explorer, 'editor.js', false);
|
|
40
|
+
const isPreview = await editor.isPreview();
|
|
41
|
+
expect(isPreview).toBeFalsy();
|
|
42
|
+
await editor.close();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('editor dirty status should be update immediately after typing and saving', async () => {
|
|
46
|
+
editor = await app.openEditor(OpenSumiTextEditor, explorer, 'editor.js');
|
|
47
|
+
await editor.addTextToNewLineAfterLineByLineNumber(
|
|
48
|
+
1,
|
|
49
|
+
`const a = 'a';
|
|
50
|
+
console.log(a);`,
|
|
51
|
+
);
|
|
52
|
+
let isDirty = await editor.isDirty();
|
|
53
|
+
expect(isDirty).toBeTruthy();
|
|
54
|
+
await editor.save();
|
|
55
|
+
await app.page.waitForTimeout(2000);
|
|
56
|
+
isDirty = await editor.isDirty();
|
|
57
|
+
expect(isDirty).toBeFalsy();
|
|
58
|
+
await editor.close();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('File tree automatic location', async () => {
|
|
62
|
+
editor = await app.openEditor(OpenSumiTextEditor, explorer, 'editor.js', false);
|
|
63
|
+
const editor2 = await app.openEditor(OpenSumiTextEditor, explorer, 'editor2.js', false);
|
|
64
|
+
await app.page.waitForTimeout(1000);
|
|
65
|
+
const firstFileTab = await editor.getTab();
|
|
66
|
+
await firstFileTab?.click();
|
|
67
|
+
await app.page.waitForTimeout(1000);
|
|
68
|
+
const node = await explorer.getFileStatTreeNodeByPath('editor.js');
|
|
69
|
+
expect(await node?.isSelected()).toBeTruthy();
|
|
70
|
+
const node2 = await explorer.getFileStatTreeNodeByPath('editor2.js');
|
|
71
|
+
expect(await node2?.isSelected()).toBeFalsy();
|
|
72
|
+
await editor.close();
|
|
73
|
+
await editor2.close();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('Close All Editors should be worked', async () => {
|
|
77
|
+
editor = await app.openEditor(OpenSumiTextEditor, explorer, 'editor.js', false);
|
|
78
|
+
await app.openEditor(OpenSumiTextEditor, explorer, 'editor2.js', false);
|
|
79
|
+
await app.page.waitForTimeout(1000);
|
|
80
|
+
expect(await editor.isTabVisible()).toBeTruthy();
|
|
81
|
+
const contextMenu = await editor.openTabContextMenu();
|
|
82
|
+
expect(await contextMenu?.isOpen()).toBeTruthy();
|
|
83
|
+
const closeAll = await contextMenu?.menuItemByName('Close All');
|
|
84
|
+
await closeAll?.click();
|
|
85
|
+
await app.page.waitForTimeout(1000);
|
|
86
|
+
expect(await editor.isTabVisible()).toBeFalsy();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('copy path from file explorer to the editor content', async () => {
|
|
90
|
+
const node = await explorer.getFileStatTreeNodeByPath('editor3.js');
|
|
91
|
+
let fileMenu = await node?.openContextMenu();
|
|
92
|
+
expect(await fileMenu?.isOpen()).toBeTruthy();
|
|
93
|
+
const copyPath = await fileMenu?.menuItemByName('Copy Path');
|
|
94
|
+
await app.page.waitForTimeout(400);
|
|
95
|
+
await copyPath?.click();
|
|
96
|
+
editor = await app.openEditor(OpenSumiTextEditor, explorer, 'editor3.js');
|
|
97
|
+
await editor.addTextToNewLineAfterLineByLineNumber(1, 'File Path: ');
|
|
98
|
+
// cause of https://github.com/microsoft/playwright/issues/8114
|
|
99
|
+
// we can just using keypress to fake the paste feature
|
|
100
|
+
let editorMenu = await editor.openLineContextMenuByLineNumber(2);
|
|
101
|
+
expect(await editorMenu?.isOpen()).toBeTruthy();
|
|
102
|
+
let paste = await editorMenu?.menuItemByName('Paste');
|
|
103
|
+
await paste?.click();
|
|
104
|
+
await app.page.waitForTimeout(200);
|
|
105
|
+
expect(await editor.numberOfLines()).toBe(2);
|
|
106
|
+
expect(
|
|
107
|
+
await editor.textContentOfLineContainingText(
|
|
108
|
+
`File Path: ${workspace.workspace.resolve('editor3.js').codeUri.fsPath.toString()}`,
|
|
109
|
+
),
|
|
110
|
+
).toBeTruthy();
|
|
111
|
+
fileMenu = await node?.openContextMenu();
|
|
112
|
+
const copyRelativePath = await fileMenu?.menuItemByName('Copy Relative Path');
|
|
113
|
+
await copyRelativePath?.click();
|
|
114
|
+
await app.page.waitForTimeout(200);
|
|
115
|
+
await editor.addTextToNewLineAfterLineByLineNumber(2, 'File Relative Path: ');
|
|
116
|
+
editorMenu = await editor.openLineContextMenuByLineNumber(3);
|
|
117
|
+
expect(await editorMenu?.isOpen()).toBeTruthy();
|
|
118
|
+
paste = await editorMenu?.menuItemByName('Paste');
|
|
119
|
+
await paste?.click();
|
|
120
|
+
await app.page.waitForTimeout(200);
|
|
121
|
+
expect(await editor.numberOfLines()).toBe(3);
|
|
122
|
+
expect(await editor.textContentOfLineContainingText('File Relative Path: editor3.js')).toBeTruthy();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('Go to Symbol... should be worked', async () => {
|
|
126
|
+
editor = await app.openEditor(OpenSumiTextEditor, explorer, 'editor2.js');
|
|
127
|
+
// waiting for extHost process done.
|
|
128
|
+
await app.page.waitForTimeout(1000);
|
|
129
|
+
const editorMenu = await editor.openLineContextMenuByLineNumber(1);
|
|
130
|
+
expect(await editorMenu?.isOpen()).toBeTruthy();
|
|
131
|
+
const goto = await editorMenu?.menuItemByName('Go to Symbol...');
|
|
132
|
+
await goto?.click();
|
|
133
|
+
await app.page.waitForTimeout(1000);
|
|
134
|
+
const input = await app.page.waitForSelector(`#${OPENSUMI_VIEW_CONTAINERS.QUICKPICK_INPUT}`);
|
|
135
|
+
await input.focus();
|
|
136
|
+
await app.page.keyboard.press(' ');
|
|
137
|
+
await app.page.keyboard.press('ArrowDown');
|
|
138
|
+
await app.page.keyboard.press('ArrowDown');
|
|
139
|
+
await app.page.keyboard.press('Enter');
|
|
140
|
+
await app.page.keyboard.press('Delete');
|
|
141
|
+
expect(await editor.textContentOfLineContainingText('Person.prototype.getAge = ;')).toBeTruthy();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
import { expect } from '@playwright/test';
|
|
4
|
+
|
|
5
|
+
import { isWindows } from '@opensumi/ide-utils';
|
|
6
|
+
|
|
7
|
+
import { OpenSumiApp } from '../app';
|
|
8
|
+
import { OpenSumiExplorerView } from '../explorer-view';
|
|
9
|
+
import { OpenSumiFileTreeView } from '../filetree-view';
|
|
10
|
+
import { OpenSumiOpenedEditorView } from '../opened-editor-view';
|
|
11
|
+
import { OpenSumiTerminal } from '../terminal';
|
|
12
|
+
import { OpenSumiTextEditor } from '../text-editor';
|
|
13
|
+
import { OpenSumiWorkspace } from '../workspace';
|
|
14
|
+
|
|
15
|
+
import test, { page } from './hooks';
|
|
16
|
+
|
|
17
|
+
let app: OpenSumiApp;
|
|
18
|
+
let explorer: OpenSumiExplorerView;
|
|
19
|
+
let fileTreeView: OpenSumiFileTreeView;
|
|
20
|
+
let openedEditorView: OpenSumiOpenedEditorView;
|
|
21
|
+
let workspace: OpenSumiWorkspace;
|
|
22
|
+
|
|
23
|
+
test.describe('OpenSumi Explorer Panel', () => {
|
|
24
|
+
test.beforeAll(async () => {
|
|
25
|
+
workspace = new OpenSumiWorkspace([path.resolve('./src/tests/workspaces/default')]);
|
|
26
|
+
app = await OpenSumiApp.load(page, workspace);
|
|
27
|
+
explorer = await app.open(OpenSumiExplorerView);
|
|
28
|
+
explorer.initFileTreeView(workspace.workspace.displayName);
|
|
29
|
+
fileTreeView = explorer.fileTreeView;
|
|
30
|
+
openedEditorView = explorer.openedEditorView;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test.afterAll(() => {
|
|
34
|
+
app.dispose();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('should show file explorer', async () => {
|
|
38
|
+
expect(explorer.isVisible()).toBeTruthy();
|
|
39
|
+
await fileTreeView.open();
|
|
40
|
+
expect(await fileTreeView.isVisible()).toBeTruthy();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('can new single file by context menu', async () => {
|
|
44
|
+
const node = await explorer.getFileStatTreeNodeByPath('test');
|
|
45
|
+
await node?.expand();
|
|
46
|
+
expect(await node?.isCollapsed()).toBeFalsy();
|
|
47
|
+
const menu = await node?.openContextMenu();
|
|
48
|
+
expect(await menu?.isOpen()).toBeTruthy();
|
|
49
|
+
const newFileMenu = await menu?.menuItemByIndex(0);
|
|
50
|
+
await newFileMenu?.click();
|
|
51
|
+
// type `new_file` as the file name
|
|
52
|
+
const newFileName = 'new_file';
|
|
53
|
+
const input = await (await fileTreeView.getViewElement())?.waitForSelector('.kt-input-box');
|
|
54
|
+
if (input != null) {
|
|
55
|
+
await input.focus();
|
|
56
|
+
await input.type(newFileName, { delay: 200 });
|
|
57
|
+
await app.page.keyboard.press('Enter');
|
|
58
|
+
}
|
|
59
|
+
await app.page.waitForTimeout(200);
|
|
60
|
+
const newFile = await explorer.getFileStatTreeNodeByPath(`test/${newFileName}`);
|
|
61
|
+
expect(newFile).toBeDefined();
|
|
62
|
+
expect(await newFile?.isFolder()).toBeFalsy();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('can new folder by context menu', async () => {
|
|
66
|
+
const node = await explorer.getFileStatTreeNodeByPath('test');
|
|
67
|
+
await node?.expand();
|
|
68
|
+
expect(await node?.isCollapsed()).toBeFalsy();
|
|
69
|
+
const menu = await node?.openContextMenu();
|
|
70
|
+
expect(await menu?.isOpen()).toBeTruthy();
|
|
71
|
+
const newFileMenu = await menu?.menuItemByName('New Folder');
|
|
72
|
+
await newFileMenu?.click();
|
|
73
|
+
// type `new_file` as the file name
|
|
74
|
+
const newFileName = 'new_folder';
|
|
75
|
+
const input = await (await fileTreeView.getViewElement())?.waitForSelector('.kt-input-box');
|
|
76
|
+
if (input != null) {
|
|
77
|
+
await input.focus();
|
|
78
|
+
await input.type(newFileName, { delay: 200 });
|
|
79
|
+
await app.page.keyboard.press('Enter');
|
|
80
|
+
}
|
|
81
|
+
await app.page.waitForTimeout(200);
|
|
82
|
+
const newFile = await explorer.getFileStatTreeNodeByPath(`test/${newFileName}`);
|
|
83
|
+
expect(newFile).toBeDefined();
|
|
84
|
+
expect(await newFile?.isFolder()).toBeTruthy();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('can new file from toolbar', async () => {
|
|
88
|
+
const node = await explorer.getFileStatTreeNodeByPath('editor.js');
|
|
89
|
+
await node?.open();
|
|
90
|
+
const action = await fileTreeView.getTitleActionByName('New File');
|
|
91
|
+
await action?.click();
|
|
92
|
+
// type `new_file` as the file name
|
|
93
|
+
const newFileName = 'new_file2';
|
|
94
|
+
const input = await (await fileTreeView.getViewElement())?.waitForSelector('.kt-input-box');
|
|
95
|
+
if (input != null) {
|
|
96
|
+
await input.focus();
|
|
97
|
+
await input.type(newFileName, { delay: 200 });
|
|
98
|
+
await app.page.keyboard.press('Enter');
|
|
99
|
+
}
|
|
100
|
+
await app.page.waitForTimeout(200);
|
|
101
|
+
const newFile = await explorer.getFileStatTreeNodeByPath(`${newFileName}`);
|
|
102
|
+
expect(newFile).toBeDefined();
|
|
103
|
+
expect(await newFile?.isFolder()).toBeFalsy();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('can new folder from toolbar', async () => {
|
|
107
|
+
const node = await explorer.getFileStatTreeNodeByPath('editor.js');
|
|
108
|
+
await node?.open();
|
|
109
|
+
const action = await fileTreeView.getTitleActionByName('New Folder');
|
|
110
|
+
await action?.click();
|
|
111
|
+
// type `new_folder2` as the file name
|
|
112
|
+
const newFileName = 'new_folder2';
|
|
113
|
+
const input = await (await fileTreeView.getViewElement())?.waitForSelector('.kt-input-box');
|
|
114
|
+
if (input != null) {
|
|
115
|
+
await input.focus();
|
|
116
|
+
await input.type(newFileName, { delay: 200 });
|
|
117
|
+
await app.page.keyboard.press('Enter');
|
|
118
|
+
}
|
|
119
|
+
await app.page.waitForTimeout(200);
|
|
120
|
+
const newFile = await explorer.getFileStatTreeNodeByPath(`${newFileName}`);
|
|
121
|
+
expect(newFile).toBeDefined();
|
|
122
|
+
expect(await newFile?.isFolder()).toBeTruthy();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
(isWindows ? test.skip : test)('fileTree should be updated while create directory from terminal', async () => {
|
|
126
|
+
const dirname = 'dir_from_terminal';
|
|
127
|
+
const terminal = await app.open(OpenSumiTerminal);
|
|
128
|
+
await terminal.sendText(`cd ${workspace.workspace.codeUri.fsPath}`);
|
|
129
|
+
await terminal.sendText(`mkdir ${dirname}`);
|
|
130
|
+
await app.page.waitForTimeout(2000);
|
|
131
|
+
let newDir = await explorer.getFileStatTreeNodeByPath(dirname);
|
|
132
|
+
if (!newDir) {
|
|
133
|
+
const action = await fileTreeView.getTitleActionByName('Refresh');
|
|
134
|
+
await action?.click();
|
|
135
|
+
await app.page.waitForTimeout(200);
|
|
136
|
+
newDir = await explorer.getFileStatTreeNodeByPath(dirname);
|
|
137
|
+
}
|
|
138
|
+
expect(newDir).toBeDefined();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test('can filter files on the filetree', async () => {
|
|
142
|
+
const action = await fileTreeView.getTitleActionByName('Filter on opened files');
|
|
143
|
+
await action?.click();
|
|
144
|
+
// type `editor2` to filter existed files
|
|
145
|
+
const filterString = 'editor2';
|
|
146
|
+
const input = await (await fileTreeView.getViewElement())?.waitForSelector('.kt-input-box');
|
|
147
|
+
if (input != null) {
|
|
148
|
+
await input.focus();
|
|
149
|
+
await input.type(filterString, { delay: 200 });
|
|
150
|
+
}
|
|
151
|
+
await app.page.waitForTimeout(200);
|
|
152
|
+
const file_1 = await explorer.getFileStatTreeNodeByPath(`${filterString}.js`);
|
|
153
|
+
expect(file_1).toBeDefined();
|
|
154
|
+
let file_2 = await explorer.getFileStatTreeNodeByPath('editor.js');
|
|
155
|
+
expect(file_2).toBeUndefined();
|
|
156
|
+
await app.page.keyboard.press('Escape');
|
|
157
|
+
file_2 = await explorer.getFileStatTreeNodeByPath('editor.js');
|
|
158
|
+
expect(file_2).toBeDefined();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('should show opened files on the opened editor panel', async () => {
|
|
162
|
+
await openedEditorView.open();
|
|
163
|
+
expect(await openedEditorView.isVisible()).toBeTruthy();
|
|
164
|
+
const testFilePath = 'editor.js';
|
|
165
|
+
await app.openEditor(OpenSumiTextEditor, explorer, testFilePath);
|
|
166
|
+
await app.page.waitForTimeout(500);
|
|
167
|
+
const node = await explorer.getOpenedEditorTreeNodeByPath(testFilePath);
|
|
168
|
+
expect(node).toBeDefined();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('should show dirty icon on the opened editor panel', async () => {
|
|
172
|
+
await openedEditorView.open();
|
|
173
|
+
expect(await openedEditorView.isVisible()).toBeTruthy();
|
|
174
|
+
const testFilePath = 'editor3.js';
|
|
175
|
+
const editor = await app.openEditor(OpenSumiTextEditor, explorer, testFilePath);
|
|
176
|
+
await editor.addTextToNewLineAfterLineByLineNumber(
|
|
177
|
+
1,
|
|
178
|
+
`const a = 'a';
|
|
179
|
+
console.log(a);`,
|
|
180
|
+
);
|
|
181
|
+
await app.page.waitForTimeout(1000);
|
|
182
|
+
let node = await explorer.getOpenedEditorTreeNodeByPath(testFilePath);
|
|
183
|
+
expect(await node?.isDirty()).toBeTruthy();
|
|
184
|
+
await editor.save();
|
|
185
|
+
await app.page.waitForTimeout(1000);
|
|
186
|
+
node = await explorer.getOpenedEditorTreeNodeByPath(testFilePath);
|
|
187
|
+
expect(await node?.isDirty()).toBeFalsy();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test('split file on the editor should showing on two group', async () => {
|
|
191
|
+
await openedEditorView.open();
|
|
192
|
+
expect(await openedEditorView.isVisible()).toBeTruthy();
|
|
193
|
+
const testFilePath = 'editor3.js';
|
|
194
|
+
const editor = await app.openEditor(OpenSumiTextEditor, explorer, testFilePath);
|
|
195
|
+
await editor.triggerTitleMenuById('editor.splitToRight');
|
|
196
|
+
await app.page.waitForTimeout(2000);
|
|
197
|
+
const group1 = await explorer.getOpenedEditorTreeNodeByPath('Group 1');
|
|
198
|
+
const group2 = await explorer.getOpenedEditorTreeNodeByPath('Group 2');
|
|
199
|
+
expect(group1).toBeDefined();
|
|
200
|
+
expect(group2).toBeDefined();
|
|
201
|
+
});
|
|
202
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import test, { Page } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
export let page: Page;
|
|
4
|
+
|
|
5
|
+
test.beforeAll(async ({ browser }) => {
|
|
6
|
+
// ref: https://playwright.dev/docs/api/class-browsercontext
|
|
7
|
+
const context = await browser.newContext({
|
|
8
|
+
permissions: ['clipboard-read', 'clipboard-write'],
|
|
9
|
+
});
|
|
10
|
+
page = await context.newPage();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export default test;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
import { expect } from '@playwright/test';
|
|
4
|
+
|
|
5
|
+
import { OpenSumiApp } from '../app';
|
|
6
|
+
import { OpenSumiComponentEditor } from '../component-editor';
|
|
7
|
+
import { OPENSUMI_VIEW_CONTAINERS } from '../constans';
|
|
8
|
+
import { OpenSumiContextMenu } from '../context-menu';
|
|
9
|
+
import { OpenSumiExplorerView } from '../explorer-view';
|
|
10
|
+
import { OpenSumiTextEditor } from '../text-editor';
|
|
11
|
+
import { keypressWithCmdCtrl } from '../utils';
|
|
12
|
+
import { OpenSumiWorkspace } from '../workspace';
|
|
13
|
+
|
|
14
|
+
import test, { page } from './hooks';
|
|
15
|
+
|
|
16
|
+
let app: OpenSumiApp;
|
|
17
|
+
let explorer: OpenSumiExplorerView;
|
|
18
|
+
let workspace: OpenSumiWorkspace;
|
|
19
|
+
|
|
20
|
+
test.describe('OpenSumi Keyboard Shortcuts', () => {
|
|
21
|
+
test.beforeAll(async () => {
|
|
22
|
+
workspace = new OpenSumiWorkspace([path.resolve('./src/tests/workspaces/default')]);
|
|
23
|
+
app = await OpenSumiApp.load(page, workspace);
|
|
24
|
+
explorer = await app.open(OpenSumiExplorerView);
|
|
25
|
+
explorer.initFileTreeView(workspace.workspace.displayName);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test.afterAll(() => {
|
|
29
|
+
app.dispose();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const openKeymapsView = async () => {
|
|
33
|
+
const leftTabbar = await app.page.waitForSelector(`#${OPENSUMI_VIEW_CONTAINERS.LEFT_TABBAR}`);
|
|
34
|
+
const settingsButton = await leftTabbar.$('[class*="titleActions___"] span');
|
|
35
|
+
await settingsButton?.click();
|
|
36
|
+
const menu = new OpenSumiContextMenu(app);
|
|
37
|
+
await menu.clickMenuItem('Keyboard Shortcut');
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
test('open Keyboard Settings by keybinding', async () => {
|
|
41
|
+
await explorer.fileTreeView.focus();
|
|
42
|
+
await app.page.keyboard.press(keypressWithCmdCtrl('KeyK'), { delay: 200 });
|
|
43
|
+
await app.page.keyboard.press(keypressWithCmdCtrl('KeyS'), { delay: 200 });
|
|
44
|
+
const editor = await app.openComponentEditor(
|
|
45
|
+
OpenSumiComponentEditor,
|
|
46
|
+
'keymaps:/',
|
|
47
|
+
'Keyboard Shortcuts',
|
|
48
|
+
"[class*='keybinding_container___']",
|
|
49
|
+
);
|
|
50
|
+
expect(await editor.isVisible()).toBeTruthy();
|
|
51
|
+
await editor.close();
|
|
52
|
+
await app.page.waitForTimeout(1000);
|
|
53
|
+
expect(await editor.isVisible()).toBeFalsy();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('open Keyboard Settings by settings button', async () => {
|
|
57
|
+
await openKeymapsView();
|
|
58
|
+
const editor = await app.openComponentEditor(
|
|
59
|
+
OpenSumiComponentEditor,
|
|
60
|
+
'keymaps:/',
|
|
61
|
+
'Keyboard Shortcuts',
|
|
62
|
+
"[class*='keybinding_container___']",
|
|
63
|
+
);
|
|
64
|
+
expect(await editor.isVisible()).toBeTruthy();
|
|
65
|
+
await editor.close();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("change 'Save Current File' keybinding to 'CMD+SHIFT+S'", async () => {
|
|
69
|
+
await openKeymapsView();
|
|
70
|
+
const editor = await app.openComponentEditor(
|
|
71
|
+
OpenSumiComponentEditor,
|
|
72
|
+
'keymaps:/',
|
|
73
|
+
'Keyboard Shortcuts',
|
|
74
|
+
"[class*='keybinding_container___']",
|
|
75
|
+
);
|
|
76
|
+
expect(await editor.isVisible()).toBeTruthy();
|
|
77
|
+
const searchArea = await (await editor.getContainer())?.$('[class*="search_input___"]');
|
|
78
|
+
const keyboardButton = await searchArea?.$('[class*="search_inline_action___"] span');
|
|
79
|
+
const searchInput = await searchArea?.$('.kt-input-box input');
|
|
80
|
+
await keyboardButton?.click();
|
|
81
|
+
await searchInput?.focus();
|
|
82
|
+
// type 'CMD+S' to filter 'Save Current File' command
|
|
83
|
+
await app.page.keyboard.press(keypressWithCmdCtrl('KeyS'), { delay: 200 });
|
|
84
|
+
|
|
85
|
+
const items = (await (await editor.getContainer())?.$$('[class*="keybinding_list_item__"]')) || [];
|
|
86
|
+
let saveFileItem;
|
|
87
|
+
for (const item of items) {
|
|
88
|
+
const command_name = await (await item.$('[class*="command_name___"]'))?.textContent();
|
|
89
|
+
if (command_name === 'Save Current File') {
|
|
90
|
+
saveFileItem = item;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
expect(saveFileItem).toBeDefined();
|
|
95
|
+
await saveFileItem?.hover();
|
|
96
|
+
const edit = await saveFileItem.$('[title="Edit"]');
|
|
97
|
+
await edit?.click();
|
|
98
|
+
await app.page.keyboard.press(keypressWithCmdCtrl('Shift+KeyS'), { delay: 200 });
|
|
99
|
+
await app.page.keyboard.press('Enter');
|
|
100
|
+
// varified file save keybinding
|
|
101
|
+
const textEditor = await app.openEditor(OpenSumiTextEditor, explorer, 'editor.js');
|
|
102
|
+
await textEditor.addTextToNewLineAfterLineByLineNumber(
|
|
103
|
+
1,
|
|
104
|
+
`const a = 'a';
|
|
105
|
+
console.log(a);`,
|
|
106
|
+
);
|
|
107
|
+
let isDirty = await textEditor.isDirty();
|
|
108
|
+
expect(isDirty).toBeTruthy();
|
|
109
|
+
await app.page.keyboard.press(keypressWithCmdCtrl('KeyS'), { delay: 200 });
|
|
110
|
+
await app.page.waitForTimeout(1000);
|
|
111
|
+
isDirty = await textEditor.isDirty();
|
|
112
|
+
expect(isDirty).toBeTruthy();
|
|
113
|
+
await app.page.keyboard.press(keypressWithCmdCtrl('Shift+KeyS'), { delay: 200 });
|
|
114
|
+
await app.page.waitForTimeout(1000);
|
|
115
|
+
isDirty = await textEditor.isDirty();
|
|
116
|
+
expect(isDirty).toBeFalsy();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
import { expect } from '@playwright/test';
|
|
4
|
+
|
|
5
|
+
import { isMacintosh } from '@opensumi/ide-utils';
|
|
6
|
+
|
|
7
|
+
import { OpenSumiApp } from '../app';
|
|
8
|
+
import { OpenSumiExplorerView } from '../explorer-view';
|
|
9
|
+
import { OpenSumiTextEditor } from '../text-editor';
|
|
10
|
+
import { OpenSumiWorkspace } from '../workspace';
|
|
11
|
+
|
|
12
|
+
import test, { page } from './hooks';
|
|
13
|
+
|
|
14
|
+
let app: OpenSumiApp;
|
|
15
|
+
let explorer: OpenSumiExplorerView;
|
|
16
|
+
let editor: OpenSumiTextEditor;
|
|
17
|
+
let workspace: OpenSumiWorkspace;
|
|
18
|
+
|
|
19
|
+
test.describe('OpenSumi Language', () => {
|
|
20
|
+
test.beforeAll(async () => {
|
|
21
|
+
workspace = new OpenSumiWorkspace([path.resolve('./src/tests/workspaces/language')]);
|
|
22
|
+
app = await OpenSumiApp.load(page, workspace);
|
|
23
|
+
explorer = await app.open(OpenSumiExplorerView);
|
|
24
|
+
explorer.initFileTreeView(workspace.workspace.displayName);
|
|
25
|
+
await explorer.fileTreeView.open();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test.afterAll(() => {
|
|
29
|
+
app.dispose();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('Go to Defination by cmd + click', async () => {
|
|
33
|
+
const folder = await explorer.getFileStatTreeNodeByPath('language');
|
|
34
|
+
await folder?.open();
|
|
35
|
+
|
|
36
|
+
editor = await app.openEditor(OpenSumiTextEditor, explorer, 'reference.ts', false);
|
|
37
|
+
await editor.activate();
|
|
38
|
+
await app.page.waitForTimeout(2000);
|
|
39
|
+
await editor.placeCursorInLineWithPosition(4, 20);
|
|
40
|
+
let cursorHandle = await editor.getCursorElement();
|
|
41
|
+
await cursorHandle?.click({ modifiers: [isMacintosh ? 'Meta' : 'Control'] });
|
|
42
|
+
await app.page.waitForTimeout(2000);
|
|
43
|
+
const definitionTree = await explorer.getFileStatTreeNodeByPath('definition.ts');
|
|
44
|
+
expect(await definitionTree?.isSelected()).toBeTruthy();
|
|
45
|
+
const currentTab = await editor.getCurrentTab();
|
|
46
|
+
expect(await currentTab?.textContent()).toStrictEqual(' definition.ts');
|
|
47
|
+
|
|
48
|
+
cursorHandle = await editor.getCursorElement();
|
|
49
|
+
const cursorLineNumber = await editor.getCursorLineNumber(cursorHandle?.asElement());
|
|
50
|
+
expect(cursorLineNumber).toBe(1);
|
|
51
|
+
expect(await editor.textContentOfLineByLineNumber(cursorLineNumber!)).toBe('export class Definition {');
|
|
52
|
+
|
|
53
|
+
await editor.close();
|
|
54
|
+
});
|
|
55
|
+
});
|