@redhat-developer/page-objects 1.21.0 → 1.23.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/README.md +59 -4
- package/out/components/AbstractElement.d.ts +8 -0
- package/out/components/AbstractElement.js +12 -1
- package/out/components/ElementWithContextMenu.d.ts +11 -0
- package/out/components/ElementWithContextMenu.js +63 -15
- package/out/components/WebviewMixin.js +73 -7
- package/out/components/activityBar/ViewControl.d.ts +16 -0
- package/out/components/activityBar/ViewControl.js +45 -8
- package/out/components/bottomBar/BottomBarPanel.js +26 -11
- package/out/components/editor/ContentAssist.d.ts +9 -1
- package/out/components/editor/ContentAssist.js +88 -13
- package/out/components/editor/CustomEditor.js +1 -1
- package/out/components/editor/EditorView.d.ts +6 -0
- package/out/components/editor/EditorView.js +53 -3
- package/out/components/editor/SettingsEditor.d.ts +1 -0
- package/out/components/editor/SettingsEditor.js +17 -1
- package/out/components/editor/TextEditor.d.ts +3 -1
- package/out/components/editor/TextEditor.js +95 -56
- package/out/components/editor/WebView.js +6 -1
- package/out/components/menu/ContextMenu.d.ts +1 -1
- package/out/components/menu/ContextMenu.js +7 -4
- package/out/components/menu/Menu.js +3 -1
- package/out/components/sidebar/ViewItem.js +1 -0
- package/out/components/sidebar/tree/default/DefaultTreeSection.js +6 -0
- package/out/components/workbench/DebugToolbar.js +1 -1
- package/out/components/workbench/NotificationsCenter.js +6 -1
- package/out/components/workbench/Workbench.js +1 -1
- package/out/components/workbench/input/Input.d.ts +18 -0
- package/out/components/workbench/input/Input.js +68 -0
- package/out/components/workbench/input/QuickOpenBox.js +1 -1
- package/out/errors/ExTesterError.js +1 -1
- package/out/index.d.ts +1 -0
- package/out/index.js +1 -0
- package/out/locators/locators.js +26 -3
- package/out/utils/TimeoutConstants.d.ts +59 -0
- package/out/utils/TimeoutConstants.js +63 -0
- package/out/utils/WaitHelper.d.ts +31 -0
- package/out/utils/WaitHelper.js +78 -2
- package/package.json +6 -3
|
@@ -86,6 +86,74 @@ class Input extends AbstractElement_1.AbstractElement {
|
|
|
86
86
|
await input.click();
|
|
87
87
|
await input.sendKeys(selenium_webdriver_1.Key.ENTER);
|
|
88
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Confirm path selection for file/folder dialog.
|
|
91
|
+
*
|
|
92
|
+
* VS Code's file picker auto-navigates as text is typed: when a full path such as
|
|
93
|
+
* `/dir/file.txt` is in the input, the picker shows the parent directory contents
|
|
94
|
+
* with the filename filtered in the list. A single Enter press navigates into the
|
|
95
|
+
* matched list entry. A subsequent click on the OK button (or a second Enter) then
|
|
96
|
+
* confirms the selection and closes the dialog.
|
|
97
|
+
*
|
|
98
|
+
* When the path is not formatted to OS specifics there may first be a 'Select' step.
|
|
99
|
+
* See also https://github.com/redhat-developer/vscode-extension-tester/issues/1778
|
|
100
|
+
*/
|
|
101
|
+
async confirmPath() {
|
|
102
|
+
// Step 1 — drive VS Code's file picker to navigate into the typed path.
|
|
103
|
+
// Sending Enter selects the highlighted entry in the suggestion list, which
|
|
104
|
+
// causes the picker to consume the directory portion and reveal the target file.
|
|
105
|
+
const inputEl = await this.findElement(Input.locators.Input.inputBox).findElement(Input.locators.Input.input);
|
|
106
|
+
await inputEl.sendKeys(selenium_webdriver_1.Key.ENTER);
|
|
107
|
+
// When the typed path uniquely resolves the target, the first Enter both
|
|
108
|
+
// navigates and confirms — the picker closes on its own and no second step
|
|
109
|
+
// is needed (attempting one would hit a hidden input).
|
|
110
|
+
if (!(await this.isPickerOpen())) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Step 2 — click the OK button to confirm and close the dialog.
|
|
114
|
+
// Use a JS click to bypass any overlay that may intercept a normal WebDriver click.
|
|
115
|
+
// Falls back to a second Enter if no matching button is found.
|
|
116
|
+
try {
|
|
117
|
+
const buttons = await this.findElements(selenium_webdriver_1.By.className('monaco-button'));
|
|
118
|
+
for (const btn of buttons) {
|
|
119
|
+
if (await btn.isDisplayed()) {
|
|
120
|
+
const label = ((await btn.getText()) || (await btn.getAttribute('aria-label')) || '').trim();
|
|
121
|
+
if (label === 'Select' || label === 'Ok' || label === 'OK') {
|
|
122
|
+
await Input.driver.executeScript('arguments[0].click()', btn);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
// ignore if button is not found or stale
|
|
130
|
+
}
|
|
131
|
+
if (!(await this.isPickerOpen())) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
await inputEl.sendKeys(selenium_webdriver_1.Key.ENTER);
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
// The picker can close in the instant between the check above and the
|
|
139
|
+
// keystroke — a failure only matters while the picker is still open.
|
|
140
|
+
if (await this.isPickerOpen()) {
|
|
141
|
+
throw err;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Best-effort check whether this input/picker widget is still displayed.
|
|
147
|
+
* A hidden, detached or stale widget counts as closed.
|
|
148
|
+
*/
|
|
149
|
+
async isPickerOpen() {
|
|
150
|
+
try {
|
|
151
|
+
return await this.isDisplayed();
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
89
157
|
/**
|
|
90
158
|
* Cancel the input field by pressing Escape
|
|
91
159
|
* @returns Promise resolving when the input is cancelled
|
|
@@ -32,7 +32,7 @@ class QuickOpenBox extends __1.Input {
|
|
|
32
32
|
* Use when a quick open box is scheduled to appear.
|
|
33
33
|
*/
|
|
34
34
|
static async create() {
|
|
35
|
-
await QuickOpenBox.driver.wait(selenium_webdriver_1.until.elementLocated(QuickOpenBox.locators.QuickOpenBox.constructor));
|
|
35
|
+
await QuickOpenBox.driver.wait(selenium_webdriver_1.until.elementLocated(QuickOpenBox.locators.QuickOpenBox.constructor), 10_000);
|
|
36
36
|
return new QuickOpenBox().wait();
|
|
37
37
|
}
|
|
38
38
|
async hasProgress() {
|
|
@@ -101,7 +101,7 @@ exports.ElementNotInteractableError = ElementNotInteractableError;
|
|
|
101
101
|
class StaleElementError extends ExTesterError {
|
|
102
102
|
constructor(message, context = {}) {
|
|
103
103
|
super(message, context);
|
|
104
|
-
this.name = '
|
|
104
|
+
this.name = 'StaleElementReferenceError';
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
exports.StaleElementError = StaleElementError;
|
package/out/index.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export * from './locators/locators';
|
|
|
21
21
|
export * from './errors/NullAttributeError';
|
|
22
22
|
export * from './errors/ExTesterError';
|
|
23
23
|
export * from './utils/WaitHelper';
|
|
24
|
+
export * from './utils/TimeoutConstants';
|
|
24
25
|
export * from './components/menu/Menu';
|
|
25
26
|
export * from './components/menu/MenuItem';
|
|
26
27
|
export * from './components/menu/TitleBar';
|
package/out/index.js
CHANGED
|
@@ -64,6 +64,7 @@ __exportStar(require("./locators/locators"), exports);
|
|
|
64
64
|
__exportStar(require("./errors/NullAttributeError"), exports);
|
|
65
65
|
__exportStar(require("./errors/ExTesterError"), exports);
|
|
66
66
|
__exportStar(require("./utils/WaitHelper"), exports);
|
|
67
|
+
__exportStar(require("./utils/TimeoutConstants"), exports);
|
|
67
68
|
__exportStar(require("./components/menu/Menu"), exports);
|
|
68
69
|
__exportStar(require("./components/menu/MenuItem"), exports);
|
|
69
70
|
__exportStar(require("./components/menu/TitleBar"), exports);
|
package/out/locators/locators.js
CHANGED
|
@@ -66,14 +66,37 @@ function fromText(locator) {
|
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
async function findBestContainingElement(container, testElements) {
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
if (testElements.length === 0) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
// Short-circuit: single candidate needs no geometry.
|
|
73
|
+
if (testElements.length === 1) {
|
|
74
|
+
return testElements[0];
|
|
75
|
+
}
|
|
76
|
+
const rects = await Promise.all(testElements.map((el) => el.getRect()));
|
|
77
|
+
// When the container has not been laid out yet its rect is {0,0,0,0}.
|
|
78
|
+
// In that case overlap with any non-zero-position iframe would be negative
|
|
79
|
+
// for all candidates, causing the function to incorrectly return undefined.
|
|
80
|
+
// Fall back to picking the element with the largest own area so that the
|
|
81
|
+
// poll loop in switchToFrame can still make progress.
|
|
82
|
+
const containerArea = container.width * container.height;
|
|
83
|
+
if (containerArea === 0) {
|
|
84
|
+
let bestIdx = 0;
|
|
85
|
+
for (let i = 1; i < rects.length; i++) {
|
|
86
|
+
if (rects[i].width * rects[i].height > rects[bestIdx].width * rects[bestIdx].height) {
|
|
87
|
+
bestIdx = i;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return testElements[bestIdx];
|
|
91
|
+
}
|
|
92
|
+
// Normal case: pick the iframe whose intersection with the container is largest.
|
|
93
|
+
const areas = rects.map((rect) => {
|
|
71
94
|
const ax = Math.max(container.x, rect.x);
|
|
72
95
|
const ay = Math.max(container.y, rect.y);
|
|
73
96
|
const bx = Math.min(container.x + container.width, rect.x + rect.width);
|
|
74
97
|
const by = Math.min(container.y + container.height, rect.y + rect.height);
|
|
75
98
|
return (bx - ax) * (by - ay);
|
|
76
|
-
})
|
|
99
|
+
});
|
|
77
100
|
let bestIdx = -1;
|
|
78
101
|
for (let i = 0; i < testElements.length; i++) {
|
|
79
102
|
if (areas[i] < 0) {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
3
|
+
* contributor license agreements. See the NOTICE file distributed with
|
|
4
|
+
* this work for additional information regarding copyright ownership.
|
|
5
|
+
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
6
|
+
* (the "License", destination); you may not use this file except in compliance with
|
|
7
|
+
* the License. You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Centralized timeout constants for page-object operations.
|
|
19
|
+
* Components should reference these instead of hardcoding values.
|
|
20
|
+
* Consumers can override by assigning to the mutable fields.
|
|
21
|
+
*/
|
|
22
|
+
export declare const TimeoutConstants: {
|
|
23
|
+
/** Default wait for an element to become visible (ms) */
|
|
24
|
+
ELEMENT_VISIBLE: number;
|
|
25
|
+
/** Default wait for an element to become stable (ms) */
|
|
26
|
+
ELEMENT_STABLE: number;
|
|
27
|
+
/** Wait for a menu to appear after context-click (ms) */
|
|
28
|
+
MENU_APPEAR: number;
|
|
29
|
+
/** Wait for a menu item count to stabilize (ms) */
|
|
30
|
+
MENU_STABLE: number;
|
|
31
|
+
/** Wait for an input box to appear (ms) */
|
|
32
|
+
INPUT_APPEAR: number;
|
|
33
|
+
/** Wait for quick open / command palette (ms) */
|
|
34
|
+
QUICK_OPEN: number;
|
|
35
|
+
/** Wait for editor to load (ms) */
|
|
36
|
+
EDITOR_LOAD: number;
|
|
37
|
+
/** Wait for content assist to populate (ms) */
|
|
38
|
+
CONTENT_ASSIST: number;
|
|
39
|
+
/** Wait for a tree section row container (ms) */
|
|
40
|
+
TREE_SECTION: number;
|
|
41
|
+
/** Wait for extension install from marketplace (ms) */
|
|
42
|
+
EXTENSION_INSTALL: number;
|
|
43
|
+
/** Wait for notifications to appear/dismiss (ms) */
|
|
44
|
+
NOTIFICATION: number;
|
|
45
|
+
/** Close all editors timeout (ms) */
|
|
46
|
+
CLOSE_ALL_EDITORS: number;
|
|
47
|
+
/** Wait for dialog / workbench elements (ms) */
|
|
48
|
+
DIALOG: number;
|
|
49
|
+
/** Debug toolbar / breakpoint wait (ms) */
|
|
50
|
+
DEBUG: number;
|
|
51
|
+
/** Webview frame switching (ms) */
|
|
52
|
+
WEBVIEW: number;
|
|
53
|
+
/** Wait for clipboard operations (ms) */
|
|
54
|
+
CLIPBOARD: number;
|
|
55
|
+
/** Default poll interval for condition waits (ms) */
|
|
56
|
+
POLL_INTERVAL: number;
|
|
57
|
+
/** Default number of stability checks */
|
|
58
|
+
STABILITY_CHECKS: number;
|
|
59
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
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.TimeoutConstants = void 0;
|
|
20
|
+
/**
|
|
21
|
+
* Centralized timeout constants for page-object operations.
|
|
22
|
+
* Components should reference these instead of hardcoding values.
|
|
23
|
+
* Consumers can override by assigning to the mutable fields.
|
|
24
|
+
*/
|
|
25
|
+
exports.TimeoutConstants = {
|
|
26
|
+
/** Default wait for an element to become visible (ms) */
|
|
27
|
+
ELEMENT_VISIBLE: 5_000,
|
|
28
|
+
/** Default wait for an element to become stable (ms) */
|
|
29
|
+
ELEMENT_STABLE: 2_000,
|
|
30
|
+
/** Wait for a menu to appear after context-click (ms) */
|
|
31
|
+
MENU_APPEAR: 2_000,
|
|
32
|
+
/** Wait for a menu item count to stabilize (ms) */
|
|
33
|
+
MENU_STABLE: 1_000,
|
|
34
|
+
/** Wait for an input box to appear (ms) */
|
|
35
|
+
INPUT_APPEAR: 5_000,
|
|
36
|
+
/** Wait for quick open / command palette (ms) */
|
|
37
|
+
QUICK_OPEN: 10_000,
|
|
38
|
+
/** Wait for editor to load (ms) */
|
|
39
|
+
EDITOR_LOAD: 5_000,
|
|
40
|
+
/** Wait for content assist to populate (ms) */
|
|
41
|
+
CONTENT_ASSIST: 10_000,
|
|
42
|
+
/** Wait for a tree section row container (ms) */
|
|
43
|
+
TREE_SECTION: 5_000,
|
|
44
|
+
/** Wait for extension install from marketplace (ms) */
|
|
45
|
+
EXTENSION_INSTALL: 300_000,
|
|
46
|
+
/** Wait for notifications to appear/dismiss (ms) */
|
|
47
|
+
NOTIFICATION: 2_000,
|
|
48
|
+
/** Close all editors timeout (ms) */
|
|
49
|
+
CLOSE_ALL_EDITORS: 60_000,
|
|
50
|
+
/** Wait for dialog / workbench elements (ms) */
|
|
51
|
+
DIALOG: 5_000,
|
|
52
|
+
/** Debug toolbar / breakpoint wait (ms) */
|
|
53
|
+
DEBUG: 10_000,
|
|
54
|
+
/** Webview frame switching (ms) */
|
|
55
|
+
WEBVIEW: 5_000,
|
|
56
|
+
/** Wait for clipboard operations (ms) */
|
|
57
|
+
CLIPBOARD: 2_000,
|
|
58
|
+
/** Default poll interval for condition waits (ms) */
|
|
59
|
+
POLL_INTERVAL: 100,
|
|
60
|
+
/** Default number of stability checks */
|
|
61
|
+
STABILITY_CHECKS: 3,
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=TimeoutConstants.js.map
|
|
@@ -189,6 +189,37 @@ export declare class WaitHelper {
|
|
|
189
189
|
* @param ms Milliseconds to sleep
|
|
190
190
|
*/
|
|
191
191
|
sleep(ms: number): Promise<void>;
|
|
192
|
+
/**
|
|
193
|
+
* Move the virtual pointer to a quiet spot: the title-bar drag region at the
|
|
194
|
+
* top-center of the window (the command center is disabled by the framework's
|
|
195
|
+
* default settings, and unlike the status bar the area has no hover targets).
|
|
196
|
+
*
|
|
197
|
+
* A WebDriver session has no OS mouse - the pointer rests wherever the last
|
|
198
|
+
* interaction left it. VS Code hovers appear while the pointer rests on an
|
|
199
|
+
* element and are torn down only when it moves away, so parking delivers the
|
|
200
|
+
* mouseout an open hover is waiting for and disarms pending hover timers.
|
|
201
|
+
*/
|
|
202
|
+
parkPointer(): Promise<void>;
|
|
203
|
+
/**
|
|
204
|
+
* Click an element, recovering when a transient overlay intercepts the click.
|
|
205
|
+
*
|
|
206
|
+
* VS Code renders rich hovers/tooltips as DOM overlays that appear while the
|
|
207
|
+
* pointer rests on the last click point and never auto-hide. A W3C click
|
|
208
|
+
* hit-tests the click point BEFORE dispatching any event, so a resting hover
|
|
209
|
+
* over the target fails every attempt until the pointer actually moves -
|
|
210
|
+
* waiting alone can never resolve it. On interception this actively parks
|
|
211
|
+
* the pointer (dismissing pointer-rest overlays), waits for hover teardown
|
|
212
|
+
* and retries the native click. As a last resort it falls back to a
|
|
213
|
+
* JS-executor click, which bypasses hit-testing but also mousedown semantics
|
|
214
|
+
* (Monaco lists select on mousedown) - the fallback is logged so the call
|
|
215
|
+
* site's true blocker can be root-caused instead of silently papered over.
|
|
216
|
+
*
|
|
217
|
+
* @param element the element to click
|
|
218
|
+
* @param nativeClick override for the native click action (used by
|
|
219
|
+
* AbstractElement.click() to reach WebElement.prototype.click through its
|
|
220
|
+
* own override)
|
|
221
|
+
*/
|
|
222
|
+
clickThroughInterception(element: WebElement, nativeClick?: () => Promise<void>): Promise<void>;
|
|
192
223
|
/**
|
|
193
224
|
* Get the WebDriver instance.
|
|
194
225
|
*/
|
package/out/utils/WaitHelper.js
CHANGED
|
@@ -54,8 +54,14 @@ class WaitHelper {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
catch (e) {
|
|
57
|
-
// Store error but continue retrying for transient
|
|
58
|
-
|
|
57
|
+
// Store error but continue retrying for transient failures.
|
|
58
|
+
// TimeoutError from driver.wait(until.elementIsVisible) means the element
|
|
59
|
+
// exists but is not yet visible — the condition should be retried rather
|
|
60
|
+
// than aborting the outer poll loop.
|
|
61
|
+
if (e instanceof selenium_webdriver_1.error.StaleElementReferenceError ||
|
|
62
|
+
e instanceof selenium_webdriver_1.error.NoSuchElementError ||
|
|
63
|
+
e instanceof selenium_webdriver_1.error.TimeoutError ||
|
|
64
|
+
e?.name === 'TimeoutError') {
|
|
59
65
|
lastError = e;
|
|
60
66
|
}
|
|
61
67
|
else {
|
|
@@ -444,6 +450,76 @@ class WaitHelper {
|
|
|
444
450
|
async sleep(ms) {
|
|
445
451
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
446
452
|
}
|
|
453
|
+
/**
|
|
454
|
+
* Move the virtual pointer to a quiet spot: the title-bar drag region at the
|
|
455
|
+
* top-center of the window (the command center is disabled by the framework's
|
|
456
|
+
* default settings, and unlike the status bar the area has no hover targets).
|
|
457
|
+
*
|
|
458
|
+
* A WebDriver session has no OS mouse - the pointer rests wherever the last
|
|
459
|
+
* interaction left it. VS Code hovers appear while the pointer rests on an
|
|
460
|
+
* element and are torn down only when it moves away, so parking delivers the
|
|
461
|
+
* mouseout an open hover is waiting for and disarms pending hover timers.
|
|
462
|
+
*/
|
|
463
|
+
async parkPointer() {
|
|
464
|
+
const width = Number(await this.driver.executeScript('return window.innerWidth')) || 200;
|
|
465
|
+
await this.driver
|
|
466
|
+
.actions()
|
|
467
|
+
.move({ x: Math.floor(width / 2), y: 5 })
|
|
468
|
+
.perform();
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Click an element, recovering when a transient overlay intercepts the click.
|
|
472
|
+
*
|
|
473
|
+
* VS Code renders rich hovers/tooltips as DOM overlays that appear while the
|
|
474
|
+
* pointer rests on the last click point and never auto-hide. A W3C click
|
|
475
|
+
* hit-tests the click point BEFORE dispatching any event, so a resting hover
|
|
476
|
+
* over the target fails every attempt until the pointer actually moves -
|
|
477
|
+
* waiting alone can never resolve it. On interception this actively parks
|
|
478
|
+
* the pointer (dismissing pointer-rest overlays), waits for hover teardown
|
|
479
|
+
* and retries the native click. As a last resort it falls back to a
|
|
480
|
+
* JS-executor click, which bypasses hit-testing but also mousedown semantics
|
|
481
|
+
* (Monaco lists select on mousedown) - the fallback is logged so the call
|
|
482
|
+
* site's true blocker can be root-caused instead of silently papered over.
|
|
483
|
+
*
|
|
484
|
+
* @param element the element to click
|
|
485
|
+
* @param nativeClick override for the native click action (used by
|
|
486
|
+
* AbstractElement.click() to reach WebElement.prototype.click through its
|
|
487
|
+
* own override)
|
|
488
|
+
*/
|
|
489
|
+
async clickThroughInterception(element, nativeClick = () => element.click()) {
|
|
490
|
+
const maxNativeAttempts = 3;
|
|
491
|
+
for (let attempt = 0;; attempt++) {
|
|
492
|
+
try {
|
|
493
|
+
return await nativeClick();
|
|
494
|
+
}
|
|
495
|
+
catch (e) {
|
|
496
|
+
if (e.name !== 'ElementClickInterceptedError') {
|
|
497
|
+
throw e;
|
|
498
|
+
}
|
|
499
|
+
// a modal dialog blocks the whole workbench by design - neither
|
|
500
|
+
// pointer parking nor a JS click may subvert it: JS-clicking
|
|
501
|
+
// through the blocker would trigger actions the real UI forbids.
|
|
502
|
+
// Fail fast with the original error so modal-aware callers (and
|
|
503
|
+
// test authors) see the true blocker.
|
|
504
|
+
const modalUp = await this.driver
|
|
505
|
+
.executeScript('return Array.from(document.querySelectorAll(".monaco-dialog-modal-block, .monaco-dialog-box")).some((el) => el.checkVisibility());')
|
|
506
|
+
.catch(() => false);
|
|
507
|
+
if (modalUp) {
|
|
508
|
+
throw e;
|
|
509
|
+
}
|
|
510
|
+
if (attempt >= maxNativeAttempts - 1) {
|
|
511
|
+
console.warn(`clickThroughInterception: falling back to a JS click after ${maxNativeAttempts} intercepted attempts: ${e.message}`);
|
|
512
|
+
await this.driver.executeScript('arguments[0].click();', element);
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
await this.parkPointer();
|
|
516
|
+
// hover teardown is event-driven and near-instant once the pointer
|
|
517
|
+
// moved away; a non-hover interceptor (modal, toast) never passes
|
|
518
|
+
// this check, so time-box it and retry regardless
|
|
519
|
+
await this.forCondition(async () => await this.driver.executeScript('return !Array.from(document.querySelectorAll(".monaco-hover")).some((el) => el.checkVisibility());'), { timeout: 1500, pollInterval: 50, message: 'hover overlay did not dismiss after parking the pointer' }).catch(() => undefined);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
447
523
|
/**
|
|
448
524
|
* Get the WebDriver instance.
|
|
449
525
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redhat-developer/page-objects",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.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",
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
},
|
|
18
18
|
"author": "Red Hat",
|
|
19
19
|
"license": "Apache-2.0",
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=22"
|
|
22
|
+
},
|
|
20
23
|
"publishConfig": {
|
|
21
24
|
"access": "public"
|
|
22
25
|
},
|
|
@@ -43,8 +46,8 @@
|
|
|
43
46
|
"fs-extra": "^11.4.0"
|
|
44
47
|
},
|
|
45
48
|
"peerDependencies": {
|
|
46
|
-
"selenium-webdriver": "
|
|
49
|
+
"selenium-webdriver": "^4.48.0",
|
|
47
50
|
"typescript": ">=4.6.2"
|
|
48
51
|
},
|
|
49
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "2333228475aff111f91b91d06e50a5b98e371886"
|
|
50
53
|
}
|