Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/test/automation/src/search.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 { Viewlet } from './viewlet';
7
import { Code } from './code';
8
9
const VIEWLET = '.search-view';
10
const INPUT = `${VIEWLET} .search-widget .search-container .monaco-inputbox textarea`;
11
const INCLUDE_INPUT = `${VIEWLET} .query-details .file-types.includes .monaco-inputbox input`;
12
const FILE_MATCH = (filename: string) => `${VIEWLET} .results .filematch[data-resource$="${filename}"]`;
13
14
async function retry(setup: () => Promise<any>, attempt: () => Promise<any>) {
15
let count = 0;
16
while (true) {
17
await setup();
18
19
try {
20
await attempt();
21
return;
22
} catch (err) {
23
if (++count > 5) {
24
throw err;
25
}
26
}
27
}
28
}
29
30
export class Search extends Viewlet {
31
32
constructor(code: Code) {
33
super(code);
34
}
35
36
async clearSearchResults(): Promise<void> {
37
await retry(
38
() => this.code.waitAndClick(`.sidebar .title-actions .codicon-search-clear-results`),
39
() => this.waitForNoResultText(10));
40
}
41
42
async openSearchViewlet(): Promise<any> {
43
const accept = () => this.waitForInputFocus(INPUT);
44
if (process.platform === 'darwin') {
45
await this.code.dispatchKeybinding('cmd+shift+f', accept);
46
} else {
47
await this.code.dispatchKeybinding('ctrl+shift+f', accept);
48
}
49
}
50
51
async getSearchTooltip(): Promise<any> {
52
const icon = await this.code.waitForElement(`.activitybar .action-label.codicon.codicon-search-view-icon`, (el) => !!el?.attributes?.['title']);
53
return icon.attributes['title'];
54
}
55
56
async searchFor(text: string): Promise<void> {
57
await this.clearSearchResults();
58
await this.waitForInputFocus(INPUT);
59
await this.code.waitForSetValue(INPUT, text);
60
await this.submitSearch();
61
}
62
63
async hasActivityBarMoved() {
64
await this.code.waitForElement('.activitybar');
65
66
const elementBoundingBox = await this.code.driver.getElementXY('.activitybar');
67
return elementBoundingBox !== null && elementBoundingBox.x === 48 && elementBoundingBox.y === 375;
68
}
69
70
async waitForPageUp(): Promise<void> {
71
await this.code.dispatchKeybinding('PageUp', async () => { });
72
}
73
74
async waitForPageDown(): Promise<void> {
75
await this.code.dispatchKeybinding('PageDown', async () => { });
76
}
77
78
async submitSearch(): Promise<void> {
79
await this.waitForInputFocus(INPUT);
80
await this.code.dispatchKeybinding('enter', async () => { await this.code.waitForElement(`${VIEWLET} .messages`); });
81
}
82
83
async setFilesToIncludeText(text: string): Promise<void> {
84
await this.waitForInputFocus(INCLUDE_INPUT);
85
await this.code.waitForSetValue(INCLUDE_INPUT, text || '');
86
}
87
88
async showQueryDetails(): Promise<void> {
89
await this.code.waitAndClick(`${VIEWLET} .query-details .more`);
90
}
91
92
async hideQueryDetails(): Promise<void> {
93
await this.code.waitAndClick(`${VIEWLET} .query-details.more .more`);
94
}
95
96
async removeFileMatch(filename: string, expectedText: string): Promise<void> {
97
const fileMatch = FILE_MATCH(filename);
98
99
// Retry this because the click can fail if the search tree is rerendered at the same time
100
await retry(
101
async () => {
102
await this.code.waitAndClick(fileMatch);
103
await this.code.waitAndClick(`${fileMatch} .action-label.codicon-search-remove`);
104
},
105
async () => this.waitForResultText(expectedText, 10));
106
}
107
108
async expandReplace(): Promise<void> {
109
await this.code.waitAndClick(`${VIEWLET} .search-widget .monaco-button.toggle-replace-button.codicon-search-hide-replace`);
110
}
111
112
async collapseReplace(): Promise<void> {
113
await this.code.waitAndClick(`${VIEWLET} .search-widget .monaco-button.toggle-replace-button.codicon-search-show-replace`);
114
}
115
116
async setReplaceText(text: string): Promise<void> {
117
await this.code.waitForSetValue(`${VIEWLET} .search-widget .replace-container .monaco-inputbox textarea[aria-label="Replace"]`, text);
118
}
119
120
async replaceFileMatch(filename: string, expectedText: string): Promise<void> {
121
const fileMatch = FILE_MATCH(filename);
122
123
// Retry this because the click can fail if the search tree is rerendered at the same time
124
await retry(
125
async () => {
126
await this.code.waitAndClick(fileMatch);
127
await this.code.waitAndClick(`${fileMatch} .action-label.codicon.codicon-search-replace-all`);
128
},
129
() => this.waitForResultText(expectedText, 10));
130
}
131
132
async waitForResultText(text: string, retryCount?: number): Promise<void> {
133
// The label can end with " - " depending on whether the search editor is enabled
134
await this.code.waitForTextContent(`${VIEWLET} .messages .message`, undefined, result => result.startsWith(text), retryCount);
135
}
136
137
async waitForNoResultText(retryCount?: number): Promise<void> {
138
await this.code.waitForTextContent(`${VIEWLET} .messages`, undefined, text => text === '' || text.startsWith('Search was canceled before any results could be found'), retryCount);
139
}
140
141
private async waitForInputFocus(selector: string): Promise<void> {
142
let retries = 0;
143
144
// other parts of code might steal focus away from input boxes :(
145
while (retries < 5) {
146
await this.code.waitAndClick(INPUT, 2, 2);
147
148
try {
149
await this.code.waitForActiveElement(INPUT, 10);
150
break;
151
} catch (err) {
152
if (++retries > 5) {
153
throw err;
154
}
155
}
156
}
157
}
158
}
159
160