@redhat-developer/page-objects 1.19.1 → 1.21.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 (38) hide show
  1. package/out/components/AbstractElement.d.ts +1 -1
  2. package/out/components/AbstractElement.js +1 -1
  3. package/out/components/activityBar/ActionsControl.js +1 -1
  4. package/out/components/activityBar/ViewControl.js +4 -4
  5. package/out/components/bottomBar/AbstractViews.js +2 -2
  6. package/out/components/bottomBar/ProblemsView.js +2 -2
  7. package/out/components/bottomBar/Views.js +1 -1
  8. package/out/components/editor/ContentAssist.js +10 -7
  9. package/out/components/editor/CustomEditor.js +1 -1
  10. package/out/components/editor/EditorAction.js +1 -1
  11. package/out/components/editor/SettingsEditor.js +3 -3
  12. package/out/components/editor/TextEditor.d.ts +1 -0
  13. package/out/components/editor/TextEditor.js +20 -10
  14. package/out/components/menu/ContextMenu.js +2 -2
  15. package/out/components/menu/TitleBar.js +1 -1
  16. package/out/components/sidebar/ViewSection.js +2 -2
  17. package/out/components/sidebar/ViewTitlePart.js +2 -2
  18. package/out/components/sidebar/debug/DebugView.d.ts +10 -0
  19. package/out/components/sidebar/debug/DebugView.js +45 -13
  20. package/out/components/sidebar/extensions/ExtensionsViewItem.js +1 -1
  21. package/out/components/sidebar/scm/NewScmView.js +1 -1
  22. package/out/components/sidebar/scm/ScmView.js +2 -2
  23. package/out/components/sidebar/tree/custom/CustomTreeItem.js +2 -1
  24. package/out/components/sidebar/tree/debug/CallStackItem.js +1 -1
  25. package/out/components/sidebar/tree/debug/WatchSectionItem.js +1 -1
  26. package/out/components/sidebar/tree/default/DefaultTreeItem.js +3 -3
  27. package/out/components/statusBar/StatusBar.js +2 -2
  28. package/out/components/workbench/Notification.js +3 -3
  29. package/out/components/workbench/input/Input.js +2 -2
  30. package/out/components/workbench/input/InputBox.js +2 -2
  31. package/out/components/workbench/input/QuickOpenBox.js +1 -1
  32. package/out/index.d.ts +2 -1
  33. package/out/index.js +32 -3
  34. package/out/locators/loader.d.ts +2 -1
  35. package/out/locators/loader.js +2 -2
  36. package/out/locators/locators.d.ts +5 -2
  37. package/out/locators/locators.js +2 -2
  38. package/package.json +4 -5
@@ -33,7 +33,7 @@ export interface LocatorInfo {
33
33
  export declare abstract class AbstractElement extends WebElement {
34
34
  static readonly ctlKey: string;
35
35
  protected static driver: WebDriver;
36
- protected static locators: Locators;
36
+ protected static locators: Locators & Record<string, Record<string, unknown>>;
37
37
  protected static versionInfo: {
38
38
  version: string;
39
39
  browser: string;
@@ -252,7 +252,7 @@ class AbstractElement extends selenium_webdriver_1.WebElement {
252
252
  */
253
253
  async safeGetAttribute(name) {
254
254
  return await this.withRecovery(async (self) => {
255
- return await self.getAttribute(name);
255
+ return (await self.getAttribute(name));
256
256
  });
257
257
  }
258
258
  }
@@ -36,7 +36,7 @@ class ActionsControl extends ElementWithContextMenu_1.ElementWithContextMenu {
36
36
  * Returns the title of the associated action
37
37
  */
38
38
  async getTitle() {
39
- return await this.getAttribute('aria-label');
39
+ return (await this.getAttribute('aria-label'));
40
40
  }
41
41
  }
42
42
  exports.ActionsControl = ActionsControl;
@@ -34,12 +34,12 @@ class ViewControl extends ElementWithContextMenu_1.ElementWithContextMenu {
34
34
  */
35
35
  async openView() {
36
36
  // Check whether view is already open
37
- const klass = await this.getAttribute(ViewControl.locators.ViewControl.attribute);
37
+ const klass = (await this.getAttribute(ViewControl.locators.ViewControl.attribute));
38
38
  if (klass.indexOf(ViewControl.locators.ViewControl.klass) < 0) {
39
39
  await this.click();
40
40
  // Wait for view to be marked as active
41
41
  await this.getWaitHelper().forCondition(async () => {
42
- const newKlass = await this.getAttribute(ViewControl.locators.ViewControl.attribute);
42
+ const newKlass = (await this.getAttribute(ViewControl.locators.ViewControl.attribute));
43
43
  return newKlass.includes(ViewControl.locators.ViewControl.klass);
44
44
  }, { timeout: 2000, pollInterval: 100 });
45
45
  }
@@ -60,7 +60,7 @@ class ViewControl extends ElementWithContextMenu_1.ElementWithContextMenu {
60
60
  * @returns Promise resolving when the view closes
61
61
  */
62
62
  async closeView() {
63
- const klass = await this.getAttribute(ViewControl.locators.ViewControl.attribute);
63
+ const klass = (await this.getAttribute(ViewControl.locators.ViewControl.attribute));
64
64
  if (klass.indexOf(ViewControl.locators.ViewControl.klass) > -1) {
65
65
  await this.click();
66
66
  }
@@ -70,7 +70,7 @@ class ViewControl extends ElementWithContextMenu_1.ElementWithContextMenu {
70
70
  */
71
71
  async getTitle() {
72
72
  const badge = await this.findElement(ViewControl.locators.ViewControl.badge);
73
- return await badge.getAttribute('aria-label');
73
+ return (await badge.getAttribute('aria-label'));
74
74
  }
75
75
  }
76
76
  exports.ViewControl = ViewControl;
@@ -35,7 +35,7 @@ class ChannelView extends ElementWithContextMenu_1.ElementWithContextMenu {
35
35
  await Promise.all(elements.map(async (element) => {
36
36
  const disabled = await element.getAttribute('disabled');
37
37
  if (!disabled) {
38
- names.push(await element.getAttribute('value'));
38
+ names.push((await element.getAttribute('value')));
39
39
  }
40
40
  }));
41
41
  return names;
@@ -50,7 +50,7 @@ class ChannelView extends ElementWithContextMenu_1.ElementWithContextMenu {
50
50
  throw Error(`DEPRECATED METHOD! The 'ChannelView.getCurrentChannel' method is broken! Read more information in 'Known Issues > Limitations in testing with VS Code 1.87+' - https://github.com/microsoft/vscode/issues/206897.`);
51
51
  }
52
52
  const combo = await this.enclosingItem.findElement(ChannelView.locators.BottomBarViews.channelCombo);
53
- return await combo.getAttribute(ChannelView.locators.OutputView.currentChannelAttribute);
53
+ return (await combo.getAttribute(ChannelView.locators.OutputView.currentChannelAttribute));
54
54
  }
55
55
  /**
56
56
  * Select a channel using the selector combo
@@ -125,7 +125,7 @@ class Marker extends ElementWithContextMenu_1.ElementWithContextMenu {
125
125
  * @returns Promise resolving to a Marker row text
126
126
  */
127
127
  async getText() {
128
- return await this.getAttribute(ProblemsView.locators.ProblemsView.rowLabel);
128
+ return (await this.getAttribute(ProblemsView.locators.ProblemsView.rowLabel));
129
129
  }
130
130
  /**
131
131
  * Get the Marker label text
@@ -141,7 +141,7 @@ class Marker extends ElementWithContextMenu_1.ElementWithContextMenu {
141
141
  */
142
142
  async toggleExpand(expand) {
143
143
  if ((await this.getType()) === MarkerType.File) {
144
- const klass = await this.findElement(ProblemsView.locators.ProblemsView.markerTwistie).getAttribute('class');
144
+ const klass = (await this.findElement(ProblemsView.locators.ProblemsView.markerTwistie).getAttribute('class'));
145
145
  if (klass.indexOf('collapsed') > -1 === expand) {
146
146
  await this.click();
147
147
  }
@@ -226,7 +226,7 @@ class TerminalView extends AbstractViews_1.ChannelView {
226
226
  const rows = await list.findElements(TerminalView.locators.TerminalView.row);
227
227
  for (const row of rows) {
228
228
  const label = await row.getAttribute('aria-label');
229
- if (label.includes(channelNumber)) {
229
+ if (label?.includes(channelNumber)) {
230
230
  await row.click();
231
231
  return;
232
232
  }
@@ -63,7 +63,7 @@ class ContentAssist extends __1.Menu {
63
63
  async getItems() {
64
64
  await this.getDriver().wait(async () => {
65
65
  return await this.isLoaded();
66
- });
66
+ }, 10000);
67
67
  const elements = await this.findElement(ContentAssist.locators.ContentAssist.itemRows).findElements(ContentAssist.locators.ContentAssist.itemRow);
68
68
  const items = [];
69
69
  for (const item of elements) {
@@ -84,12 +84,15 @@ class ContentAssist extends __1.Menu {
84
84
  * to false otherwise
85
85
  */
86
86
  async isLoaded() {
87
- const message = await this.findElement(ContentAssist.locators.ContentAssist.message);
88
- if (await message.isDisplayed()) {
89
- if ((await message.getText()).startsWith('No suggestions')) {
90
- return true;
91
- }
92
- return false;
87
+ const messages = await this.findElements(ContentAssist.locators.ContentAssist.message);
88
+ if (messages.length === 0) {
89
+ return true;
90
+ }
91
+ if (await messages[0].isDisplayed()) {
92
+ const text = await messages[0].getText();
93
+ // Empty message means loaded; "No suggestions" is also a terminal state.
94
+ // Any other non-empty text (e.g. "Loading...") means still loading.
95
+ return text === '' || text.startsWith('No suggestions');
93
96
  }
94
97
  return true;
95
98
  }
@@ -36,7 +36,7 @@ class CustomEditor extends __1.Editor {
36
36
  */
37
37
  async isDirty() {
38
38
  const tab = await this.getTab();
39
- const klass = await tab.getAttribute('class');
39
+ const klass = (await tab.getAttribute('class'));
40
40
  return klass.includes('dirty');
41
41
  }
42
42
  /**
@@ -26,7 +26,7 @@ class BaseEditorAction extends ActionButtonElementDropdown_1.ActionButtonElement
26
26
  * Get text description of the action.
27
27
  */
28
28
  async getTitle() {
29
- return await this.getAttribute(BaseEditorAction.locators.EditorView.attribute);
29
+ return (await this.getAttribute(BaseEditorAction.locators.EditorView.attribute));
30
30
  }
31
31
  }
32
32
  class EditorAction extends BaseEditorAction {
@@ -203,7 +203,7 @@ exports.Setting = Setting;
203
203
  class ComboSetting extends Setting {
204
204
  async getValue() {
205
205
  const combo = await this.findElement(SettingsEditor.locators.SettingsEditor.comboSetting);
206
- return await combo.getAttribute(SettingsEditor.locators.SettingsEditor.comboValue);
206
+ return (await combo.getAttribute(SettingsEditor.locators.SettingsEditor.comboValue));
207
207
  }
208
208
  async setValue(value) {
209
209
  const rows = await this.getOptions();
@@ -233,7 +233,7 @@ class ComboSetting extends Setting {
233
233
  const rows = await this.getOptions();
234
234
  for (const row of rows) {
235
235
  try {
236
- values.push(await row.getAttribute(SettingsEditor.locators.SettingsEditor.comboValue));
236
+ values.push((await row.getAttribute(SettingsEditor.locators.SettingsEditor.comboValue)));
237
237
  }
238
238
  catch (e) {
239
239
  if (e.name === 'StaleElementReferenceError') {
@@ -256,7 +256,7 @@ exports.ComboSetting = ComboSetting;
256
256
  class TextSetting extends Setting {
257
257
  async getValue() {
258
258
  const input = await this.findElement(SettingsEditor.locators.SettingsEditor.textSetting);
259
- return await input.getAttribute('value');
259
+ return (await input.getAttribute('value'));
260
260
  }
261
261
  async setValue(value) {
262
262
  const input = await this.findElement(SettingsEditor.locators.SettingsEditor.textSetting);
@@ -223,6 +223,7 @@ export declare class TextEditor extends Editor {
223
223
  * **Caution** line & column coordinates do not start at `0` but at `1`!
224
224
  */
225
225
  getCoordinates(): Promise<[number, number]>;
226
+ private getTabSize;
226
227
  /**
227
228
  * Toggle breakpoint on a given line
228
229
  *
@@ -40,7 +40,7 @@ class TextEditor extends Editor_1.Editor {
40
40
  */
41
41
  async isDirty() {
42
42
  const tab = await this.enclosingItem.findElement(TextEditor.locators.TextEditor.activeTab);
43
- const klass = await tab.getAttribute('class');
43
+ const klass = (await tab.getAttribute('class'));
44
44
  return klass.indexOf('dirty') >= 0;
45
45
  }
46
46
  /**
@@ -67,7 +67,7 @@ class TextEditor extends Editor_1.Editor {
67
67
  */
68
68
  async getFileUri() {
69
69
  const ed = await this.findElement(TextEditor.locators.TextEditor.editorContainer);
70
- return await ed.getAttribute(TextEditor.locators.TextEditor.dataUri);
70
+ return (await ed.getAttribute(TextEditor.locators.TextEditor.dataUri));
71
71
  }
72
72
  /**
73
73
  * Retrieve the path to the file opened in the active editor
@@ -87,7 +87,7 @@ class TextEditor extends Editor_1.Editor {
87
87
  let isHidden = true;
88
88
  try {
89
89
  const assist = await self.findElement(TextEditor.locators.ContentAssist.constructor);
90
- const klass = await assist.getAttribute('class');
90
+ const klass = (await assist.getAttribute('class'));
91
91
  const visibility = await assist.getCssValue('visibility');
92
92
  isHidden = klass.indexOf('visible') < 0 || visibility === 'hidden';
93
93
  }
@@ -236,7 +236,7 @@ class TextEditor extends Editor_1.Editor {
236
236
  if (lineNum < 1) {
237
237
  throw new Error(`Text '${text}' not found`);
238
238
  }
239
- await this.moveCursor(lineNum, column);
239
+ await this.setCursor(lineNum, column);
240
240
  let actions = this.getDriver().actions();
241
241
  await actions.clear();
242
242
  actions.keyDown(selenium_webdriver_1.Key.SHIFT);
@@ -320,7 +320,7 @@ class TextEditor extends Editor_1.Editor {
320
320
  * @returns Promise resolving when the text is typed in
321
321
  */
322
322
  async typeTextAt(line, column, text) {
323
- await this.moveCursor(line, column);
323
+ await this.setCursor(line, column);
324
324
  await this.typeText(text);
325
325
  }
326
326
  /**
@@ -489,11 +489,15 @@ class TextEditor extends Editor_1.Editor {
489
489
  let column = -1;
490
490
  let found = 0;
491
491
  const lines = (await this.getText()).split('\n');
492
+ const tabSize = await this.getTabSize();
492
493
  for (let i = 0; i < lines.length; i++) {
493
494
  let searchIndex = -1; // start at the beginning of the line
494
495
  while ((searchIndex = lines[i].indexOf(text, searchIndex)) > -1) {
495
496
  found++;
496
- column = searchIndex + 1;
497
+ // Compute offset for tabs
498
+ const nTabs = lines[i].substring(0, searchIndex + 1).split('\t').length - 1;
499
+ const tabOffset = nTabs * (tabSize - 1); // 1 character per tab has already been counted ('\t') - now offset for the tab size
500
+ column = searchIndex + 1 + tabOffset;
497
501
  line = i + 1;
498
502
  if (found >= occurrence) {
499
503
  return [line, column];
@@ -559,6 +563,12 @@ class TextEditor extends Editor_1.Editor {
559
563
  }
560
564
  return [coords[0], coords[1]];
561
565
  }
566
+ async getTabSize() {
567
+ const statusBar = new StatusBar_1.StatusBar();
568
+ const indent = await statusBar.getCurrentIndentation();
569
+ const matches = indent.match(/\d+/g);
570
+ return +matches[0];
571
+ }
562
572
  /**
563
573
  * Toggle breakpoint on a given line
564
574
  *
@@ -795,7 +805,7 @@ class CodeLens extends AbstractElement_1.AbstractElement {
795
805
  * @returns tooltip as string
796
806
  */
797
807
  async getTooltip() {
798
- return await this.getAttribute('title');
808
+ return (await this.getAttribute('title'));
799
809
  }
800
810
  }
801
811
  exports.CodeLens = CodeLens;
@@ -817,7 +827,7 @@ class FindWidget extends AbstractElement_1.AbstractElement {
817
827
  */
818
828
  async toggleReplace(replace) {
819
829
  const btn = await this.findElement(FindWidget.locators.FindWidget.toggleReplace);
820
- const klass = await btn.getAttribute('class');
830
+ const klass = (await btn.getAttribute('class'));
821
831
  if ((replace && klass.includes('collapsed')) || (!replace && !klass.includes('collapsed'))) {
822
832
  await btn.sendKeys(selenium_webdriver_1.Key.SPACE);
823
833
  const repl = await this.getDriver().wait(selenium_webdriver_1.until.elementLocated(FindWidget.locators.FindWidget.replacePart), 2000);
@@ -843,7 +853,7 @@ class FindWidget extends AbstractElement_1.AbstractElement {
843
853
  */
844
854
  async getSearchText() {
845
855
  const findPart = await this.findElement(FindWidget.locators.FindWidget.findPart);
846
- return await this.getInputText(findPart);
856
+ return (await this.getInputText(findPart));
847
857
  }
848
858
  /**
849
859
  * Set text in the replace box. Will toggle replace mode on if called in find mode.
@@ -860,7 +870,7 @@ class FindWidget extends AbstractElement_1.AbstractElement {
860
870
  */
861
871
  async getReplaceText() {
862
872
  const replacePart = await this.findElement(FindWidget.locators.FindWidget.replacePart);
863
- return await this.getInputText(replacePart);
873
+ return (await this.getInputText(replacePart));
864
874
  }
865
875
  /**
866
876
  * Click 'Next match'
@@ -61,7 +61,7 @@ class ContextMenu extends __1.Menu {
61
61
  const elements = await this.findElements(ContextMenu.locators.ContextMenu.itemElement);
62
62
  for (const element of elements) {
63
63
  try {
64
- const klass = await element.getAttribute('class');
64
+ const klass = (await element.getAttribute('class'));
65
65
  if (klass.indexOf('disabled') < 0) {
66
66
  items.push(await new ContextMenuItem(element, this).wait());
67
67
  }
@@ -158,7 +158,7 @@ class ContextMenuItem extends __1.MenuItem {
158
158
  }
159
159
  async getLabel() {
160
160
  const labelItem = await this.findElement(ContextMenu.locators.ContextMenu.itemLabel);
161
- return await labelItem.getAttribute(ContextMenu.locators.ContextMenu.itemText);
161
+ return (await labelItem.getAttribute(ContextMenu.locators.ContextMenu.itemText));
162
162
  }
163
163
  async isNesting() {
164
164
  try {
@@ -52,7 +52,7 @@ class TitleBar extends Menu_1.Menu {
52
52
  for (const element of elements) {
53
53
  try {
54
54
  if (await element.isDisplayed()) {
55
- items.push(await new TitleBarItem(await element.getAttribute(TitleBar.locators.TitleBar.itemLabel), this).wait());
55
+ items.push(await new TitleBarItem((await element.getAttribute(TitleBar.locators.TitleBar.itemLabel)), this).wait());
56
56
  }
57
57
  }
58
58
  catch (e) {
@@ -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
  }
@@ -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;
@@ -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
  /**
@@ -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
@@ -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
  /**
@@ -36,7 +36,7 @@ class QuickOpenBox extends __1.Input {
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() {
package/out/index.d.ts CHANGED
@@ -15,6 +15,7 @@
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';
@@ -88,4 +89,4 @@ export * from './conditions/WaitForAttribute';
88
89
  * @param driver WebDriver instance
89
90
  * @param browserID identifier/name of the browser (i.e. vscode)
90
91
  */
91
- export declare function initPageObjects(currentVersion: string, baseVersion: string, locatorFolder: string, driver: WebDriver, browserID: string): void;
92
+ export declare function initPageObjects(currentVersion: string, baseVersion: string, locatorFolder: string, driver: WebDriver, browserID: string, customLocatorsPath?: string): void;
package/out/index.js CHANGED
@@ -26,14 +26,39 @@ 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);
@@ -112,8 +137,12 @@ __exportStar(require("./conditions/WaitForAttribute"), exports);
112
137
  * @param driver WebDriver instance
113
138
  * @param browserID identifier/name of the browser (i.e. vscode)
114
139
  */
115
- function initPageObjects(currentVersion, baseVersion, locatorFolder, driver, browserID) {
116
- const locators = new loader_1.LocatorLoader(currentVersion, baseVersion, locatorFolder).loadLocators();
140
+ function initPageObjects(currentVersion, baseVersion, locatorFolder, driver, browserID, customLocatorsPath) {
141
+ let locators = new loader_1.LocatorLoader(currentVersion, baseVersion, locatorFolder).loadLocators();
142
+ if (customLocatorsPath) {
143
+ const custom = require(path.resolve(customLocatorsPath));
144
+ locators = (0, loader_1.mergeLocators)(locators, custom);
145
+ }
117
146
  AbstractElement_1.AbstractElement.init(locators, driver, browserID, currentVersion);
118
147
  }
119
148
  //# 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
  }
@@ -15,8 +15,10 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  import { By, IRectangle, WebElement } from 'selenium-webdriver';
18
- import { PartialDeep } from 'type-fest';
19
18
  import { ViewSection } from '../components/sidebar/ViewSection';
19
+ type PartialDeep<T> = T extends object ? {
20
+ [K in keyof T]?: PartialDeep<T[K]>;
21
+ } : T;
20
22
  type WebElementFunction<E extends WebElement, T> = (element: E) => T | PromiseLike<T>;
21
23
  type LocatorAwareWebElementFunction<T> = (element: WebElement, locator: Locators) => T | PromiseLike<T>;
22
24
  /**
@@ -336,6 +338,7 @@ export interface Locators {
336
338
  expandedAttr: string;
337
339
  expandedValue: string;
338
340
  tooltipAttribute: string;
341
+ tooltipElement: By;
339
342
  description: By;
340
343
  };
341
344
  DebugBreakpointSection: {
@@ -547,7 +550,7 @@ export interface Locators {
547
550
  * Definition for locator diff object
548
551
  */
549
552
  export interface LocatorDiff {
550
- locators: PartialDeep<Locators>;
553
+ locators: PartialDeep<Locators> & Record<string, Record<string, unknown>>;
551
554
  extras?: object;
552
555
  }
553
556
  export declare function hasAttribute(attr: string, value?: string, locator?: By): (el: WebElement) => Promise<boolean>;
@@ -39,7 +39,7 @@ function hasClass(classOrPredicate, locator) {
39
39
  const klasses = await el.getAttribute('class');
40
40
  const segments = klasses?.split(/\s+/g);
41
41
  const predicate = typeof classOrPredicate === 'string' ? (klass) => klass === classOrPredicate : classOrPredicate;
42
- return segments.find(predicate) !== undefined;
42
+ return segments?.find(predicate) !== undefined;
43
43
  };
44
44
  }
45
45
  function hasNotClass(klass, locator) {
@@ -56,7 +56,7 @@ function hasElement(locatorSelector) {
56
56
  function fromAttribute(attribute, locator) {
57
57
  return async (el) => {
58
58
  el = locator ? await el.findElement(locator) : el;
59
- return await el.getAttribute(attribute);
59
+ return (await el.getAttribute(attribute));
60
60
  };
61
61
  }
62
62
  function fromText(locator) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redhat-developer/page-objects",
3
- "version": "1.19.1",
3
+ "version": "1.21.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,14 @@
37
37
  "ui-test"
38
38
  ],
39
39
  "dependencies": {
40
- "clipboardy": "^5.3.1",
40
+ "clipboardy": "^5.3.2",
41
41
  "clone-deep": "^4.0.1",
42
42
  "compare-versions": "^6.1.1",
43
- "fs-extra": "^11.3.3",
44
- "type-fest": "^4.41.0"
43
+ "fs-extra": "^11.4.0"
45
44
  },
46
45
  "peerDependencies": {
47
46
  "selenium-webdriver": ">=4.6.1",
48
47
  "typescript": ">=4.6.2"
49
48
  },
50
- "gitHead": "2348dcd72380e2b62591c3c589f81cd26ed7b76a"
49
+ "gitHead": "d3ed855411fdff9575ce7d7e8a65faa70a08349f"
51
50
  }