@redhat-developer/page-objects 1.20.0 → 1.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 (60) hide show
  1. package/out/components/AbstractElement.d.ts +1 -1
  2. package/out/components/AbstractElement.js +3 -2
  3. package/out/components/ElementWithContextMenu.d.ts +11 -0
  4. package/out/components/ElementWithContextMenu.js +63 -15
  5. package/out/components/WebviewMixin.js +73 -7
  6. package/out/components/activityBar/ActionsControl.js +1 -1
  7. package/out/components/activityBar/ViewControl.d.ts +16 -0
  8. package/out/components/activityBar/ViewControl.js +45 -8
  9. package/out/components/bottomBar/AbstractViews.js +2 -2
  10. package/out/components/bottomBar/BottomBarPanel.js +26 -11
  11. package/out/components/bottomBar/ProblemsView.js +2 -2
  12. package/out/components/bottomBar/Views.js +1 -1
  13. package/out/components/editor/ContentAssist.d.ts +9 -1
  14. package/out/components/editor/ContentAssist.js +98 -20
  15. package/out/components/editor/CustomEditor.js +2 -2
  16. package/out/components/editor/EditorAction.js +1 -1
  17. package/out/components/editor/EditorView.d.ts +6 -0
  18. package/out/components/editor/EditorView.js +73 -3
  19. package/out/components/editor/SettingsEditor.d.ts +1 -0
  20. package/out/components/editor/SettingsEditor.js +20 -4
  21. package/out/components/editor/TextEditor.d.ts +4 -1
  22. package/out/components/editor/TextEditor.js +99 -62
  23. package/out/components/editor/WebView.js +6 -1
  24. package/out/components/menu/ContextMenu.d.ts +1 -1
  25. package/out/components/menu/ContextMenu.js +9 -6
  26. package/out/components/menu/Menu.js +3 -1
  27. package/out/components/menu/TitleBar.js +1 -1
  28. package/out/components/sidebar/ViewItem.js +1 -0
  29. package/out/components/sidebar/ViewSection.js +2 -2
  30. package/out/components/sidebar/ViewTitlePart.js +2 -2
  31. package/out/components/sidebar/debug/DebugView.d.ts +10 -0
  32. package/out/components/sidebar/debug/DebugView.js +45 -13
  33. package/out/components/sidebar/extensions/ExtensionsViewItem.js +1 -1
  34. package/out/components/sidebar/scm/NewScmView.js +1 -1
  35. package/out/components/sidebar/scm/ScmView.js +2 -2
  36. package/out/components/sidebar/tree/custom/CustomTreeItem.js +2 -1
  37. package/out/components/sidebar/tree/debug/CallStackItem.js +1 -1
  38. package/out/components/sidebar/tree/debug/WatchSectionItem.js +1 -1
  39. package/out/components/sidebar/tree/default/DefaultTreeItem.js +3 -3
  40. package/out/components/sidebar/tree/default/DefaultTreeSection.js +6 -0
  41. package/out/components/statusBar/StatusBar.js +2 -2
  42. package/out/components/workbench/DebugToolbar.js +1 -1
  43. package/out/components/workbench/Notification.js +3 -3
  44. package/out/components/workbench/NotificationsCenter.js +6 -1
  45. package/out/components/workbench/Workbench.js +1 -1
  46. package/out/components/workbench/input/Input.d.ts +18 -0
  47. package/out/components/workbench/input/Input.js +70 -2
  48. package/out/components/workbench/input/InputBox.js +2 -2
  49. package/out/components/workbench/input/QuickOpenBox.js +2 -2
  50. package/out/errors/ExTesterError.js +1 -1
  51. package/out/index.d.ts +3 -1
  52. package/out/index.js +33 -3
  53. package/out/locators/loader.d.ts +2 -1
  54. package/out/locators/loader.js +2 -2
  55. package/out/locators/locators.d.ts +5 -2
  56. package/out/locators/locators.js +28 -5
  57. package/out/utils/TimeoutConstants.d.ts +59 -0
  58. package/out/utils/TimeoutConstants.js +63 -0
  59. package/out/utils/WaitHelper.js +8 -2
  60. package/package.json +8 -6
@@ -68,6 +68,7 @@ class TreeItem extends ViewItem {
68
68
  async expand() {
69
69
  if ((await this.isExpandable()) && !(await this.isExpanded())) {
70
70
  await (await this.findTwistie()).click();
71
+ await this.getDriver().wait(() => this.isExpanded(), 5000, 'Tree item did not expand within 5s');
71
72
  }
72
73
  }
73
74
  /**
@@ -35,7 +35,7 @@ class ViewSection extends AbstractElement_1.AbstractElement {
35
35
  */
36
36
  async getTitle() {
37
37
  const title = await this.findElement(ViewSection.locators.ViewSection.title);
38
- return await title.getAttribute(ViewSection.locators.ViewSection.titleText);
38
+ return (await title.getAttribute(ViewSection.locators.ViewSection.titleText));
39
39
  }
40
40
  /**
41
41
  * Expand the section if collapsed
@@ -186,7 +186,7 @@ class BaseViewPanelAction extends ActionButtonElementDropdown_1.ActionButtonElem
186
186
  * Get label of the action button
187
187
  */
188
188
  async getLabel() {
189
- return await this.getAttribute(BaseViewPanelAction.locators.ViewSection.buttonLabel);
189
+ return (await this.getAttribute(BaseViewPanelAction.locators.ViewSection.buttonLabel));
190
190
  }
191
191
  /**
192
192
  * Wait for the action button to be located within a given timeout.
@@ -42,7 +42,7 @@ class ViewTitlePart extends ElementWithContextMenu_1.ElementWithContextMenu {
42
42
  const actions = [];
43
43
  const elements = await this.findElements(ViewTitlePart.locators.ViewTitlePart.action);
44
44
  for (const element of elements) {
45
- const title = await element.getAttribute(ViewTitlePart.locators.ViewTitlePart.actionLabel);
45
+ const title = (await element.getAttribute(ViewTitlePart.locators.ViewTitlePart.actionLabel));
46
46
  actions.push(await new TitleActionButton(TitleActionButton.locators.ViewTitlePart.actionConstructor(title), this).wait());
47
47
  }
48
48
  return actions;
@@ -68,7 +68,7 @@ class TitleActionButton extends AbstractElement_1.AbstractElement {
68
68
  * Get title of the button
69
69
  */
70
70
  async getTitle() {
71
- return await this.getAttribute(TitleActionButton.locators.ViewTitlePart.actionLabel);
71
+ return (await this.getAttribute(TitleActionButton.locators.ViewTitlePart.actionLabel));
72
72
  }
73
73
  }
74
74
  exports.TitleActionButton = TitleActionButton;
@@ -45,6 +45,16 @@ export declare class DebugView extends SideBarView {
45
45
  * @param title title of the configuration to select
46
46
  */
47
47
  selectLaunchConfiguration(title: string): Promise<void>;
48
+ /**
49
+ * Opens the launch config dropdown overlay, waiting until it is visible.
50
+ * If it is already open (context-view-pointerBlock present) the click is skipped.
51
+ * @returns the context-view element to scope subsequent searches against
52
+ */
53
+ private openLaunchDropdown;
54
+ /**
55
+ * Closes the launch config dropdown by sending Escape to the filter input that holds focus.
56
+ */
57
+ private closeLaunchDropdown;
48
58
  /**
49
59
  * Start Debugging using the current launch configuration
50
60
  */
@@ -18,6 +18,7 @@
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.DebugView = void 0;
20
20
  const compare_versions_1 = require("compare-versions");
21
+ const selenium_webdriver_1 = require("selenium-webdriver");
21
22
  const SideBarView_1 = require("../SideBarView");
22
23
  const DebugBreakpointSection_1 = require("../tree/debug/DebugBreakpointSection");
23
24
  const DebugCallStackSection_1 = require("../tree/debug/DebugCallStackSection");
@@ -34,26 +35,22 @@ class DebugView extends SideBarView_1.SideBarView {
34
35
  */
35
36
  async getLaunchConfiguration() {
36
37
  if ((0, compare_versions_1.satisfies)(DebugView.versionInfo.version, '>=1.87.0') && process.platform !== 'darwin') {
37
- throw Error(`DEPRECATED METHOD! The 'DebugView.getLaunchConfiguration' method is broken! Read more information in 'Known Issues > Limitations in testing with VS Code 1.87+' - https://github.com/microsoft/vscode/issues/206897.`);
38
+ throw new Error(`DEPRECATED METHOD! The 'DebugView.getLaunchConfiguration' method is broken! Read more information in 'Known Issues > Limitations in testing with VS Code 1.87+' - https://github.com/microsoft/vscode/issues/206897.`);
38
39
  }
39
40
  const action = await this.getTitlePart().findElement(DebugView.locators.DebugView.launchCombo);
40
- const combo = await action.findElement(DebugView.locators.DebugView.launchSelect);
41
- return await combo.getAttribute(DebugView.locators.DebugView.launchSelectAttribute);
41
+ return await action.getText();
42
42
  }
43
43
  /**
44
44
  * Get titles of all available launch configurations
45
45
  * @returns Promise resolving to list of titles
46
46
  */
47
47
  async getLaunchConfigurations() {
48
- const action = await this.getTitlePart().findElement(DebugView.locators.DebugView.launchCombo);
49
- const combo = await action.findElement(DebugView.locators.DebugView.launchSelect);
48
+ const contextView = await this.openLaunchDropdown();
50
49
  const configs = [];
51
- const options = await combo.findElements(DebugView.locators.DebugView.launchOption);
50
+ const options = await contextView.findElements(DebugView.locators.DebugView.launchOption);
52
51
  for (const option of options) {
53
52
  try {
54
- if (await option.isEnabled()) {
55
- configs.push(await option.getAttribute('value'));
56
- }
53
+ configs.push((await option.getAttribute(DebugView.locators.DebugView.launchSelectAttribute)));
57
54
  }
58
55
  catch (e) {
59
56
  if (e.name === 'StaleElementReferenceError') {
@@ -62,6 +59,7 @@ class DebugView extends SideBarView_1.SideBarView {
62
59
  throw e;
63
60
  }
64
61
  }
62
+ await this.closeLaunchDropdown();
65
63
  return configs;
66
64
  }
67
65
  async getVariablesSection() {
@@ -81,10 +79,44 @@ class DebugView extends SideBarView_1.SideBarView {
81
79
  * @param title title of the configuration to select
82
80
  */
83
81
  async selectLaunchConfiguration(title) {
84
- const action = await this.getTitlePart().findElement(DebugView.locators.DebugView.launchCombo);
85
- const combo = await action.findElement(DebugView.locators.DebugView.launchSelect);
86
- const option = await combo.findElement(DebugView.locators.DebugView.optionByName(title));
87
- await option.click();
82
+ const contextView = await this.openLaunchDropdown();
83
+ const option = await contextView.findElement(DebugView.locators.DebugView.optionByName(title));
84
+ // context-view-pointerBlock overlays the list and intercepts regular clicks —
85
+ // use JavaScript click to bypass it.
86
+ await this.getDriver().executeScript('arguments[0].click()', option);
87
+ // Wait for the overlay to be dismissed so subsequent operations are not blocked.
88
+ await this.getDriver()
89
+ .wait(selenium_webdriver_1.until.elementIsNotVisible(contextView), 2000)
90
+ .catch(() => {
91
+ // If already gone, that's fine.
92
+ });
93
+ }
94
+ /**
95
+ * Opens the launch config dropdown overlay, waiting until it is visible.
96
+ * If it is already open (context-view-pointerBlock present) the click is skipped.
97
+ * @returns the context-view element to scope subsequent searches against
98
+ */
99
+ async openLaunchDropdown() {
100
+ // context-view persists in the DOM between uses (display:none when closed).
101
+ // Only click to open if it is not currently visible.
102
+ const existingViews = await this.getDriver().findElements(DebugView.locators.ContextMenu.contextView);
103
+ const isOpen = existingViews.length > 0 && (await existingViews[0].isDisplayed());
104
+ if (!isOpen) {
105
+ const action = await this.getTitlePart().findElement(DebugView.locators.DebugView.launchCombo);
106
+ await action.click();
107
+ }
108
+ const contextView = await this.getDriver().wait(selenium_webdriver_1.until.elementLocated(DebugView.locators.ContextMenu.contextView), 2000);
109
+ await this.getDriver().wait(selenium_webdriver_1.until.elementIsVisible(contextView), 2000);
110
+ // Wait until at least one option row is present inside the overlay
111
+ await this.getDriver().wait(async () => (await contextView.findElements(DebugView.locators.DebugView.launchOption)).length > 0, 2000);
112
+ return contextView;
113
+ }
114
+ /**
115
+ * Closes the launch config dropdown by sending Escape to the filter input that holds focus.
116
+ */
117
+ async closeLaunchDropdown() {
118
+ const input = await this.getDriver().findElement(selenium_webdriver_1.By.className('action-list-filter-input'));
119
+ await input.sendKeys('\uE00C'); // Escape
88
120
  }
89
121
  /**
90
122
  * Start Debugging using the current launch configuration
@@ -42,7 +42,7 @@ class ExtensionsViewItem extends ViewItem_1.ViewItem {
42
42
  if (version.length > 0) {
43
43
  return await version[0].getText();
44
44
  }
45
- const label = await this.getAttribute('aria-label');
45
+ const label = (await this.getAttribute('aria-label'));
46
46
  const ver = label.split(',')[1].trim();
47
47
  return ver;
48
48
  }
@@ -64,7 +64,7 @@ class SingleScmProvider extends ScmView_1.ScmProvider {
64
64
  const buttons = [];
65
65
  if ((0, compare_versions_1.satisfies)(ScmView_1.ScmProvider.versionInfo.version, '>=1.93.0')) {
66
66
  actions = await this.getProviderHeaderActions(view);
67
- names = await Promise.all(actions.map(async (action) => await action.getAttribute(ScmView_1.ScmProvider.locators.ScmView.actionLabel)));
67
+ names = await Promise.all(actions.map(async (action) => (await action.getAttribute(ScmView_1.ScmProvider.locators.ScmView.actionLabel))));
68
68
  }
69
69
  else {
70
70
  const titlePart = view.getTitlePart();
@@ -75,13 +75,13 @@ class ScmProvider extends AbstractElement_1.AbstractElement {
75
75
  * Get title of the scm provider
76
76
  */
77
77
  async getTitle() {
78
- return await this.findElement(ScmProvider.locators.ScmView.providerTitle).getAttribute('innerHTML');
78
+ return (await this.findElement(ScmProvider.locators.ScmView.providerTitle).getAttribute('innerHTML'));
79
79
  }
80
80
  /**
81
81
  * Get type of the scm provider (e.g. Git)
82
82
  */
83
83
  async getType() {
84
- return await this.findElement(ScmProvider.locators.ScmView.providerType).getAttribute('innerHTML');
84
+ return (await this.findElement(ScmProvider.locators.ScmView.providerType).getAttribute('innerHTML'));
85
85
  }
86
86
  /**
87
87
  * Find an action button for the SCM provider by title and click it. (e.g 'Commit')
@@ -29,7 +29,8 @@ class CustomTreeItem extends ViewItem_1.TreeItem {
29
29
  return await this.findElement(CustomTreeItem.locators.CustomTreeSection.itemLabel).getText();
30
30
  }
31
31
  async getTooltip() {
32
- return await this.getAttribute(CustomTreeItem.locators.CustomTreeItem.tooltipAttribute);
32
+ const resourceItem = await this.findElement(CustomTreeItem.locators.CustomTreeItem.tooltipElement);
33
+ return (await resourceItem.getAttribute(CustomTreeItem.locators.CustomTreeItem.tooltipAttribute));
33
34
  }
34
35
  async getDescription() {
35
36
  return await this.findElement(CustomTreeItem.locators.CustomTreeItem.description).getText();
@@ -28,7 +28,7 @@ class CallStackItem extends CustomTreeItem_1.CustomTreeItem {
28
28
  */
29
29
  async getLabel() {
30
30
  const name = await this.findElement(CallStackItem.locators.CallStackItem.label);
31
- return await name?.getAttribute('textContent');
31
+ return (await name?.getAttribute('textContent'));
32
32
  }
33
33
  /**
34
34
  * Get text of the Call Stack item.
@@ -28,7 +28,7 @@ class WatchSectionItem extends CustomTreeItem_1.CustomTreeItem {
28
28
  */
29
29
  async getLabel() {
30
30
  const name = await this.findElement(WatchSectionItem.locators.WatchSectionItem.label);
31
- return await name?.getAttribute('textContent');
31
+ return (await name?.getAttribute('textContent'));
32
32
  }
33
33
  /**
34
34
  * Get value of the Watch section item.
@@ -35,10 +35,10 @@ class DefaultTreeItem extends ViewItem_1.TreeItem {
35
35
  }
36
36
  async getTooltip() {
37
37
  const tooltip = await this.findElement(DefaultTreeItem.locators.DefaultTreeItem.tooltip);
38
- return await tooltip.getAttribute(DefaultTreeItem.locators.DefaultTreeItem.labelAttribute);
38
+ return (await tooltip.getAttribute(DefaultTreeItem.locators.DefaultTreeItem.labelAttribute));
39
39
  }
40
40
  async isExpanded() {
41
- const twistieClass = await this.findElement(DefaultTreeItem.locators.DefaultTreeItem.twistie).getAttribute('class');
41
+ const twistieClass = (await this.findElement(DefaultTreeItem.locators.DefaultTreeItem.twistie).getAttribute('class'));
42
42
  return twistieClass.indexOf('collapsed') < 0;
43
43
  }
44
44
  async getChildren() {
@@ -47,7 +47,7 @@ class DefaultTreeItem extends ViewItem_1.TreeItem {
47
47
  return items;
48
48
  }
49
49
  async isExpandable() {
50
- const twistieClass = await this.findElement(DefaultTreeItem.locators.DefaultTreeItem.twistie).getAttribute('class');
50
+ const twistieClass = (await this.findElement(DefaultTreeItem.locators.DefaultTreeItem.twistie).getAttribute('class'));
51
51
  return twistieClass.indexOf('collapsible') > -1;
52
52
  }
53
53
  }
@@ -45,7 +45,13 @@ class DefaultTreeSection extends TreeSection_1.TreeSection {
45
45
  const container = await this.findElement(DefaultTreeSection.locators.DefaultTreeSection.rowContainer);
46
46
  await container.sendKeys(selenium_webdriver_1.Key.HOME);
47
47
  let item = undefined;
48
+ const maxIterations = 500;
49
+ let iteration = 0;
48
50
  do {
51
+ if (iteration++ >= maxIterations) {
52
+ console.warn(`findItem('${label}'): exceeded ${maxIterations} scroll iterations, giving up`);
53
+ break;
54
+ }
49
55
  const temp = await container.findElements(DefaultTreeSection.locators.DefaultTreeItem.ctor(label));
50
56
  if (temp.length > 0) {
51
57
  const level = +(await temp[0].getAttribute(DefaultTreeSection.locators.ViewSection.level));
@@ -157,7 +157,7 @@ class StatusBar extends AbstractElement_1.AbstractElement {
157
157
  async toggleNotificationsCentre(open) {
158
158
  let visible = false;
159
159
  try {
160
- const klass = await this.enclosingItem.findElement(StatusBar.locators.StatusBar.notifications).getAttribute('class');
160
+ const klass = (await this.enclosingItem.findElement(StatusBar.locators.StatusBar.notifications).getAttribute('class'));
161
161
  visible = klass.indexOf('visible') > -1;
162
162
  }
163
163
  catch (err) {
@@ -168,7 +168,7 @@ class StatusBar extends AbstractElement_1.AbstractElement {
168
168
  }
169
169
  }
170
170
  async getPartText(locator) {
171
- return await this.findElement(locator).findElement(StatusBar.locators.StatusBar.aTag).getAttribute('innerHTML');
171
+ return (await this.findElement(locator).findElement(StatusBar.locators.StatusBar.aTag).getAttribute('innerHTML'));
172
172
  }
173
173
  }
174
174
  exports.StatusBar = StatusBar;
@@ -42,7 +42,7 @@ class DebugToolbar extends AbstractElement_1.AbstractElement {
42
42
  * Wait for the execution to pause at the next breakpoint
43
43
  */
44
44
  async waitForBreakPoint(timeout = 10_000) {
45
- let btn = await this.getDriver().wait(selenium_webdriver_1.until.elementLocated(DebugToolbar.locators.DebugToolbar.button('continue')));
45
+ let btn = await this.getDriver().wait(selenium_webdriver_1.until.elementLocated(DebugToolbar.locators.DebugToolbar.button('continue')), timeout);
46
46
  await this.getDriver().wait(async () => {
47
47
  try {
48
48
  const enabled = await btn.isEnabled();
@@ -47,7 +47,7 @@ class Notification extends ElementWithContextMenu_1.ElementWithContextMenu {
47
47
  * @returns Promise resolving to NotificationType
48
48
  */
49
49
  async getType() {
50
- const iconType = await (await this.findElement(Notification.locators.Notification.icon)).getAttribute('class');
50
+ const iconType = (await (await this.findElement(Notification.locators.Notification.icon)).getAttribute('class'));
51
51
  if (iconType.indexOf('icon-info') > -1) {
52
52
  return NotificationType.Info;
53
53
  }
@@ -68,14 +68,14 @@ class Notification extends ElementWithContextMenu_1.ElementWithContextMenu {
68
68
  throw Error(`DEPRECATED METHOD! For VS Code 1.88+ this method won't be working any more.`);
69
69
  }
70
70
  await this.expand();
71
- return await (await this.findElement(Notification.locators.Notification.source)).getAttribute('title');
71
+ return (await (await this.findElement(Notification.locators.Notification.source)).getAttribute('title'));
72
72
  }
73
73
  /**
74
74
  * Find whether the notification has an active progress bar
75
75
  * @returns Promise resolving to true/false
76
76
  */
77
77
  async hasProgress() {
78
- const klass = await (await this.findElement(Notification.locators.Notification.progress)).getAttribute('class');
78
+ const klass = (await (await this.findElement(Notification.locators.Notification.progress)).getAttribute('class'));
79
79
  return klass.indexOf('done') < 0;
80
80
  }
81
81
  /**
@@ -48,7 +48,12 @@ class NotificationsCenter extends AbstractElement_1.AbstractElement {
48
48
  * @returns Promise resolving when the clear all button is pressed
49
49
  */
50
50
  async clearAllNotifications() {
51
- await this.findElement(NotificationsCenter.locators.NotificationsCenter.clear).click();
51
+ const btn = await this.findElement(NotificationsCenter.locators.NotificationsCenter.clear);
52
+ const classes = (await btn.getAttribute('class')) ?? '';
53
+ if (classes.includes('disabled')) {
54
+ return;
55
+ }
56
+ await this.getDriver().executeScript('arguments[0].click()', btn);
52
57
  }
53
58
  /**
54
59
  * Get all notifications of a given type
@@ -115,7 +115,7 @@ class Workbench extends AbstractElement_1.AbstractElement {
115
115
  async openSettings() {
116
116
  await this.executeCommand('Preferences: Open User Settings');
117
117
  await new EditorView_1.EditorView().openEditor('Settings');
118
- await Workbench.driver.wait(selenium_webdriver_1.until.elementLocated(Workbench.locators.Editor.constructor));
118
+ await Workbench.driver.wait(selenium_webdriver_1.until.elementLocated(Workbench.locators.Editor.constructor), 10_000);
119
119
  // Wait for settings editor to fully load
120
120
  const editor = new SettingsEditor_1.SettingsEditor();
121
121
  await this.getWaitHelper().forStable(editor, { timeout: 2000 });
@@ -41,6 +41,24 @@ export declare abstract class Input extends AbstractElement {
41
41
  * @returns Promise resolving when the input is confirmed
42
42
  */
43
43
  confirm(): Promise<void>;
44
+ /**
45
+ * Confirm path selection for file/folder dialog.
46
+ *
47
+ * VS Code's file picker auto-navigates as text is typed: when a full path such as
48
+ * `/dir/file.txt` is in the input, the picker shows the parent directory contents
49
+ * with the filename filtered in the list. A single Enter press navigates into the
50
+ * matched list entry. A subsequent click on the OK button (or a second Enter) then
51
+ * confirms the selection and closes the dialog.
52
+ *
53
+ * When the path is not formatted to OS specifics there may first be a 'Select' step.
54
+ * See also https://github.com/redhat-developer/vscode-extension-tester/issues/1778
55
+ */
56
+ confirmPath(): Promise<void>;
57
+ /**
58
+ * Best-effort check whether this input/picker widget is still displayed.
59
+ * A hidden, detached or stale widget counts as closed.
60
+ */
61
+ private isPickerOpen;
44
62
  /**
45
63
  * Cancel the input field by pressing Escape
46
64
  * @returns Promise resolving when the input is cancelled
@@ -31,7 +31,7 @@ class Input extends AbstractElement_1.AbstractElement {
31
31
  */
32
32
  async getText() {
33
33
  const input = await this.findElement(Input.locators.Input.inputBox).findElement(Input.locators.Input.input);
34
- return await input.getAttribute('value');
34
+ return (await input.getAttribute('value'));
35
35
  }
36
36
  /**
37
37
  * Set (by selecting all and typing) text in the input field
@@ -75,7 +75,7 @@ class Input extends AbstractElement_1.AbstractElement {
75
75
  * @returns Promise resolving to input placeholder
76
76
  */
77
77
  async getPlaceHolder() {
78
- return await this.findElement(Input.locators.Input.inputBox).findElement(Input.locators.Input.input).getAttribute('placeholder');
78
+ return (await this.findElement(Input.locators.Input.inputBox).findElement(Input.locators.Input.input).getAttribute('placeholder'));
79
79
  }
80
80
  /**
81
81
  * Confirm the input field by pressing Enter
@@ -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
@@ -42,7 +42,7 @@ class InputBox extends __1.Input {
42
42
  return await this.findElement(InputBox.locators.InputBox.message).getText();
43
43
  }
44
44
  async hasProgress() {
45
- const klass = await this.findElement(InputBox.locators.InputBox.progress).getAttribute('class');
45
+ const klass = (await this.findElement(InputBox.locators.InputBox.progress).getAttribute('class'));
46
46
  return klass.indexOf('done') < 0;
47
47
  }
48
48
  async getQuickPicks() {
@@ -90,7 +90,7 @@ class InputBox extends __1.Input {
90
90
  * @returns Promise resolving to notification message
91
91
  */
92
92
  async hasError() {
93
- const klass = await this.findElement(InputBox.locators.Input.inputBox).getAttribute('class');
93
+ const klass = (await this.findElement(InputBox.locators.Input.inputBox).getAttribute('class'));
94
94
  return klass.indexOf('error') > -1;
95
95
  }
96
96
  /**
@@ -32,11 +32,11 @@ 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() {
39
- const klass = await this.findElement(QuickOpenBox.locators.QuickOpenBox.progress).getAttribute('class');
39
+ const klass = (await this.findElement(QuickOpenBox.locators.QuickOpenBox.progress).getAttribute('class'));
40
40
  return klass.indexOf('done') < 0;
41
41
  }
42
42
  async getQuickPicks() {
@@ -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 = 'StaleElementError';
104
+ this.name = 'StaleElementReferenceError';
105
105
  }
106
106
  }
107
107
  exports.StaleElementError = StaleElementError;
package/out/index.d.ts CHANGED
@@ -15,11 +15,13 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  import { WebDriver } from 'selenium-webdriver';
18
+ export { AbstractElement } from './components/AbstractElement';
18
19
  export * from 'selenium-webdriver';
19
20
  export * from './locators/locators';
20
21
  export * from './errors/NullAttributeError';
21
22
  export * from './errors/ExTesterError';
22
23
  export * from './utils/WaitHelper';
24
+ export * from './utils/TimeoutConstants';
23
25
  export * from './components/menu/Menu';
24
26
  export * from './components/menu/MenuItem';
25
27
  export * from './components/menu/TitleBar';
@@ -88,4 +90,4 @@ export * from './conditions/WaitForAttribute';
88
90
  * @param driver WebDriver instance
89
91
  * @param browserID identifier/name of the browser (i.e. vscode)
90
92
  */
91
- export declare function initPageObjects(currentVersion: string, baseVersion: string, locatorFolder: string, driver: WebDriver, browserID: string): void;
93
+ export declare function initPageObjects(currentVersion: string, baseVersion: string, locatorFolder: string, driver: WebDriver, browserID: string, customLocatorsPath?: string): void;
package/out/index.js CHANGED
@@ -26,19 +26,45 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
26
26
  if (k2 === undefined) k2 = k;
27
27
  o[k2] = m[k];
28
28
  }));
29
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
30
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
31
+ }) : function(o, v) {
32
+ o["default"] = v;
33
+ });
34
+ var __importStar = (this && this.__importStar) || (function () {
35
+ var ownKeys = function(o) {
36
+ ownKeys = Object.getOwnPropertyNames || function (o) {
37
+ var ar = [];
38
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
39
+ return ar;
40
+ };
41
+ return ownKeys(o);
42
+ };
43
+ return function (mod) {
44
+ if (mod && mod.__esModule) return mod;
45
+ var result = {};
46
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
47
+ __setModuleDefault(result, mod);
48
+ return result;
49
+ };
50
+ })();
29
51
  var __exportStar = (this && this.__exportStar) || function(m, exports) {
30
52
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
31
53
  };
32
54
  Object.defineProperty(exports, "__esModule", { value: true });
33
- exports.NotificationType = exports.Notification = exports.ScmChange = exports.ScmProvider = exports.ScmView = void 0;
55
+ exports.NotificationType = exports.Notification = exports.ScmChange = exports.ScmProvider = exports.ScmView = exports.AbstractElement = void 0;
34
56
  exports.initPageObjects = initPageObjects;
57
+ const path = __importStar(require("node:path"));
35
58
  const AbstractElement_1 = require("./components/AbstractElement");
36
59
  const loader_1 = require("./locators/loader");
60
+ var AbstractElement_2 = require("./components/AbstractElement");
61
+ Object.defineProperty(exports, "AbstractElement", { enumerable: true, get: function () { return AbstractElement_2.AbstractElement; } });
37
62
  __exportStar(require("selenium-webdriver"), exports);
38
63
  __exportStar(require("./locators/locators"), exports);
39
64
  __exportStar(require("./errors/NullAttributeError"), exports);
40
65
  __exportStar(require("./errors/ExTesterError"), exports);
41
66
  __exportStar(require("./utils/WaitHelper"), exports);
67
+ __exportStar(require("./utils/TimeoutConstants"), exports);
42
68
  __exportStar(require("./components/menu/Menu"), exports);
43
69
  __exportStar(require("./components/menu/MenuItem"), exports);
44
70
  __exportStar(require("./components/menu/TitleBar"), exports);
@@ -112,8 +138,12 @@ __exportStar(require("./conditions/WaitForAttribute"), exports);
112
138
  * @param driver WebDriver instance
113
139
  * @param browserID identifier/name of the browser (i.e. vscode)
114
140
  */
115
- function initPageObjects(currentVersion, baseVersion, locatorFolder, driver, browserID) {
116
- const locators = new loader_1.LocatorLoader(currentVersion, baseVersion, locatorFolder).loadLocators();
141
+ function initPageObjects(currentVersion, baseVersion, locatorFolder, driver, browserID, customLocatorsPath) {
142
+ let locators = new loader_1.LocatorLoader(currentVersion, baseVersion, locatorFolder).loadLocators();
143
+ if (customLocatorsPath) {
144
+ const custom = require(path.resolve(customLocatorsPath));
145
+ locators = (0, loader_1.mergeLocators)(locators, custom);
146
+ }
117
147
  AbstractElement_1.AbstractElement.init(locators, driver, browserID, currentVersion);
118
148
  }
119
149
  //# sourceMappingURL=index.js.map
@@ -14,7 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- import { Locators } from './locators';
17
+ import { Locators, LocatorDiff } from './locators';
18
18
  /**
19
19
  * Utility for loading locators for a given vscode version
20
20
  */
@@ -34,3 +34,4 @@ export declare class LocatorLoader {
34
34
  */
35
35
  loadLocators(): Locators;
36
36
  }
37
+ export declare function mergeLocators(original: Locators, diff: LocatorDiff): Locators;
@@ -53,6 +53,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
53
53
  };
54
54
  Object.defineProperty(exports, "__esModule", { value: true });
55
55
  exports.LocatorLoader = void 0;
56
+ exports.mergeLocators = mergeLocators;
56
57
  const fs = __importStar(require("fs-extra"));
57
58
  const path = __importStar(require("path"));
58
59
  const compare_versions_1 = require("compare-versions");
@@ -102,8 +103,7 @@ class LocatorLoader {
102
103
  }
103
104
  for (const version of versions) {
104
105
  const diff = require(path.join(this.baseFolder, version)).diff;
105
- const newLocators = mergeLocators(this.locators, diff);
106
- this.locators = newLocators;
106
+ this.locators = mergeLocators(this.locators, diff);
107
107
  }
108
108
  return this.locators;
109
109
  }