Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/test/automation/src/editor.ts
3520 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { References } from './peek';
7
import { Commands } from './workbench';
8
import { Code } from './code';
9
10
const RENAME_BOX = '.monaco-editor .monaco-editor.rename-box';
11
const RENAME_INPUT = `${RENAME_BOX} .rename-input`;
12
const EDITOR = (filename: string) => `.monaco-editor[data-uri$="${filename}"]`;
13
const VIEW_LINES = (filename: string) => `${EDITOR(filename)} .view-lines`;
14
const LINE_NUMBERS = (filename: string) => `${EDITOR(filename)} .margin .margin-view-overlays .line-numbers`;
15
16
export class Editor {
17
18
private static readonly FOLDING_EXPANDED = '.monaco-editor .margin .margin-view-overlays>:nth-child(${INDEX}) .folding';
19
private static readonly FOLDING_COLLAPSED = `${Editor.FOLDING_EXPANDED}.collapsed`;
20
21
constructor(private code: Code, private commands: Commands) { }
22
23
async findReferences(filename: string, term: string, line: number): Promise<References> {
24
await this.clickOnTerm(filename, term, line);
25
await this.commands.runCommand('Peek References');
26
const references = new References(this.code);
27
await references.waitUntilOpen();
28
return references;
29
}
30
31
async rename(filename: string, line: number, from: string, to: string): Promise<void> {
32
await this.clickOnTerm(filename, from, line);
33
await this.commands.runCommand('Rename Symbol');
34
35
await this.code.waitForActiveElement(RENAME_INPUT);
36
await this.code.waitForSetValue(RENAME_INPUT, to);
37
38
await this.code.dispatchKeybinding('enter', async () => {
39
// TODO: Add an accept callback to verify the keybinding was successful
40
});
41
}
42
43
async gotoDefinition(filename: string, term: string, line: number): Promise<void> {
44
await this.clickOnTerm(filename, term, line);
45
await this.commands.runCommand('Go to Implementations');
46
}
47
48
async peekDefinition(filename: string, term: string, line: number): Promise<References> {
49
await this.clickOnTerm(filename, term, line);
50
await this.commands.runCommand('Peek Definition');
51
const peek = new References(this.code);
52
await peek.waitUntilOpen();
53
return peek;
54
}
55
56
private async getSelector(filename: string, term: string, line: number): Promise<string> {
57
const lineIndex = await this.getViewLineIndex(filename, line);
58
const classNames = await this.getClassSelectors(filename, term, lineIndex);
59
60
return `${VIEW_LINES(filename)}>:nth-child(${lineIndex}) span span.${classNames[0]}`;
61
}
62
63
async foldAtLine(filename: string, line: number): Promise<any> {
64
const lineIndex = await this.getViewLineIndex(filename, line);
65
await this.code.waitAndClick(Editor.FOLDING_EXPANDED.replace('${INDEX}', '' + lineIndex));
66
await this.code.waitForElement(Editor.FOLDING_COLLAPSED.replace('${INDEX}', '' + lineIndex));
67
}
68
69
async unfoldAtLine(filename: string, line: number): Promise<any> {
70
const lineIndex = await this.getViewLineIndex(filename, line);
71
await this.code.waitAndClick(Editor.FOLDING_COLLAPSED.replace('${INDEX}', '' + lineIndex));
72
await this.code.waitForElement(Editor.FOLDING_EXPANDED.replace('${INDEX}', '' + lineIndex));
73
}
74
75
private async clickOnTerm(filename: string, term: string, line: number): Promise<void> {
76
const selector = await this.getSelector(filename, term, line);
77
await this.code.waitAndClick(selector);
78
}
79
80
async waitForEditorFocus(filename: string, lineNumber: number, selectorPrefix = ''): Promise<void> {
81
const editor = [selectorPrefix || '', EDITOR(filename)].join(' ');
82
const line = `${editor} .view-lines > .view-line:nth-child(${lineNumber})`;
83
const editContext = `${editor} ${this._editContextSelector()}`;
84
85
await this.code.waitAndClick(line, 1, 1);
86
await this.code.waitForActiveElement(editContext);
87
}
88
89
async waitForTypeInEditor(filename: string, text: string, selectorPrefix = ''): Promise<any> {
90
if (text.includes('\n')) {
91
throw new Error('waitForTypeInEditor does not support new lines, use either a long single line or dispatchKeybinding(\'Enter\')');
92
}
93
const editor = [selectorPrefix || '', EDITOR(filename)].join(' ');
94
95
await this.code.waitForElement(editor);
96
97
const editContext = `${editor} ${this._editContextSelector()}`;
98
await this.code.waitForActiveElement(editContext);
99
100
await this.code.waitForTypeInEditor(editContext, text);
101
102
await this.waitForEditorContents(filename, c => c.indexOf(text) > -1, selectorPrefix);
103
}
104
105
async waitForEditorSelection(filename: string, accept: (selection: { selectionStart: number; selectionEnd: number }) => boolean): Promise<void> {
106
const selector = `${EDITOR(filename)} ${this._editContextSelector()}`;
107
await this.code.waitForEditorSelection(selector, accept);
108
}
109
110
private _editContextSelector() {
111
return !this.code.editContextEnabled ? 'textarea' : '.native-edit-context';
112
}
113
114
async waitForEditorContents(filename: string, accept: (contents: string) => boolean, selectorPrefix = ''): Promise<any> {
115
const selector = [selectorPrefix || '', `${EDITOR(filename)} .view-lines`].join(' ');
116
return this.code.waitForTextContent(selector, undefined, c => accept(c.replace(/\u00a0/g, ' ')));
117
}
118
119
private async getClassSelectors(filename: string, term: string, viewline: number): Promise<string[]> {
120
const elements = await this.code.waitForElements(`${VIEW_LINES(filename)}>:nth-child(${viewline}) span span`, false, els => els.some(el => el.textContent === term));
121
const { className } = elements.filter(r => r.textContent === term)[0];
122
return className.split(/\s/g);
123
}
124
125
private async getViewLineIndex(filename: string, line: number): Promise<number> {
126
const elements = await this.code.waitForElements(LINE_NUMBERS(filename), false, els => {
127
return els.some(el => el.textContent === `${line}`);
128
});
129
130
for (let index = 0; index < elements.length; index++) {
131
if (elements[index].textContent === `${line}`) {
132
return index + 1;
133
}
134
}
135
136
throw new Error('Line not found');
137
}
138
}
139
140