@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.
Files changed (155) hide show
  1. package/lib/app.d.ts +2 -1
  2. package/lib/app.d.ts.map +1 -1
  3. package/lib/app.js +21 -9
  4. package/lib/app.js.map +1 -1
  5. package/lib/component-editor.d.ts +2 -2
  6. package/lib/debug-view.d.ts +10 -0
  7. package/lib/debug-view.d.ts.map +1 -1
  8. package/lib/debug-view.js +41 -1
  9. package/lib/debug-view.js.map +1 -1
  10. package/lib/diff-editor.d.ts +4 -0
  11. package/lib/diff-editor.d.ts.map +1 -0
  12. package/lib/diff-editor.js +8 -0
  13. package/lib/diff-editor.js.map +1 -0
  14. package/lib/editor.d.ts +6 -5
  15. package/lib/editor.d.ts.map +1 -1
  16. package/lib/editor.js +6 -2
  17. package/lib/editor.js.map +1 -1
  18. package/lib/explorer-view.d.ts +4 -0
  19. package/lib/explorer-view.d.ts.map +1 -1
  20. package/lib/explorer-view.js +36 -10
  21. package/lib/explorer-view.js.map +1 -1
  22. package/lib/filetree-view.d.ts +1 -1
  23. package/lib/filetree-view.js +2 -2
  24. package/lib/filetree-view.js.map +1 -1
  25. package/lib/index.d.ts +2 -1
  26. package/lib/index.d.ts.map +1 -1
  27. package/lib/index.js +2 -1
  28. package/lib/index.js.map +1 -1
  29. package/lib/menu.d.ts +1 -1
  30. package/lib/opened-editor-view.d.ts +2 -2
  31. package/lib/opened-editor-view.d.ts.map +1 -1
  32. package/lib/opened-editor-view.js +4 -4
  33. package/lib/opened-editor-view.js.map +1 -1
  34. package/lib/outline-view.d.ts +9 -0
  35. package/lib/outline-view.d.ts.map +1 -0
  36. package/lib/outline-view.js +38 -0
  37. package/lib/outline-view.js.map +1 -0
  38. package/lib/output-view.d.ts +10 -0
  39. package/lib/output-view.d.ts.map +1 -0
  40. package/lib/output-view.js +78 -0
  41. package/lib/output-view.js.map +1 -0
  42. package/lib/panel.js +2 -2
  43. package/lib/panel.js.map +1 -1
  44. package/lib/scm-view.d.ts +21 -0
  45. package/lib/scm-view.d.ts.map +1 -0
  46. package/lib/scm-view.js +65 -0
  47. package/lib/scm-view.js.map +1 -0
  48. package/lib/search-view.d.ts +34 -0
  49. package/lib/search-view.d.ts.map +1 -1
  50. package/lib/search-view.js +196 -4
  51. package/lib/search-view.js.map +1 -1
  52. package/lib/source-control-view.d.ts +8 -0
  53. package/lib/source-control-view.d.ts.map +1 -0
  54. package/lib/source-control-view.js +45 -0
  55. package/lib/source-control-view.js.map +1 -0
  56. package/lib/terminal-view.d.ts +10 -0
  57. package/lib/terminal-view.d.ts.map +1 -0
  58. package/lib/terminal-view.js +50 -0
  59. package/lib/terminal-view.js.map +1 -0
  60. package/lib/tests/debug.test.js +49 -0
  61. package/lib/tests/debug.test.js.map +1 -1
  62. package/lib/tests/editor/undoRedo.test.d.ts +2 -0
  63. package/lib/tests/editor/undoRedo.test.d.ts.map +1 -0
  64. package/lib/tests/editor/undoRedo.test.js +52 -0
  65. package/lib/tests/editor/undoRedo.test.js.map +1 -0
  66. package/lib/tests/editor.test.js +0 -2
  67. package/lib/tests/editor.test.js.map +1 -1
  68. package/lib/tests/explorer-view.test.js +129 -5
  69. package/lib/tests/explorer-view.test.js.map +1 -1
  70. package/lib/tests/output.test.d.ts +2 -0
  71. package/lib/tests/output.test.d.ts.map +1 -0
  72. package/lib/tests/output.test.js +76 -0
  73. package/lib/tests/output.test.js.map +1 -0
  74. package/lib/tests/scm.test.js +42 -4
  75. package/lib/tests/scm.test.js.map +1 -1
  76. package/lib/tests/search-view.test.js +191 -8
  77. package/lib/tests/search-view.test.js.map +1 -1
  78. package/lib/tests/settings.test.d.ts +2 -0
  79. package/lib/tests/settings.test.d.ts.map +1 -0
  80. package/lib/tests/settings.test.js +95 -0
  81. package/lib/tests/settings.test.js.map +1 -0
  82. package/lib/text-editor.d.ts +7 -3
  83. package/lib/text-editor.d.ts.map +1 -1
  84. package/lib/text-editor.js +15 -0
  85. package/lib/text-editor.js.map +1 -1
  86. package/lib/tree-node.d.ts +3 -1
  87. package/lib/tree-node.d.ts.map +1 -1
  88. package/lib/tree-node.js +4 -4
  89. package/lib/tree-node.js.map +1 -1
  90. package/lib/utils/key.d.ts +1 -0
  91. package/lib/utils/key.d.ts.map +1 -1
  92. package/lib/utils/key.js +6 -1
  93. package/lib/utils/key.js.map +1 -1
  94. package/lib/view.js +1 -1
  95. package/lib/view.js.map +1 -1
  96. package/package.json +12 -11
  97. package/src/app.ts +148 -0
  98. package/src/component-editor.ts +64 -0
  99. package/src/constans/index.ts +18 -0
  100. package/src/context-menu.ts +27 -0
  101. package/src/debug-view.ts +73 -0
  102. package/src/diff-editor.ts +3 -0
  103. package/src/editor.ts +135 -0
  104. package/src/explorer-view.ts +149 -0
  105. package/src/filetree-view.ts +28 -0
  106. package/src/index.ts +20 -0
  107. package/src/menu-item.ts +40 -0
  108. package/src/menu.ts +69 -0
  109. package/src/menubar.ts +53 -0
  110. package/src/opened-editor-view.ts +28 -0
  111. package/src/outline-view.ts +37 -0
  112. package/src/output-view.ts +76 -0
  113. package/src/panel.ts +50 -0
  114. package/src/quick-command-palette.ts +62 -0
  115. package/src/quick-open-palette.ts +62 -0
  116. package/src/scm-view.ts +72 -0
  117. package/src/search-view.ts +260 -0
  118. package/src/source-control-view.ts +44 -0
  119. package/src/terminal-view.ts +50 -0
  120. package/src/tests/app.test.ts +16 -0
  121. package/src/tests/debug.test.ts +121 -0
  122. package/src/tests/editor/undoRedo.test.ts +55 -0
  123. package/src/tests/editor.test.ts +141 -0
  124. package/src/tests/explorer-view.test.ts +329 -0
  125. package/src/tests/hooks/index.ts +13 -0
  126. package/src/tests/keymaps.test.ts +118 -0
  127. package/src/tests/language.test.ts +55 -0
  128. package/src/tests/output.test.ts +87 -0
  129. package/src/tests/scm.test.ts +84 -0
  130. package/src/tests/search-view.test.ts +239 -0
  131. package/src/tests/settings.test.ts +115 -0
  132. package/src/tests/workspaces/debug/.sumi/launch.json +15 -0
  133. package/src/tests/workspaces/debug/index.js +18 -0
  134. package/src/tests/workspaces/default/editor-undo-redo.text +0 -0
  135. package/src/tests/workspaces/default/editor.js +0 -0
  136. package/src/tests/workspaces/default/editor2.js +87 -0
  137. package/src/tests/workspaces/default/editor3.js +0 -0
  138. package/src/tests/workspaces/default/test/test.js +1 -0
  139. package/src/tests/workspaces/git-workspace/a.js +0 -0
  140. package/src/tests/workspaces/language/definition.ts +12 -0
  141. package/src/tests/workspaces/language/reference.ts +9 -0
  142. package/src/tests/workspaces/search/index.js +5 -0
  143. package/src/tests/workspaces/search/index2.js +1 -0
  144. package/src/text-editor.ts +333 -0
  145. package/src/tree-node.ts +98 -0
  146. package/src/utils/element.ts +35 -0
  147. package/src/utils/index.ts +2 -0
  148. package/src/utils/key.ts +11 -0
  149. package/src/view-base.ts +11 -0
  150. package/src/view.ts +90 -0
  151. package/src/workspace.ts +36 -0
  152. package/lib/terminal.d.ts +0 -7
  153. package/lib/terminal.d.ts.map +0 -1
  154. package/lib/terminal.js +0 -25
  155. package/lib/terminal.js.map +0 -1
@@ -0,0 +1,141 @@
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
+ let editorMenu = await editor.openLineContextMenuByLineNumber(2);
99
+ expect(await editorMenu?.isOpen()).toBeTruthy();
100
+ let paste = await editorMenu?.menuItemByName('Paste');
101
+ await paste?.click();
102
+ await app.page.waitForTimeout(200);
103
+ expect(await editor.numberOfLines()).toBe(2);
104
+ expect(
105
+ await editor.textContentOfLineContainingText(
106
+ `File Path: ${workspace.workspace.resolve('editor3.js').codeUri.fsPath.toString()}`,
107
+ ),
108
+ ).toBeTruthy();
109
+ fileMenu = await node?.openContextMenu();
110
+ const copyRelativePath = await fileMenu?.menuItemByName('Copy Relative Path');
111
+ await copyRelativePath?.click();
112
+ await app.page.waitForTimeout(200);
113
+ await editor.addTextToNewLineAfterLineByLineNumber(2, 'File Relative Path: ');
114
+ editorMenu = await editor.openLineContextMenuByLineNumber(3);
115
+ expect(await editorMenu?.isOpen()).toBeTruthy();
116
+ paste = await editorMenu?.menuItemByName('Paste');
117
+ await paste?.click();
118
+ await app.page.waitForTimeout(200);
119
+ expect(await editor.numberOfLines()).toBe(3);
120
+ expect(await editor.textContentOfLineContainingText('File Relative Path: editor3.js')).toBeTruthy();
121
+ });
122
+
123
+ test('Go to Symbol... should be worked', async () => {
124
+ editor = await app.openEditor(OpenSumiTextEditor, explorer, 'editor2.js');
125
+ // waiting for extHost process done.
126
+ await app.page.waitForTimeout(1000);
127
+ const editorMenu = await editor.openLineContextMenuByLineNumber(1);
128
+ expect(await editorMenu?.isOpen()).toBeTruthy();
129
+ const goto = await editorMenu?.menuItemByName('Go to Symbol...');
130
+ await goto?.click();
131
+ await app.page.waitForTimeout(1000);
132
+ const input = await app.page.waitForSelector(`#${OPENSUMI_VIEW_CONTAINERS.QUICKPICK_INPUT}`);
133
+ await input.focus();
134
+ await app.page.keyboard.press(' ');
135
+ await app.page.keyboard.press('ArrowDown');
136
+ await app.page.keyboard.press('ArrowDown');
137
+ await app.page.keyboard.press('Enter');
138
+ await app.page.keyboard.press('Delete');
139
+ expect(await editor.textContentOfLineContainingText('Person.prototype.getAge = ;')).toBeTruthy();
140
+ });
141
+ });
@@ -0,0 +1,329 @@
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 { OpenSumiOutlineView } from '../outline-view';
12
+ import { OpenSumiTerminalView } from '../terminal-view';
13
+ import { OpenSumiTextEditor } from '../text-editor';
14
+ import { OpenSumiWorkspace } from '../workspace';
15
+
16
+ import test, { page } from './hooks';
17
+
18
+ let app: OpenSumiApp;
19
+ let explorer: OpenSumiExplorerView;
20
+ let fileTreeView: OpenSumiFileTreeView;
21
+ let openedEditorView: OpenSumiOpenedEditorView;
22
+ let outlineView: OpenSumiOutlineView;
23
+ let workspace: OpenSumiWorkspace;
24
+
25
+ test.describe('OpenSumi Explorer Panel', () => {
26
+ test.beforeAll(async () => {
27
+ workspace = new OpenSumiWorkspace([path.resolve('./src/tests/workspaces/default')]);
28
+ app = await OpenSumiApp.load(page, workspace);
29
+ explorer = await app.open(OpenSumiExplorerView);
30
+ explorer.initFileTreeView(workspace.workspace.displayName);
31
+ fileTreeView = explorer.fileTreeView;
32
+ outlineView = explorer.outlineView;
33
+ openedEditorView = explorer.openedEditorView;
34
+ });
35
+
36
+ test.afterAll(() => {
37
+ app.dispose();
38
+ });
39
+
40
+ test('should show file explorer', async () => {
41
+ expect(await explorer.isVisible()).toBeTruthy();
42
+ await fileTreeView.open();
43
+ expect(await fileTreeView.isVisible()).toBeTruthy();
44
+ });
45
+
46
+ test('can new single file by context menu', async () => {
47
+ const node = await explorer.getFileStatTreeNodeByPath('test');
48
+ await node?.expand();
49
+ expect(await node?.isCollapsed()).toBeFalsy();
50
+ const menu = await node?.openContextMenu();
51
+ expect(await menu?.isOpen()).toBeTruthy();
52
+ const newFileMenu = await menu?.menuItemByIndex(0);
53
+ await newFileMenu?.click();
54
+ // type `new_file` as the file name
55
+ const newFileName = 'new_file';
56
+ const input = await (await fileTreeView.getViewElement())?.waitForSelector('.kt-input-box');
57
+ if (input != null) {
58
+ await input.focus();
59
+ await input.type(newFileName, { delay: 200 });
60
+ await app.page.keyboard.press('Enter');
61
+ }
62
+ await app.page.waitForTimeout(200);
63
+ const newFile = await explorer.getFileStatTreeNodeByPath(`test/${newFileName}`);
64
+ expect(newFile).toBeDefined();
65
+ expect(await newFile?.isFolder()).toBeFalsy();
66
+ });
67
+
68
+ test('can new folder by context menu', async () => {
69
+ const node = await explorer.getFileStatTreeNodeByPath('test');
70
+ await node?.expand();
71
+ expect(await node?.isCollapsed()).toBeFalsy();
72
+ const menu = await node?.openContextMenu();
73
+ expect(await menu?.isOpen()).toBeTruthy();
74
+ const newFileMenu = await menu?.menuItemByName('New Folder');
75
+ await newFileMenu?.click();
76
+ // type `new_file` as the file name
77
+ const newFileName = 'new_folder';
78
+ const input = await (await fileTreeView.getViewElement())?.waitForSelector('.kt-input-box');
79
+ if (input != null) {
80
+ await input.focus();
81
+ await input.type(newFileName, { delay: 200 });
82
+ await app.page.keyboard.press('Enter');
83
+ }
84
+ await app.page.waitForTimeout(200);
85
+ const newFile = await explorer.getFileStatTreeNodeByPath(`test/${newFileName}`);
86
+ expect(newFile).toBeDefined();
87
+ expect(await newFile?.isFolder()).toBeTruthy();
88
+ });
89
+
90
+ test('can new file from toolbar', async () => {
91
+ const node = await explorer.getFileStatTreeNodeByPath('editor.js');
92
+ await node?.open();
93
+ const action = await fileTreeView.getTitleActionByName('New File');
94
+ await action?.click();
95
+ // type `new_file` as the file name
96
+ const newFileName = 'new_file2';
97
+ const input = await (await fileTreeView.getViewElement())?.waitForSelector('.kt-input-box');
98
+ if (input != null) {
99
+ await input.focus();
100
+ await input.type(newFileName, { delay: 200 });
101
+ await app.page.keyboard.press('Enter');
102
+ }
103
+ await app.page.waitForTimeout(200);
104
+ const newFile = await explorer.getFileStatTreeNodeByPath(`${newFileName}`);
105
+ expect(newFile).toBeDefined();
106
+ expect(await newFile?.isFolder()).toBeFalsy();
107
+ });
108
+
109
+ test('can new folder from toolbar', async () => {
110
+ const node = await explorer.getFileStatTreeNodeByPath('editor.js');
111
+ await node?.open();
112
+ const action = await fileTreeView.getTitleActionByName('New Folder');
113
+ await action?.click();
114
+ // type `new_folder2` as the file name
115
+ const newFileName = 'new_folder2';
116
+ const input = await (await fileTreeView.getViewElement())?.waitForSelector('.kt-input-box');
117
+ if (input != null) {
118
+ await input.focus();
119
+ await input.type(newFileName, { delay: 200 });
120
+ await app.page.keyboard.press('Enter');
121
+ }
122
+ await app.page.waitForTimeout(200);
123
+ const newFile = await explorer.getFileStatTreeNodeByPath(`${newFileName}`);
124
+ expect(newFile).toBeDefined();
125
+ expect(await newFile?.isFolder()).toBeTruthy();
126
+ });
127
+
128
+ (isWindows ? test.skip : test)('fileTree should be updated while create directory from terminal', async () => {
129
+ const dirname = 'dir_from_terminal';
130
+ const terminal = await app.open(OpenSumiTerminalView);
131
+ await terminal.sendText(`cd ${workspace.workspace.codeUri.fsPath}`);
132
+ await terminal.sendText(`mkdir ${dirname}`);
133
+ await app.page.waitForTimeout(2000);
134
+ let newDir = await explorer.getFileStatTreeNodeByPath(dirname);
135
+ if (!newDir) {
136
+ const action = await fileTreeView.getTitleActionByName('Refresh');
137
+ await action?.click();
138
+ await app.page.waitForTimeout(200);
139
+ newDir = await explorer.getFileStatTreeNodeByPath(dirname);
140
+ }
141
+ expect(newDir).toBeDefined();
142
+ });
143
+
144
+ test('can filter files on the filetree', async () => {
145
+ const action = await fileTreeView.getTitleActionByName('Filter on opened files');
146
+ await action?.click();
147
+ // type `editor2` to filter existed files
148
+ const filterString = 'editor2';
149
+ const input = await (await fileTreeView.getViewElement())?.waitForSelector('.kt-input-box');
150
+ if (input != null) {
151
+ await input.focus();
152
+ await input.type(filterString, { delay: 200 });
153
+ }
154
+ await app.page.waitForTimeout(200);
155
+ const file_1 = await explorer.getFileStatTreeNodeByPath(`${filterString}.js`);
156
+ expect(file_1).toBeDefined();
157
+ let file_2 = await explorer.getFileStatTreeNodeByPath('editor.js');
158
+ expect(file_2).toBeUndefined();
159
+ await app.page.keyboard.press('Escape');
160
+ file_2 = await explorer.getFileStatTreeNodeByPath('editor.js');
161
+ expect(file_2).toBeDefined();
162
+ });
163
+
164
+ test('should show opened files on the opened editor panel', async () => {
165
+ await openedEditorView.open();
166
+ expect(await openedEditorView.isVisible()).toBeTruthy();
167
+ const testFilePath = 'editor.js';
168
+ await app.openEditor(OpenSumiTextEditor, explorer, testFilePath);
169
+ await app.page.waitForTimeout(500);
170
+ const node = await explorer.getOpenedEditorTreeNodeByPath(testFilePath);
171
+ expect(node).toBeDefined();
172
+ });
173
+
174
+ test('should show dirty icon on the opened editor panel', async () => {
175
+ await openedEditorView.open();
176
+ expect(await openedEditorView.isVisible()).toBeTruthy();
177
+ const testFilePath = 'editor3.js';
178
+ const editor = await app.openEditor(OpenSumiTextEditor, explorer, testFilePath);
179
+ await editor.addTextToNewLineAfterLineByLineNumber(
180
+ 1,
181
+ `const a = 'a';
182
+ console.log(a);`,
183
+ );
184
+ await app.page.waitForTimeout(1000);
185
+ let node = await explorer.getOpenedEditorTreeNodeByPath(testFilePath);
186
+ expect(await node?.isDirty()).toBeTruthy();
187
+ await editor.save();
188
+ await app.page.waitForTimeout(1000);
189
+ node = await explorer.getOpenedEditorTreeNodeByPath(testFilePath);
190
+ expect(await node?.isDirty()).toBeFalsy();
191
+ });
192
+
193
+ test('the open state of the editor should be restored after refreshing', async () => {
194
+ await openedEditorView.open();
195
+ const testFilePath_1 = 'editor2.js';
196
+ const testFilePath_2 = 'editor3.js';
197
+ // Close All Edtior Tabs
198
+ const editor = await app.openEditor(OpenSumiTextEditor, explorer, testFilePath_1);
199
+ await app.page.waitForTimeout(1000);
200
+ let node = await explorer.getOpenedEditorTreeNodeByPath(testFilePath_1);
201
+ expect(node).toBeDefined();
202
+ const contextMenu = await editor.openTabContextMenu();
203
+ expect(await contextMenu?.isOpen()).toBeTruthy();
204
+ const closeAll = await contextMenu?.menuItemByName('Close All');
205
+ await closeAll?.click();
206
+ await app.page.waitForTimeout(1000);
207
+ node = await explorer.getOpenedEditorTreeNodeByPath(testFilePath_1);
208
+ expect(node).toBeUndefined();
209
+ // Open File
210
+ await app.openEditor(OpenSumiTextEditor, explorer, testFilePath_1, false);
211
+ await app.openEditor(OpenSumiTextEditor, explorer, testFilePath_2, false);
212
+ await app.page.waitForTimeout(1000);
213
+ node = await explorer.getOpenedEditorTreeNodeByPath(testFilePath_1);
214
+ expect(node).toBeDefined();
215
+ node = await explorer.getOpenedEditorTreeNodeByPath(testFilePath_2);
216
+ expect(node).toBeDefined();
217
+ await app.page.reload();
218
+ await app.page.waitForTimeout(2000);
219
+ node = await explorer.getOpenedEditorTreeNodeByPath(testFilePath_1);
220
+ expect(node).toBeDefined();
221
+ node = await explorer.getOpenedEditorTreeNodeByPath(testFilePath_2);
222
+ expect(node).toBeDefined();
223
+ });
224
+
225
+ test('split file on the editor should showing on two group', async () => {
226
+ await openedEditorView.open();
227
+ expect(await openedEditorView.isVisible()).toBeTruthy();
228
+ const testFilePath = 'editor3.js';
229
+ const editor = await app.openEditor(OpenSumiTextEditor, explorer, testFilePath);
230
+ await editor.triggerTitleMenuById('editor.splitToRight');
231
+ await app.page.waitForTimeout(2000);
232
+ const group1 = await explorer.getOpenedEditorTreeNodeByPath('GROUP 1');
233
+ const group2 = await explorer.getOpenedEditorTreeNodeByPath('GROUP 2');
234
+ expect(group1).toBeDefined();
235
+ expect(group2).toBeDefined();
236
+ });
237
+
238
+ test('create file with path', async () => {
239
+ await fileTreeView.open();
240
+ const node = await explorer.getFileStatTreeNodeByPath('test');
241
+ await node?.expand();
242
+ expect(await node?.isCollapsed()).toBeFalsy();
243
+ let menu = await node?.openContextMenu();
244
+ expect(await menu?.isOpen()).toBeTruthy();
245
+ let newFileMenu = await menu?.menuItemByName('New File');
246
+ await newFileMenu?.click();
247
+ // type `index.ts` as the file name
248
+ let newFileName = 'index.ts';
249
+ let input = await (await fileTreeView.getViewElement())?.waitForSelector('.kt-input-box');
250
+ if (input != null) {
251
+ await input.focus();
252
+ await input.type(newFileName, { delay: 200 });
253
+ await app.page.keyboard.press('Enter');
254
+ }
255
+ await app.page.waitForTimeout(200);
256
+ const newFile = await explorer.getFileStatTreeNodeByPath(`test/${newFileName}`);
257
+ expect(newFile).toBeDefined();
258
+ expect(await newFile?.isFolder()).toBeFalsy();
259
+ // new compress node by path
260
+ menu = await node?.openContextMenu();
261
+ newFileMenu = await menu?.menuItemByName('New File');
262
+ await newFileMenu?.click();
263
+ // type `a/b/c.js` as the file name
264
+ newFileName = 'a/b/c.js';
265
+ input = await (await fileTreeView.getViewElement())?.waitForSelector('.kt-input-box');
266
+ if (input != null) {
267
+ await input.focus();
268
+ await input.type(newFileName, { delay: 200 });
269
+ await app.page.keyboard.press('Enter');
270
+ }
271
+ await app.page.waitForTimeout(1000);
272
+ // |- test
273
+ // |----a/b
274
+ const nodeA = await explorer.getFileStatTreeNodeByPath('test/a');
275
+ await nodeA?.expand();
276
+ await app.page.waitForTimeout(2000);
277
+ expect(await nodeA?.isCollapsed()).toBeFalsy();
278
+ const compressNode = await explorer.getFileStatTreeNodeByPath('test/a/b');
279
+ expect(compressNode).toBeDefined();
280
+ expect(await compressNode?.label()).toBe('a/b');
281
+ menu = await node?.openContextMenu();
282
+ newFileMenu = await menu?.menuItemByName('New File');
283
+ await newFileMenu?.click();
284
+ // type `a/d/c.js` as the file name
285
+ newFileName = 'a/d/c.js';
286
+ input = await (await fileTreeView.getViewElement())?.waitForSelector('.kt-input-box');
287
+ if (input != null) {
288
+ await input.focus();
289
+ await input.type(newFileName, { delay: 200 });
290
+ await app.page.keyboard.press('Enter');
291
+ }
292
+ await app.page.waitForTimeout(2000);
293
+ // |- test
294
+ // |----a
295
+ // |------b
296
+ // |------d
297
+ const uncompressNode = await explorer.getFileStatTreeNodeByPath('test/a/b');
298
+ expect(uncompressNode).toBeDefined();
299
+ expect(await uncompressNode?.label()).toBe('b');
300
+ // After delete `test/a/b` folder
301
+ // |- test
302
+ // |----a/d
303
+ menu = await uncompressNode?.openContextMenu();
304
+ const deleteMenu = await menu?.menuItemByName('Delete');
305
+ await deleteMenu?.click();
306
+ await app.page.waitForTimeout(200);
307
+ const confirmed = await app.getDialogButton('Move to trash');
308
+ await confirmed?.click();
309
+ await app.page.waitForTimeout(2000);
310
+ const afterDeleteNode = await explorer.getFileStatTreeNodeByPath('test/a/d');
311
+ expect(afterDeleteNode).toBeDefined();
312
+ expect(await afterDeleteNode?.label()).toBe('a/d');
313
+ });
314
+
315
+ test('the visible state of outline panel should be restored after refreshing', async () => {
316
+ if (!(await explorer.isVisible())) {
317
+ await explorer.open();
318
+ }
319
+ await outlineView.open();
320
+ const menu = await outlineView.openTabContextMenu();
321
+ await menu?.clickMenuItem(outlineView.name!);
322
+ await app.page.waitForTimeout(1000);
323
+ // Default to be visibled
324
+ expect(await outlineView.isVisible()).toBeFalsy();
325
+ await app.page.reload();
326
+ await app.page.waitForTimeout(2000);
327
+ expect(await outlineView.isVisible()).toBeFalsy();
328
+ });
329
+ });
@@ -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
+ });