@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,72 @@
1
+ import { OpenSumiApp } from './app';
2
+ import { OpenSumiOpenedEditorView } from './opened-editor-view';
3
+ import { OpenSumiPanel } from './panel';
4
+ import { OpenSumiSourceControlView } from './source-control-view';
5
+ import { OpenSumiTreeNode } from './tree-node';
6
+
7
+ export class OpenSumiSCMFileStatNode extends OpenSumiTreeNode {
8
+ async getFsPath() {
9
+ return await this.elementHandle.getAttribute('title');
10
+ }
11
+
12
+ async isFolder() {
13
+ const icon = await this.elementHandle.$("[class*='file_icon___']");
14
+ if (!icon) {
15
+ return false;
16
+ }
17
+ const className = await icon.getAttribute('class');
18
+ return className?.includes('folder-icon');
19
+ }
20
+
21
+ async getMenuItemByName(name: string) {
22
+ const contextMenu = await this.openContextMenu();
23
+ const menuItem = await contextMenu.menuItemByName(name);
24
+ return menuItem;
25
+ }
26
+
27
+ async open() {
28
+ await this.elementHandle.click();
29
+ }
30
+
31
+ async getBadge() {
32
+ const status = await this.elementHandle.$('[class*="scm_tree_node_status___"]');
33
+ return await status?.textContent();
34
+ }
35
+ }
36
+
37
+ export class OpenSumiSCMView extends OpenSumiPanel {
38
+ private _scmView: OpenSumiSourceControlView;
39
+ private _openedEditorView: OpenSumiOpenedEditorView;
40
+
41
+ constructor(app: OpenSumiApp) {
42
+ super(app, 'SCM');
43
+ this._scmView = new OpenSumiSourceControlView(app, 'SOURCE CONTROL');
44
+ }
45
+
46
+ get scmView() {
47
+ return this._scmView;
48
+ }
49
+
50
+ get openedEditorView() {
51
+ return this._openedEditorView;
52
+ }
53
+
54
+ async getFileStatTreeNodeByPath(path: string) {
55
+ const treeItems = await (await this.scmView.getViewElement())?.$$('[class*="scm_tree_node___"]');
56
+ if (!treeItems) {
57
+ return;
58
+ }
59
+ let node;
60
+ for (const item of treeItems) {
61
+ const title = await item.getAttribute('title');
62
+ // title maybe `a.js • Untracked`
63
+ if (title?.split(' ')[0]?.endsWith(path)) {
64
+ node = item;
65
+ break;
66
+ }
67
+ }
68
+ if (node) {
69
+ return new OpenSumiSCMFileStatNode(node, this.app);
70
+ }
71
+ }
72
+ }
@@ -0,0 +1,260 @@
1
+ import { isDefined } from '@opensumi/ide-utils';
2
+
3
+ import { OpenSumiApp } from './app';
4
+ import { OpenSumiPanel } from './panel';
5
+ import { OpenSumiTreeNode } from './tree-node';
6
+
7
+ export interface ISearchOptions {
8
+ search: string;
9
+ include?: string;
10
+ exclude?: string;
11
+ useDefaultExclude?: boolean;
12
+ useRegexp?: boolean;
13
+ isMatchWhileWord?: boolean;
14
+ isMatchCase?: boolean;
15
+ }
16
+
17
+ const SEARCH_OPTIONS = {
18
+ MATCH_CASE: 'Match Case',
19
+ MATCH_WHOLE_WORD: 'Match Whole Word',
20
+ USE_REGEXP: 'Use Regular Expression',
21
+ };
22
+
23
+ export class OpenSumiSearchFileStatNode extends OpenSumiTreeNode {
24
+ async getFsPath() {
25
+ return await this.elementHandle.getAttribute('title');
26
+ }
27
+
28
+ async isFile() {
29
+ const icon = await this.elementHandle.$("[class*='icon__']");
30
+ if (!icon) {
31
+ return false;
32
+ }
33
+ const className = await icon.getAttribute('class');
34
+ return className?.includes('file-icon');
35
+ }
36
+
37
+ async getMenuItemByName(name: string) {
38
+ const contextMenu = await this.openContextMenu();
39
+ const menuItem = await contextMenu.menuItemByName(name);
40
+ return menuItem;
41
+ }
42
+
43
+ async open() {
44
+ await this.elementHandle.click();
45
+ }
46
+
47
+ async getBadge() {
48
+ const status = await this.elementHandle.$('[class*="status___"]');
49
+ return await status?.textContent();
50
+ }
51
+ }
52
+
53
+ export class OpenSumiSearchView extends OpenSumiPanel {
54
+ constructor(app: OpenSumiApp) {
55
+ super(app, 'SEARCH');
56
+ }
57
+
58
+ get searchInputSelector() {
59
+ return '#search-input-field';
60
+ }
61
+
62
+ get replaceInputSelector() {
63
+ return '#replace-input-field';
64
+ }
65
+
66
+ get includeInputSelector() {
67
+ return '#include-input-field';
68
+ }
69
+
70
+ get excludeInputSelector() {
71
+ return '#exclude-input-field';
72
+ }
73
+
74
+ async getSearchResult() {
75
+ let results;
76
+ let files;
77
+ const resultElement = await this.app.page.waitForSelector('[class*="result_describe__"]');
78
+ const text = await resultElement.textContent();
79
+ const regx = /^(\d+) results found in (\d+) files/gi;
80
+ if (text) {
81
+ const match = regx.exec(text);
82
+ if (match) {
83
+ results = Number(match[1]);
84
+ files = Number(match[2]);
85
+ }
86
+ }
87
+ return {
88
+ results,
89
+ files,
90
+ };
91
+ }
92
+
93
+ async getReplaceAllButton() {
94
+ const button = this.view?.$('[class*="replace_all_button__"]');
95
+ return button;
96
+ }
97
+
98
+ async focusOnSearch() {
99
+ const visible = await this.isVisible();
100
+ if (!visible) {
101
+ await this.open();
102
+ }
103
+ const input = await this.app.page.$(this.searchInputSelector);
104
+ await input?.fill('');
105
+ await input?.focus();
106
+ }
107
+
108
+ async focusOnReplace() {
109
+ const visible = await this.isVisible();
110
+ if (!visible) {
111
+ await this.open();
112
+ }
113
+ const input = await this.app.page.$(this.replaceInputSelector);
114
+ await input?.focus();
115
+ return input;
116
+ }
117
+
118
+ async focusOnExclude() {
119
+ const visible = await this.isVisible();
120
+ if (!visible) {
121
+ await this.open();
122
+ }
123
+ const input = await this.getExcludeInput();
124
+ await input?.focus();
125
+ }
126
+
127
+ async focusOnInclude() {
128
+ const visible = await this.isVisible();
129
+ if (!visible) {
130
+ await this.open();
131
+ }
132
+ const input = await this.getIncludeInput();
133
+ await input?.focus();
134
+ }
135
+
136
+ async getSearchInput() {
137
+ return await this.view?.$(this.searchInputSelector);
138
+ }
139
+
140
+ async getReplaceInput() {
141
+ return await this.view?.$(this.replaceInputSelector);
142
+ }
143
+
144
+ async getExcludeInput() {
145
+ return await this.view?.$(this.excludeInputSelector);
146
+ }
147
+
148
+ async getIncludeInput() {
149
+ return await this.view?.$(this.includeInputSelector);
150
+ }
151
+
152
+ async getSearchAction(value: string) {
153
+ const searchField = await this.view?.$('[class*="search_field__"]');
154
+ if (searchField) {
155
+ const options = await searchField.$$('[class*="search_option__"]');
156
+ for (const option of options) {
157
+ const title = await option.getAttribute('title');
158
+ if (title === value) {
159
+ return option;
160
+ }
161
+ }
162
+ }
163
+ }
164
+
165
+ async activeSearchAction(value: string) {
166
+ const action = await this.getSearchAction(value);
167
+ const classname = await action?.getAttribute('class');
168
+ if (classname?.includes('select___')) {
169
+ return;
170
+ }
171
+ await action?.click();
172
+ }
173
+
174
+ async deactiveSearchAction(value: string) {
175
+ const action = await this.getSearchAction(value);
176
+ const classname = await action?.getAttribute('class');
177
+ if (classname?.includes('select___')) {
178
+ await action?.click();
179
+ }
180
+ }
181
+
182
+ async toggleDisplaySearchRules(expected?: boolean) {
183
+ const visible = await this.isVisible();
184
+ if (!visible) {
185
+ await this.open();
186
+ }
187
+ const titleBar = await this.view?.$('[class*="search_input_title_"]');
188
+ const checkbox = await titleBar?.$('.kt-checkbox');
189
+ const checked = await checkbox?.isChecked();
190
+ if (isDefined(expected) && expected === checked) {
191
+ return;
192
+ }
193
+ await checkbox?.click();
194
+ }
195
+
196
+ async toggleUseDefaultExclude(expected?: boolean) {
197
+ const visible = await this.isVisible();
198
+ if (!visible) {
199
+ await this.open();
200
+ }
201
+ const wrapper = await this.view?.$('[class*="use_default_excludes_wrapper_"]');
202
+ const checkbox = await wrapper?.$('.kt-checkbox');
203
+ const checked = await checkbox?.isChecked();
204
+ if (isDefined(expected) && expected === checked) {
205
+ return;
206
+ }
207
+ await checkbox?.click();
208
+ }
209
+
210
+ async search(options: ISearchOptions) {
211
+ if (!(await this.isVisible())) {
212
+ await this.open();
213
+ }
214
+ if (!options.search) {
215
+ return;
216
+ }
217
+ if (isDefined(options.isMatchCase)) {
218
+ (await options.isMatchCase)
219
+ ? this.activeSearchAction(SEARCH_OPTIONS.MATCH_CASE)
220
+ : this.deactiveSearchAction(SEARCH_OPTIONS.MATCH_CASE);
221
+ }
222
+ if (isDefined(options.isMatchWhileWord)) {
223
+ (await options.isMatchWhileWord)
224
+ ? this.activeSearchAction(SEARCH_OPTIONS.MATCH_WHOLE_WORD)
225
+ : this.deactiveSearchAction(SEARCH_OPTIONS.MATCH_WHOLE_WORD);
226
+ }
227
+ if (isDefined(options.useRegexp)) {
228
+ (await options.useRegexp)
229
+ ? this.activeSearchAction(SEARCH_OPTIONS.USE_REGEXP)
230
+ : this.deactiveSearchAction(SEARCH_OPTIONS.USE_REGEXP);
231
+ }
232
+ if (options.exclude || options.include || isDefined(options.useDefaultExclude)) {
233
+ await this.toggleDisplaySearchRules(true);
234
+ if (options.exclude) {
235
+ await this.focusOnExclude();
236
+ await this.page.keyboard.type(options.exclude);
237
+ }
238
+ if (options.include) {
239
+ await this.focusOnInclude();
240
+ await this.page.keyboard.type(options.include);
241
+ }
242
+ const useDefaultExlude = isDefined(options.useDefaultExclude) ? options.useDefaultExclude : true;
243
+ await this.toggleUseDefaultExclude(useDefaultExlude);
244
+ }
245
+ // Search on the end.
246
+ await this.focusOnSearch();
247
+ await this.page.keyboard.type(options.search);
248
+ }
249
+
250
+ async getTreeNodeByIndex(index: number) {
251
+ const treeItems = await this.view?.$$('[class*="search_node___"]');
252
+ if (!treeItems) {
253
+ return;
254
+ }
255
+ const node = treeItems[index];
256
+ if (node) {
257
+ return new OpenSumiSearchFileStatNode(node, this.app);
258
+ }
259
+ }
260
+ }
@@ -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
+ }
@@ -0,0 +1,50 @@
1
+ import { OpenSumiApp } from './app';
2
+ import { OpenSumiContextMenu } from './context-menu';
3
+ import { OpenSumiPanel } from './panel';
4
+
5
+ type TerminalType = 'bash' | 'zsh' | 'Javascript Debug Terminal';
6
+
7
+ export class OpenSumiTerminalView extends OpenSumiPanel {
8
+ constructor(app: OpenSumiApp) {
9
+ super(app, 'TERMINAL');
10
+ }
11
+
12
+ async sendText(text: string) {
13
+ const visible = await this.isVisible();
14
+ if (!visible) {
15
+ await this.open();
16
+ }
17
+ await this.focus();
18
+ const box = await this.view?.boundingBox();
19
+ if (box) {
20
+ await this.app.page.mouse.click(box.x + box?.width / 2, box.y + box?.height / 2);
21
+ }
22
+ await this.page.keyboard.type(text);
23
+ await this.app.page.keyboard.press('Enter');
24
+ }
25
+
26
+ async createTerminalByType(type: TerminalType) {
27
+ const buttonWrapper = await this.view?.$('[class*="item_wrapper__"]');
28
+ const buttons = await buttonWrapper?.$$('.kaitian-icon');
29
+ if (!buttons) {
30
+ return;
31
+ }
32
+ let button;
33
+ for (const item of buttons) {
34
+ const title = await item.getAttribute('title');
35
+ if (title === 'Create terminal by type') {
36
+ button = item;
37
+ break;
38
+ }
39
+ }
40
+ if (!button) {
41
+ return;
42
+ }
43
+ await button.click();
44
+ const menu = new OpenSumiContextMenu(this.app);
45
+ await menu.waitForVisible();
46
+ await menu.clickMenuItem(type);
47
+
48
+ await this.app.page.waitForTimeout(1000);
49
+ }
50
+ }
@@ -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,121 @@
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 { OpenSumiTerminalView } from '../terminal-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 debugView: OpenSumiDebugView;
17
+ let editor: OpenSumiTextEditor;
18
+ let workspace: OpenSumiWorkspace;
19
+
20
+ test.describe('OpenSumi Debug', () => {
21
+ test.beforeAll(async () => {
22
+ workspace = new OpenSumiWorkspace([path.resolve('./src/tests/workspaces/debug')]);
23
+ app = await OpenSumiApp.load(page, workspace);
24
+ explorer = await app.open(OpenSumiExplorerView);
25
+ explorer.initFileTreeView(workspace.workspace.displayName);
26
+ await explorer.fileTreeView.open();
27
+ });
28
+
29
+ test.afterAll(() => {
30
+ app.dispose();
31
+ });
32
+
33
+ test('Debug breakpoint editor glyph margin should be worked', async () => {
34
+ editor = await app.openEditor(OpenSumiTextEditor, explorer, 'index.js', false);
35
+ const glyphMarginModel = await editor.getGlyphMarginModel();
36
+ let overlay = await glyphMarginModel.getOverlay(6);
37
+ await overlay?.click({ position: { x: 9, y: 9 }, force: true });
38
+ // 此时元素 dom 结构已经改变,需要重新获取
39
+ overlay = await glyphMarginModel.getOverlay(6);
40
+ expect(await glyphMarginModel.hasBreakpoint(overlay!)).toBeTruthy();
41
+ await editor.close();
42
+ });
43
+
44
+ test('Run Debug should be worked', async () => {
45
+ editor = await app.openEditor(OpenSumiTextEditor, explorer, 'index.js', false);
46
+ await app.page.waitForTimeout(1000);
47
+
48
+ debugView = await app.open(OpenSumiDebugView);
49
+ const glyphMarginModel = await editor.getGlyphMarginModel();
50
+ let glyphOverlay = await glyphMarginModel.getOverlay(6);
51
+ expect(glyphOverlay).toBeDefined();
52
+ if (!glyphOverlay) {
53
+ return;
54
+ }
55
+ const isClicked = await glyphMarginModel.hasBreakpoint(glyphOverlay);
56
+ if (!isClicked) {
57
+ await glyphOverlay?.click({ position: { x: 9, y: 9 }, force: true });
58
+ await app.page.waitForTimeout(1000);
59
+ }
60
+
61
+ await debugView.start();
62
+ await app.page.waitForTimeout(2000);
63
+
64
+ glyphOverlay = await glyphMarginModel.getOverlay(6);
65
+ expect(glyphOverlay).toBeDefined();
66
+ if (!glyphOverlay) {
67
+ return;
68
+ }
69
+ expect(await glyphMarginModel.hasTopStackFrame(glyphOverlay)).toBeTruthy();
70
+
71
+ const overlaysModel = await editor.getOverlaysModel();
72
+ const viewOverlay = await overlaysModel.getOverlay(6);
73
+ expect(viewOverlay).toBeDefined();
74
+ if (!viewOverlay) {
75
+ return;
76
+ }
77
+ expect(await glyphMarginModel.hasTopStackFrameLine(viewOverlay)).toBeTruthy();
78
+ await editor.close();
79
+ await debugView.stop();
80
+ await page.waitForTimeout(1000);
81
+ });
82
+
83
+ test('Run Debug by Javascript Debug Terminal', async () => {
84
+ await explorer.open();
85
+ editor = await app.openEditor(OpenSumiTextEditor, explorer, 'index.js', false);
86
+
87
+ const terminal = await app.open(OpenSumiTerminalView);
88
+ await terminal.createTerminalByType('Javascript Debug Terminal');
89
+ await terminal.sendText('node index.js');
90
+ await app.page.waitForTimeout(1000);
91
+
92
+ const glyphMarginModel = await editor.getGlyphMarginModel();
93
+ let glyphOverlay = await glyphMarginModel.getOverlay(6);
94
+ expect(glyphOverlay).toBeDefined();
95
+ if (!glyphOverlay) {
96
+ return;
97
+ }
98
+ const isClicked = await glyphMarginModel.hasBreakpoint(glyphOverlay);
99
+ if (!isClicked) {
100
+ await glyphOverlay?.click({ position: { x: 9, y: 9 }, force: true });
101
+ await app.page.waitForTimeout(1000);
102
+ }
103
+
104
+ glyphOverlay = await glyphMarginModel.getOverlay(6);
105
+ expect(glyphOverlay).toBeDefined();
106
+ if (!glyphOverlay) {
107
+ return;
108
+ }
109
+ expect(await glyphMarginModel.hasTopStackFrame(glyphOverlay)).toBeTruthy();
110
+
111
+ const overlaysModel = await editor.getOverlaysModel();
112
+ const viewOverlay = await overlaysModel.getOverlay(6);
113
+ expect(viewOverlay).toBeDefined();
114
+ if (!viewOverlay) {
115
+ return;
116
+ }
117
+ expect(await glyphMarginModel.hasTopStackFrameLine(viewOverlay)).toBeTruthy();
118
+ await debugView.stop();
119
+ await page.waitForTimeout(1000);
120
+ });
121
+ });
@@ -0,0 +1,55 @@
1
+ import path from 'path';
2
+
3
+ import { expect } from '@playwright/test';
4
+
5
+ import { OpenSumiApp } from '../../app';
6
+ import { OpenSumiExplorerView } from '../../explorer-view';
7
+ import { OpenSumiTextEditor } from '../../text-editor';
8
+ import { OpenSumiWorkspace } from '../../workspace';
9
+ import test, { page } from '../hooks';
10
+
11
+ let app: OpenSumiApp;
12
+ let explorer: OpenSumiExplorerView;
13
+ let editor: OpenSumiTextEditor;
14
+ let workspace: OpenSumiWorkspace;
15
+
16
+ test.describe('OpenSumi Editor Undo Redo', () => {
17
+ test.beforeAll(async () => {
18
+ workspace = new OpenSumiWorkspace([path.resolve('./src/tests/workspaces/default')]);
19
+ app = await OpenSumiApp.load(page, workspace);
20
+ explorer = await app.open(OpenSumiExplorerView);
21
+ explorer.initFileTreeView(workspace.workspace.displayName);
22
+ await explorer.fileTreeView.open();
23
+ });
24
+
25
+ test.afterAll(() => {
26
+ app.dispose();
27
+ });
28
+
29
+ test('simple editor undo/redo should work', async () => {
30
+ editor = await app.openEditor(OpenSumiTextEditor, explorer, 'editor-undo-redo.text');
31
+ const existingLine = await editor.lineByLineNumber(1);
32
+ await editor.placeCursorInLine(existingLine);
33
+ await editor.typeText('a');
34
+ await editor.saveByKeyboard();
35
+ await editor.typeText('b');
36
+ await editor.saveByKeyboard();
37
+ await editor.typeText('c');
38
+ await editor.saveByKeyboard();
39
+ await expectLineHasText('abc');
40
+ await editor.undoByKeyboard();
41
+ await expectLineHasText('ab');
42
+ await editor.undoByKeyboard();
43
+ await expectLineHasText('a');
44
+ await editor.redoByKeyboard();
45
+ await expectLineHasText('ab');
46
+ await editor.redoByKeyboard();
47
+ await expectLineHasText('abc');
48
+
49
+ async function expectLineHasText(text: string) {
50
+ const line = await editor.lineByLineNumber(1);
51
+ expect(await line?.innerText()).toEqual(text);
52
+ await editor.placeCursorInLine(line);
53
+ }
54
+ });
55
+ });