@redhat-developer/page-objects 1.17.0 → 1.18.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/out/components/AbstractElement.d.ts +77 -1
- package/out/components/AbstractElement.js +169 -12
- package/out/components/ActionButtonElementDropdown.js +14 -2
- package/out/components/activityBar/ViewControl.js +5 -1
- package/out/components/bottomBar/Views.js +25 -12
- package/out/components/editor/ContentAssist.js +2 -1
- package/out/components/editor/EditorView.js +50 -9
- package/out/components/editor/ExtensionEditorDetailsSection.js +31 -7
- package/out/components/editor/SettingsEditor.js +110 -28
- package/out/components/editor/TextEditor.js +120 -25
- package/out/components/menu/ContextMenu.js +27 -6
- package/out/components/menu/MenuItem.js +14 -1
- package/out/components/menu/TitleBar.js +15 -3
- package/out/components/sidebar/ViewSection.js +14 -4
- package/out/components/sidebar/WelcomeContent.js +10 -2
- package/out/components/sidebar/debug/DebugView.js +10 -2
- package/out/components/sidebar/extensions/ExtensionsViewSection.js +20 -5
- package/out/components/sidebar/scm/NewScmView.js +53 -13
- package/out/components/sidebar/scm/ScmView.js +12 -4
- package/out/components/sidebar/tree/TreeSection.js +11 -3
- package/out/components/sidebar/tree/custom/CustomTreeSection.js +22 -6
- package/out/components/sidebar/tree/debug/WatchSection.js +5 -1
- package/out/components/sidebar/tree/default/DefaultTreeSection.js +9 -1
- package/out/components/workbench/Notification.js +10 -2
- package/out/components/workbench/NotificationsCenter.js +11 -3
- package/out/components/workbench/Workbench.js +14 -4
- package/out/components/workbench/input/Input.js +45 -16
- package/out/components/workbench/input/InputBox.js +20 -4
- package/out/components/workbench/input/QuickOpenBox.js +11 -3
- package/out/errors/ExTesterError.d.ts +112 -0
- package/out/errors/ExTesterError.js +201 -0
- package/out/index.d.ts +2 -0
- package/out/index.js +2 -0
- package/out/utils/WaitHelper.d.ts +202 -0
- package/out/utils/WaitHelper.js +465 -0
- package/package.json +4 -4
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
4
|
+
* contributor license agreements. See the NOTICE file distributed with
|
|
5
|
+
* this work for additional information regarding copyright ownership.
|
|
6
|
+
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
7
|
+
* (the "License", destination); you may not use this file except in compliance with
|
|
8
|
+
* the License. You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.WaitHelper = void 0;
|
|
20
|
+
exports.createWaitHelper = createWaitHelper;
|
|
21
|
+
const selenium_webdriver_1 = require("selenium-webdriver");
|
|
22
|
+
const DEFAULT_TIMEOUT = 5000;
|
|
23
|
+
const DEFAULT_POLL_INTERVAL = 100;
|
|
24
|
+
const DEFAULT_STABILITY_CHECKS = 3;
|
|
25
|
+
const DEFAULT_STABILITY_INTERVAL = 100;
|
|
26
|
+
/**
|
|
27
|
+
* Utility class for condition-based waiting operations.
|
|
28
|
+
* Replaces arbitrary sleep() calls with intelligent waiting strategies.
|
|
29
|
+
*/
|
|
30
|
+
class WaitHelper {
|
|
31
|
+
driver;
|
|
32
|
+
defaultTimeout;
|
|
33
|
+
defaultPollInterval;
|
|
34
|
+
constructor(driver, defaultTimeout = DEFAULT_TIMEOUT, defaultPollInterval = DEFAULT_POLL_INTERVAL) {
|
|
35
|
+
this.driver = driver;
|
|
36
|
+
this.defaultTimeout = defaultTimeout;
|
|
37
|
+
this.defaultPollInterval = defaultPollInterval;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Wait for a condition to become truthy.
|
|
41
|
+
* @param condition Async function that returns a value - waiting stops when value is truthy
|
|
42
|
+
* @param options Wait configuration options
|
|
43
|
+
* @returns The truthy value returned by the condition
|
|
44
|
+
*/
|
|
45
|
+
async forCondition(condition, options = {}) {
|
|
46
|
+
const { timeout = this.defaultTimeout, pollInterval = this.defaultPollInterval, message } = options;
|
|
47
|
+
const start = Date.now();
|
|
48
|
+
let lastError;
|
|
49
|
+
while (Date.now() - start < timeout) {
|
|
50
|
+
try {
|
|
51
|
+
const result = await condition();
|
|
52
|
+
if (result) {
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
// Store error but continue retrying for transient errors
|
|
58
|
+
if (e instanceof selenium_webdriver_1.error.StaleElementReferenceError || e instanceof selenium_webdriver_1.error.NoSuchElementError) {
|
|
59
|
+
lastError = e;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
throw e;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
await this.sleep(pollInterval);
|
|
66
|
+
}
|
|
67
|
+
const errorMessage = message || `Condition not met within ${timeout}ms`;
|
|
68
|
+
if (lastError) {
|
|
69
|
+
throw new Error(`${errorMessage}. Last error: ${lastError.message}`);
|
|
70
|
+
}
|
|
71
|
+
throw new Error(errorMessage);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Wait for an element's attribute to have a specific value.
|
|
75
|
+
* @param element The WebElement to check
|
|
76
|
+
* @param attribute Name of the attribute
|
|
77
|
+
* @param value Expected value
|
|
78
|
+
* @param options Wait configuration options
|
|
79
|
+
*/
|
|
80
|
+
async forAttributeValue(element, attribute, value, options = {}) {
|
|
81
|
+
const { timeout = this.defaultTimeout, message } = options;
|
|
82
|
+
await this.forCondition(async () => (await element.getAttribute(attribute)) === value, {
|
|
83
|
+
...options,
|
|
84
|
+
timeout,
|
|
85
|
+
message: message || `Attribute '${attribute}' did not become '${value}' within ${timeout}ms`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Wait for an element's attribute to contain a specific value.
|
|
90
|
+
* @param element The WebElement to check
|
|
91
|
+
* @param attribute Name of the attribute
|
|
92
|
+
* @param value Value to search for
|
|
93
|
+
* @param options Wait configuration options
|
|
94
|
+
*/
|
|
95
|
+
async forAttributeContains(element, attribute, value, options = {}) {
|
|
96
|
+
const { timeout = this.defaultTimeout, message } = options;
|
|
97
|
+
await this.forCondition(async () => {
|
|
98
|
+
const attrValue = await element.getAttribute(attribute);
|
|
99
|
+
return attrValue?.includes(value);
|
|
100
|
+
}, {
|
|
101
|
+
...options,
|
|
102
|
+
timeout,
|
|
103
|
+
message: message || `Attribute '${attribute}' did not contain '${value}' within ${timeout}ms`,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Wait for an element to become stable (position/size stops changing).
|
|
108
|
+
* Useful for waiting after animations or dynamic content loading.
|
|
109
|
+
*
|
|
110
|
+
* Note: If the element becomes stale (removed from DOM), this method returns
|
|
111
|
+
* successfully since element removal is a form of "stability".
|
|
112
|
+
*
|
|
113
|
+
* @param element The WebElement to monitor
|
|
114
|
+
* @param options Stability configuration options
|
|
115
|
+
*/
|
|
116
|
+
async forStable(element, options = {}) {
|
|
117
|
+
const { timeout = this.defaultTimeout, stableChecks = DEFAULT_STABILITY_CHECKS, stabilityInterval = DEFAULT_STABILITY_INTERVAL, message } = options;
|
|
118
|
+
const start = Date.now();
|
|
119
|
+
let lastRect;
|
|
120
|
+
let stableCount = 0;
|
|
121
|
+
try {
|
|
122
|
+
lastRect = await element.getRect();
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
if (e instanceof selenium_webdriver_1.error.StaleElementReferenceError || e instanceof selenium_webdriver_1.error.NoSuchElementError) {
|
|
126
|
+
// Element already gone - consider it stable
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
throw e;
|
|
130
|
+
}
|
|
131
|
+
while (Date.now() - start < timeout) {
|
|
132
|
+
await this.sleep(stabilityInterval);
|
|
133
|
+
try {
|
|
134
|
+
const currentRect = await element.getRect();
|
|
135
|
+
if (this.rectsEqual(lastRect, currentRect)) {
|
|
136
|
+
stableCount++;
|
|
137
|
+
if (stableCount >= stableChecks) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
stableCount = 0;
|
|
143
|
+
lastRect = currentRect;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
if (e instanceof selenium_webdriver_1.error.StaleElementReferenceError || e instanceof selenium_webdriver_1.error.NoSuchElementError) {
|
|
148
|
+
// Element was removed from DOM - consider it stable/done
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
throw e;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
throw new Error(message || `Element did not stabilize within ${timeout}ms`);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Wait for an element to become visible.
|
|
158
|
+
* @param element The WebElement to check
|
|
159
|
+
* @param options Wait configuration options
|
|
160
|
+
*/
|
|
161
|
+
async forVisible(element, options = {}) {
|
|
162
|
+
const { timeout = this.defaultTimeout, message } = options;
|
|
163
|
+
await this.driver.wait(selenium_webdriver_1.until.elementIsVisible(element), timeout, message || `Element did not become visible within ${timeout}ms`);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Wait for an element to become invisible.
|
|
167
|
+
* @param element The WebElement to check
|
|
168
|
+
* @param options Wait configuration options
|
|
169
|
+
*/
|
|
170
|
+
async forNotVisible(element, options = {}) {
|
|
171
|
+
const { timeout = this.defaultTimeout, message } = options;
|
|
172
|
+
await this.driver.wait(selenium_webdriver_1.until.elementIsNotVisible(element), timeout, message || `Element did not become invisible within ${timeout}ms`);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Wait for an element to become enabled.
|
|
176
|
+
* @param element The WebElement to check
|
|
177
|
+
* @param options Wait configuration options
|
|
178
|
+
*/
|
|
179
|
+
async forEnabled(element, options = {}) {
|
|
180
|
+
const { timeout = this.defaultTimeout, message } = options;
|
|
181
|
+
await this.driver.wait(selenium_webdriver_1.until.elementIsEnabled(element), timeout, message || `Element did not become enabled within ${timeout}ms`);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Wait for the number of elements matching a condition to stabilize.
|
|
185
|
+
* Useful for lists that are loading items dynamically.
|
|
186
|
+
* @param getCount Function that returns the current count
|
|
187
|
+
* @param options Stability configuration options
|
|
188
|
+
*/
|
|
189
|
+
async forCountStable(getCount, options = {}) {
|
|
190
|
+
const { timeout = this.defaultTimeout, stableChecks = DEFAULT_STABILITY_CHECKS, stabilityInterval = DEFAULT_STABILITY_INTERVAL, message } = options;
|
|
191
|
+
const start = Date.now();
|
|
192
|
+
let lastCount = await getCount();
|
|
193
|
+
let stableCount = 0;
|
|
194
|
+
while (Date.now() - start < timeout) {
|
|
195
|
+
await this.sleep(stabilityInterval);
|
|
196
|
+
const currentCount = await getCount();
|
|
197
|
+
if (currentCount === lastCount) {
|
|
198
|
+
stableCount++;
|
|
199
|
+
if (stableCount >= stableChecks) {
|
|
200
|
+
return currentCount;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
stableCount = 0;
|
|
205
|
+
lastCount = currentCount;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
throw new Error(message || `Count did not stabilize within ${timeout}ms`);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Wait for text content to be present in an element.
|
|
212
|
+
* @param element The WebElement to check
|
|
213
|
+
* @param text Expected text (partial match)
|
|
214
|
+
* @param options Wait configuration options
|
|
215
|
+
*/
|
|
216
|
+
async forTextPresent(element, text, options = {}) {
|
|
217
|
+
const { timeout = this.defaultTimeout, message } = options;
|
|
218
|
+
await this.forCondition(async () => {
|
|
219
|
+
const elementText = await element.getText();
|
|
220
|
+
return elementText.includes(text);
|
|
221
|
+
}, {
|
|
222
|
+
...options,
|
|
223
|
+
timeout,
|
|
224
|
+
message: message || `Text '${text}' did not appear within ${timeout}ms`,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Wait for an element's class list to contain a specific class.
|
|
229
|
+
* @param element The WebElement to check
|
|
230
|
+
* @param className Class name to look for
|
|
231
|
+
* @param options Wait configuration options
|
|
232
|
+
*/
|
|
233
|
+
async forClass(element, className, options = {}) {
|
|
234
|
+
await this.forAttributeContains(element, 'class', className, {
|
|
235
|
+
...options,
|
|
236
|
+
message: options.message || `Element did not get class '${className}' within ${options.timeout || this.defaultTimeout}ms`,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Wait for an element's class list to NOT contain a specific class.
|
|
241
|
+
* @param element The WebElement to check
|
|
242
|
+
* @param className Class name that should not be present
|
|
243
|
+
* @param options Wait configuration options
|
|
244
|
+
*/
|
|
245
|
+
async forNoClass(element, className, options = {}) {
|
|
246
|
+
const { timeout = this.defaultTimeout, message } = options;
|
|
247
|
+
await this.forCondition(async () => {
|
|
248
|
+
const classAttr = await element.getAttribute('class');
|
|
249
|
+
return !classAttr?.includes(className);
|
|
250
|
+
}, {
|
|
251
|
+
...options,
|
|
252
|
+
timeout,
|
|
253
|
+
message: message || `Element still has class '${className}' after ${timeout}ms`,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Wait for an element to be clickable (visible AND enabled).
|
|
258
|
+
* @param element The WebElement to check
|
|
259
|
+
* @param options Wait configuration options
|
|
260
|
+
*/
|
|
261
|
+
async forClickable(element, options = {}) {
|
|
262
|
+
const { timeout = this.defaultTimeout, message } = options;
|
|
263
|
+
await this.forCondition(async () => {
|
|
264
|
+
try {
|
|
265
|
+
const isDisplayed = await element.isDisplayed();
|
|
266
|
+
const isEnabled = await element.isEnabled();
|
|
267
|
+
return isDisplayed && isEnabled;
|
|
268
|
+
}
|
|
269
|
+
catch (e) {
|
|
270
|
+
if (e instanceof selenium_webdriver_1.error.StaleElementReferenceError) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
throw e;
|
|
274
|
+
}
|
|
275
|
+
}, {
|
|
276
|
+
...options,
|
|
277
|
+
timeout,
|
|
278
|
+
message: message || `Element did not become clickable within ${timeout}ms`,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Wait for text content to match exactly or a regex pattern.
|
|
283
|
+
* @param element The WebElement to check
|
|
284
|
+
* @param expected Expected text string or RegExp pattern
|
|
285
|
+
* @param options Wait configuration options
|
|
286
|
+
*/
|
|
287
|
+
async forTextContent(element, expected, options = {}) {
|
|
288
|
+
const { timeout = this.defaultTimeout, message } = options;
|
|
289
|
+
await this.forCondition(async () => {
|
|
290
|
+
const text = await element.getText();
|
|
291
|
+
if (expected instanceof RegExp) {
|
|
292
|
+
return expected.test(text);
|
|
293
|
+
}
|
|
294
|
+
return text === expected;
|
|
295
|
+
}, {
|
|
296
|
+
...options,
|
|
297
|
+
timeout,
|
|
298
|
+
message: message || `Text did not match '${expected}' within ${timeout}ms`,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Wait for a specific number of elements to be present.
|
|
303
|
+
* @param parent Parent element or driver to search within
|
|
304
|
+
* @param locator Locator for the elements
|
|
305
|
+
* @param expectedCount Expected number of elements
|
|
306
|
+
* @param options Wait configuration options
|
|
307
|
+
*/
|
|
308
|
+
async forElementCount(parent, locator, expectedCount, options = {}) {
|
|
309
|
+
const { timeout = this.defaultTimeout, message } = options;
|
|
310
|
+
const result = await this.forCondition(async () => {
|
|
311
|
+
const elements = await parent.findElements(locator);
|
|
312
|
+
return elements.length === expectedCount ? elements : null;
|
|
313
|
+
}, {
|
|
314
|
+
...options,
|
|
315
|
+
timeout,
|
|
316
|
+
message: message || `Expected ${expectedCount} elements but condition not met within ${timeout}ms`,
|
|
317
|
+
});
|
|
318
|
+
return result;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Wait for at least one element matching the locator to be present.
|
|
322
|
+
* @param parent Parent element or driver to search within
|
|
323
|
+
* @param locator Locator for the element
|
|
324
|
+
* @param options Wait configuration options
|
|
325
|
+
* @returns The first matching element
|
|
326
|
+
*/
|
|
327
|
+
async forElementLocated(parent, locator, options = {}) {
|
|
328
|
+
const { timeout = this.defaultTimeout, message } = options;
|
|
329
|
+
const result = await this.forCondition(async () => {
|
|
330
|
+
const elements = await parent.findElements(locator);
|
|
331
|
+
return elements.length > 0 ? elements[0] : null;
|
|
332
|
+
}, {
|
|
333
|
+
...options,
|
|
334
|
+
timeout,
|
|
335
|
+
message: message || `Element not found within ${timeout}ms`,
|
|
336
|
+
});
|
|
337
|
+
return result;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Wait for no elements matching the locator to be present (element removed).
|
|
341
|
+
* @param parent Parent element or driver to search within
|
|
342
|
+
* @param locator Locator for the element
|
|
343
|
+
* @param options Wait configuration options
|
|
344
|
+
*/
|
|
345
|
+
async forNoElement(parent, locator, options = {}) {
|
|
346
|
+
const { timeout = this.defaultTimeout, message } = options;
|
|
347
|
+
await this.forCondition(async () => {
|
|
348
|
+
try {
|
|
349
|
+
const elements = await parent.findElements(locator);
|
|
350
|
+
return elements.length === 0;
|
|
351
|
+
}
|
|
352
|
+
catch (e) {
|
|
353
|
+
if (e instanceof selenium_webdriver_1.error.StaleElementReferenceError) {
|
|
354
|
+
return true; // Parent is gone, so element is definitely gone
|
|
355
|
+
}
|
|
356
|
+
throw e;
|
|
357
|
+
}
|
|
358
|
+
}, {
|
|
359
|
+
...options,
|
|
360
|
+
timeout,
|
|
361
|
+
message: message || `Element still present after ${timeout}ms`,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Wait for element to be removed from DOM or become stale.
|
|
366
|
+
* @param element The WebElement to monitor
|
|
367
|
+
* @param options Wait configuration options
|
|
368
|
+
*/
|
|
369
|
+
async forElementRemoved(element, options = {}) {
|
|
370
|
+
const { timeout = this.defaultTimeout, message } = options;
|
|
371
|
+
await this.forCondition(async () => {
|
|
372
|
+
try {
|
|
373
|
+
await element.getTagName(); // Will throw if element is stale
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
catch (e) {
|
|
377
|
+
if (e instanceof selenium_webdriver_1.error.StaleElementReferenceError || e instanceof selenium_webdriver_1.error.NoSuchElementError) {
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
throw e;
|
|
381
|
+
}
|
|
382
|
+
}, {
|
|
383
|
+
...options,
|
|
384
|
+
timeout,
|
|
385
|
+
message: message || `Element was not removed within ${timeout}ms`,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Wait for any of the conditions to be met (OR logic).
|
|
390
|
+
* @param conditions Array of condition functions
|
|
391
|
+
* @param options Wait configuration options
|
|
392
|
+
* @returns Index of the first condition that was met
|
|
393
|
+
*/
|
|
394
|
+
async forAnyCondition(conditions, options = {}) {
|
|
395
|
+
const { timeout = this.defaultTimeout, message } = options;
|
|
396
|
+
const result = await this.forCondition(async () => {
|
|
397
|
+
for (let i = 0; i < conditions.length; i++) {
|
|
398
|
+
try {
|
|
399
|
+
if (await conditions[i]()) {
|
|
400
|
+
return i;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
// Continue checking other conditions
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return null;
|
|
408
|
+
}, {
|
|
409
|
+
...options,
|
|
410
|
+
timeout,
|
|
411
|
+
message: message || `None of the conditions were met within ${timeout}ms`,
|
|
412
|
+
});
|
|
413
|
+
return result;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Execute a function with automatic retry on failure.
|
|
417
|
+
* @param fn The function to execute
|
|
418
|
+
* @param options Retry configuration options
|
|
419
|
+
* @returns The result of the function
|
|
420
|
+
*/
|
|
421
|
+
async withRetry(fn, options = {}) {
|
|
422
|
+
const { maxRetries = 3, retryDelay = 500, backoffMultiplier = 1, isRetryable = (e) => e.name === 'StaleElementReferenceError' || e.name === 'ElementNotInteractableError', } = options;
|
|
423
|
+
let lastError;
|
|
424
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
425
|
+
try {
|
|
426
|
+
return await fn();
|
|
427
|
+
}
|
|
428
|
+
catch (e) {
|
|
429
|
+
lastError = e;
|
|
430
|
+
if (!isRetryable(e) || attempt === maxRetries) {
|
|
431
|
+
throw e;
|
|
432
|
+
}
|
|
433
|
+
const delay = retryDelay * Math.pow(backoffMultiplier, attempt);
|
|
434
|
+
await this.sleep(delay);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
// This should never be reached as the loop always throws or returns
|
|
438
|
+
throw lastError ?? new Error('withRetry: unexpected state - no result and no error');
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Simple sleep helper - use sparingly and prefer condition-based waits.
|
|
442
|
+
* @param ms Milliseconds to sleep
|
|
443
|
+
*/
|
|
444
|
+
async sleep(ms) {
|
|
445
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Get the WebDriver instance.
|
|
449
|
+
*/
|
|
450
|
+
getDriver() {
|
|
451
|
+
return this.driver;
|
|
452
|
+
}
|
|
453
|
+
rectsEqual(a, b) {
|
|
454
|
+
return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
exports.WaitHelper = WaitHelper;
|
|
458
|
+
/**
|
|
459
|
+
* Create a WaitHelper instance bound to a WebDriver.
|
|
460
|
+
* Convenience factory function for quick access.
|
|
461
|
+
*/
|
|
462
|
+
function createWaitHelper(driver, defaultTimeout) {
|
|
463
|
+
return new WaitHelper(driver, defaultTimeout);
|
|
464
|
+
}
|
|
465
|
+
//# sourceMappingURL=WaitHelper.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redhat-developer/page-objects",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.18.0",
|
|
4
4
|
"description": "Page Object API implementation for a VS Code editor used by ExTester framework.",
|
|
5
5
|
"main": "out/index.js",
|
|
6
6
|
"types": "out/index.d.ts",
|
|
@@ -37,15 +37,15 @@
|
|
|
37
37
|
"ui-test"
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"clipboardy": "^5.0.
|
|
40
|
+
"clipboardy": "^5.0.2",
|
|
41
41
|
"clone-deep": "^4.0.1",
|
|
42
42
|
"compare-versions": "^6.1.1",
|
|
43
|
-
"fs-extra": "^11.3.
|
|
43
|
+
"fs-extra": "^11.3.3",
|
|
44
44
|
"type-fest": "^4.41.0"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"selenium-webdriver": ">=4.6.1",
|
|
48
48
|
"typescript": ">=4.6.2"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "41c9e28f480e39d23317c0e358c021873af48acf"
|
|
51
51
|
}
|