@redhat-developer/page-objects 1.17.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/out/components/AbstractElement.d.ts +77 -1
  2. package/out/components/AbstractElement.js +169 -12
  3. package/out/components/ActionButtonElementDropdown.js +14 -2
  4. package/out/components/activityBar/ViewControl.js +5 -1
  5. package/out/components/bottomBar/Views.js +25 -12
  6. package/out/components/editor/ContentAssist.js +2 -1
  7. package/out/components/editor/EditorView.js +50 -9
  8. package/out/components/editor/ExtensionEditorDetailsSection.js +31 -7
  9. package/out/components/editor/SettingsEditor.js +110 -28
  10. package/out/components/editor/TextEditor.js +120 -25
  11. package/out/components/menu/ContextMenu.js +27 -6
  12. package/out/components/menu/MenuItem.js +14 -1
  13. package/out/components/menu/TitleBar.js +15 -3
  14. package/out/components/sidebar/ViewSection.js +14 -4
  15. package/out/components/sidebar/WelcomeContent.js +10 -2
  16. package/out/components/sidebar/debug/DebugView.js +10 -2
  17. package/out/components/sidebar/extensions/ExtensionsViewSection.js +20 -5
  18. package/out/components/sidebar/scm/NewScmView.js +53 -13
  19. package/out/components/sidebar/scm/ScmView.js +12 -4
  20. package/out/components/sidebar/tree/TreeSection.js +11 -3
  21. package/out/components/sidebar/tree/custom/CustomTreeSection.js +22 -6
  22. package/out/components/sidebar/tree/debug/WatchSection.js +5 -1
  23. package/out/components/sidebar/tree/default/DefaultTreeSection.js +9 -1
  24. package/out/components/workbench/Notification.js +10 -2
  25. package/out/components/workbench/NotificationsCenter.js +11 -3
  26. package/out/components/workbench/Workbench.js +14 -4
  27. package/out/components/workbench/input/Input.js +45 -16
  28. package/out/components/workbench/input/InputBox.js +20 -4
  29. package/out/components/workbench/input/QuickOpenBox.js +11 -3
  30. package/out/errors/ExTesterError.d.ts +112 -0
  31. package/out/errors/ExTesterError.js +201 -0
  32. package/out/index.d.ts +2 -0
  33. package/out/index.js +2 -0
  34. package/out/utils/WaitHelper.d.ts +202 -0
  35. package/out/utils/WaitHelper.js +465 -0
  36. package/package.json +4 -4
@@ -76,16 +76,12 @@ class SettingsEditor extends Editor_1.Editor {
76
76
  }
77
77
  async _getSettingItem(title = '', category = '') {
78
78
  const count = await this.findElement(SettingsEditor.locators.SettingsEditor.itemCount);
79
- let textCount = await count.getText();
80
- await this.getDriver().wait(async function () {
81
- await new Promise((res) => setTimeout(res, 1500));
79
+ // Wait for settings count to stabilize (loading complete)
80
+ await this.getWaitHelper().forCountStable(async () => {
82
81
  const text = await count.getText();
83
- if (text !== textCount) {
84
- textCount = text;
85
- return false;
86
- }
87
- return true;
88
- });
82
+ const match = new RegExp(/\d+/).exec(text);
83
+ return match ? Number.parseInt(match[0], 10) : 0;
84
+ }, { timeout: 5000, stabilityInterval: 300, stableChecks: 2 });
89
85
  let setting;
90
86
  const items = await this.findElements(SettingsEditor.locators.SettingsEditor.itemRow);
91
87
  for (const item of items) {
@@ -212,12 +208,20 @@ class ComboSetting extends Setting {
212
208
  async setValue(value) {
213
209
  const rows = await this.getOptions();
214
210
  for (const row of rows) {
215
- if ((await row.getAttribute('class')).indexOf('disabled') < 0) {
216
- const text = await row.getAttribute(SettingsEditor.locators.SettingsEditor.comboValue);
217
- if (value === text) {
218
- return await row.click();
211
+ try {
212
+ if ((await row.getAttribute('class')).indexOf('disabled') < 0) {
213
+ const text = await row.getAttribute(SettingsEditor.locators.SettingsEditor.comboValue);
214
+ if (value === text) {
215
+ return await row.click();
216
+ }
219
217
  }
220
218
  }
219
+ catch (e) {
220
+ if (e.name === 'StaleElementReferenceError') {
221
+ continue;
222
+ }
223
+ throw e;
224
+ }
221
225
  }
222
226
  }
223
227
  /**
@@ -228,7 +232,15 @@ class ComboSetting extends Setting {
228
232
  const values = [];
229
233
  const rows = await this.getOptions();
230
234
  for (const row of rows) {
231
- values.push(await row.getAttribute(SettingsEditor.locators.SettingsEditor.comboValue));
235
+ try {
236
+ values.push(await row.getAttribute(SettingsEditor.locators.SettingsEditor.comboValue));
237
+ }
238
+ catch (e) {
239
+ if (e.name === 'StaleElementReferenceError') {
240
+ continue;
241
+ }
242
+ throw e;
243
+ }
232
244
  }
233
245
  return values;
234
246
  }
@@ -348,10 +360,21 @@ class ArraySetting extends Setting {
348
360
  * @returns string[]
349
361
  */
350
362
  async getValues() {
351
- const items = await this.getItems();
352
363
  const values = [];
353
- for (const item of items) {
354
- values.push(await item.getValue());
364
+ const listRows = await this.getRows();
365
+ for (const row of listRows) {
366
+ try {
367
+ const li = await row.findElement(SettingsEditor.locators.SettingsEditor.arrayRowValue);
368
+ values.push(await li.getText());
369
+ }
370
+ catch (e) {
371
+ if (e.name === 'StaleElementReferenceError') {
372
+ // Item became stale during iteration (DOM updating), skip it
373
+ // The caller's retry logic will handle getting a consistent view
374
+ continue;
375
+ }
376
+ throw e;
377
+ }
355
378
  }
356
379
  return values;
357
380
  }
@@ -363,9 +386,13 @@ class ArraySetting extends Setting {
363
386
  // click 'Add Item' button
364
387
  const button = await this.findElement(SettingsEditor.locators.SettingsEditor.arrayNewRow).findElement(SettingsEditor.locators.SettingsEditor.button);
365
388
  await button.click();
366
- await new Promise((sleep) => setTimeout(sleep, 1_000)); // need to force some time to allow DOM rerender elements
367
- // get item row switched to 'edit' mode
389
+ // Wait for edit row to appear in DOM
368
390
  const list = await this.getListRootElement();
391
+ await this.getWaitHelper().forCondition(async () => {
392
+ const rows = await list.findElements(SettingsEditor.locators.SettingsEditor.arrayEditRow);
393
+ return rows.length > 0;
394
+ }, { timeout: 2000, message: 'Edit row did not appear after clicking Add Item' });
395
+ // get item row switched to 'edit' mode
369
396
  const editRow = await list.findElement(SettingsEditor.locators.SettingsEditor.arrayEditRow);
370
397
  return new ArraySettingItem(editRow, this);
371
398
  }
@@ -383,9 +410,13 @@ class ArraySetting extends Setting {
383
410
  // click 'Edit Item' button
384
411
  const edit = await toEdit.findElement(SettingsEditor.locators.SettingsEditor.arrayBtnConstructor('Edit Item'));
385
412
  await edit.click();
386
- await new Promise((sleep) => setTimeout(sleep, 1_000)); // need to force some time to allow DOM rerender elements
387
- // get item row switched to 'edit' mode
413
+ // Wait for edit row to appear in DOM
388
414
  const list = await this.getListRootElement();
415
+ await this.getWaitHelper().forCondition(async () => {
416
+ const rows = await list.findElements(SettingsEditor.locators.SettingsEditor.arrayEditRow);
417
+ return rows.length > 0;
418
+ }, { timeout: 2000, message: 'Edit row did not appear after clicking Edit Item' });
419
+ // get item row switched to 'edit' mode
389
420
  const editRow = await list.findElement(SettingsEditor.locators.SettingsEditor.arrayEditRow);
390
421
  return new ArraySettingItem(editRow, this);
391
422
  }
@@ -409,9 +440,17 @@ class ArraySetting extends Setting {
409
440
  }
410
441
  else {
411
442
  for (const row of listRows) {
412
- const li = await row.findElement(SettingsEditor.locators.SettingsEditor.arrayRowValue);
413
- if ((await li.getText()) === item) {
414
- return row;
443
+ try {
444
+ const li = await row.findElement(SettingsEditor.locators.SettingsEditor.arrayRowValue);
445
+ if ((await li.getText()) === item) {
446
+ return row;
447
+ }
448
+ }
449
+ catch (e) {
450
+ if (e.name === 'StaleElementReferenceError') {
451
+ continue;
452
+ }
453
+ throw e;
415
454
  }
416
455
  }
417
456
  }
@@ -431,7 +470,11 @@ class ArraySettingItem extends AbstractElement_1.AbstractElement {
431
470
  */
432
471
  async select() {
433
472
  await this.click();
434
- await new Promise((sleep) => setTimeout(sleep, 500));
473
+ // Wait for selection state to be applied
474
+ await this.getWaitHelper().forCondition(async () => {
475
+ const classAttr = await this.getAttribute('class');
476
+ return classAttr && (classAttr.includes('selected') || classAttr.includes('focused'));
477
+ }, { timeout: 1000, pollInterval: 50 });
435
478
  }
436
479
  /**
437
480
  * Get item text value
@@ -456,7 +499,16 @@ class ArraySettingItem extends AbstractElement_1.AbstractElement {
456
499
  await this.select();
457
500
  const remove = await this.findElement(SettingsEditor.locators.SettingsEditor.arrayBtnConstructor('Remove Item'));
458
501
  await remove.click();
459
- await new Promise((sleep) => setTimeout(sleep, 500));
502
+ // Wait for item to be removed from DOM
503
+ await this.getWaitHelper().forCondition(async () => {
504
+ try {
505
+ await this.isDisplayed();
506
+ return false;
507
+ }
508
+ catch {
509
+ return true; // Element no longer exists
510
+ }
511
+ }, { timeout: 1000, pollInterval: 50 });
460
512
  }
461
513
  /**
462
514
  * Click 'OK' button
@@ -465,7 +517,22 @@ class ArraySettingItem extends AbstractElement_1.AbstractElement {
465
517
  async ok() {
466
518
  const ok = await this.findElement(SettingsEditor.locators.SettingsEditor.arraySettingItem.btnConstructor('OK'));
467
519
  await ok.click();
468
- await new Promise((sleep) => setTimeout(sleep, 500));
520
+ // Wait for edit mode to close - the edit row will be removed from DOM
521
+ await this.getWaitHelper()
522
+ .forCondition(async () => {
523
+ try {
524
+ await this.isDisplayed();
525
+ // Check if still in edit mode by looking for OK button
526
+ const okButtons = await this.findElements(SettingsEditor.locators.SettingsEditor.arraySettingItem.btnConstructor('OK'));
527
+ return okButtons.length === 0;
528
+ }
529
+ catch {
530
+ return true; // Element removed from DOM
531
+ }
532
+ }, { timeout: 2000, pollInterval: 100 })
533
+ .catch(() => {
534
+ /* Element may already be gone */
535
+ });
469
536
  }
470
537
  /**
471
538
  * Click 'Cancel' button
@@ -474,7 +541,22 @@ class ArraySettingItem extends AbstractElement_1.AbstractElement {
474
541
  async cancel() {
475
542
  const cancel = await this.findElement(SettingsEditor.locators.SettingsEditor.arraySettingItem.btnConstructor('Cancel'));
476
543
  await cancel.click();
477
- await new Promise((sleep) => setTimeout(sleep, 500));
544
+ // Wait for edit mode to close - the edit row will be removed from DOM
545
+ await this.getWaitHelper()
546
+ .forCondition(async () => {
547
+ try {
548
+ await this.isDisplayed();
549
+ // Check if still in edit mode by looking for Cancel button
550
+ const cancelButtons = await this.findElements(SettingsEditor.locators.SettingsEditor.arraySettingItem.btnConstructor('Cancel'));
551
+ return cancelButtons.length === 0;
552
+ }
553
+ catch {
554
+ return true; // Element removed from DOM
555
+ }
556
+ }, { timeout: 2000, pollInterval: 100 })
557
+ .catch(() => {
558
+ /* Element may already be gone */
559
+ });
478
560
  }
479
561
  }
480
562
  exports.ArraySettingItem = ArraySettingItem;
@@ -130,7 +130,16 @@ class TextEditor extends Editor_1.Editor {
130
130
  }
131
131
  const inputarea = await self.findElement(TextEditor.locators.Editor.inputArea);
132
132
  await inputarea.sendKeys(selenium_webdriver_1.Key.chord(TextEditor.ctlKey, 'a'), selenium_webdriver_1.Key.chord(TextEditor.ctlKey, 'c'));
133
- await new Promise((res) => setTimeout(res, 500));
133
+ // Wait for clipboard operation to complete
134
+ await self.getWaitHelper().forCondition(async () => {
135
+ try {
136
+ const currentClip = clipboard.readSync();
137
+ return currentClip !== originalClipboard || currentClip.length > 0;
138
+ }
139
+ catch {
140
+ return false;
141
+ }
142
+ }, { timeout: 2000, pollInterval: 50, message: 'Clipboard copy operation did not complete' });
134
143
  const text = clipboard.readSync();
135
144
  await inputarea.sendKeys(selenium_webdriver_1.Key.UP);
136
145
  if (originalClipboard.length > 0) {
@@ -250,7 +259,11 @@ class TextEditor extends Editor_1.Editor {
250
259
  }
251
260
  actions = actions.keyUp(selenium_webdriver_1.Key.SHIFT);
252
261
  await actions.perform();
253
- await new Promise((res) => setTimeout(res, 500));
262
+ // Wait for selection to be rendered
263
+ await this.getWaitHelper().forCondition(async () => {
264
+ const selection = await this.findElements(TextEditor.locators.TextEditor.selection);
265
+ return selection.length > 0;
266
+ }, { timeout: 2000, pollInterval: 50, message: 'Text selection did not appear' });
254
267
  }
255
268
  /**
256
269
  * Get the text that is currently selected as string
@@ -276,10 +289,18 @@ class TextEditor extends Editor_1.Editor {
276
289
  else {
277
290
  const inputarea = await this.findElement(TextEditor.locators.Editor.inputArea);
278
291
  await inputarea.sendKeys(selenium_webdriver_1.Key.chord(TextEditor.ctlKey, 'c'));
279
- await new Promise((res) => setTimeout(res, 500));
280
292
  await inputarea.sendKeys(selenium_webdriver_1.Key.UP);
281
293
  }
282
- await new Promise((res) => setTimeout(res, 500));
294
+ // Wait for clipboard operation to complete
295
+ await this.getWaitHelper().forCondition(async () => {
296
+ try {
297
+ const currentClip = clipboard.readSync();
298
+ return currentClip !== originalClipboard;
299
+ }
300
+ catch {
301
+ return false;
302
+ }
303
+ }, { timeout: 2000, pollInterval: 50, message: 'Copy operation did not complete' });
283
304
  const text = clipboard.readSync();
284
305
  if (originalClipboard.length > 0) {
285
306
  clipboard.writeSync(originalClipboard);
@@ -334,7 +355,12 @@ class TextEditor extends Editor_1.Editor {
334
355
  async setCursor(line, column, timeout = 2_500) {
335
356
  const input = await new __1.Workbench().openCommandPrompt();
336
357
  await input.setText(`:${line},${column}`);
337
- await input.confirm();
358
+ // Wait for input to be processed - check for quick picks appearing
359
+ await this.getWaitHelper().forCondition(async () => {
360
+ const picks = await input.getQuickPicks().catch(() => []);
361
+ return picks.length > 0;
362
+ }, { timeout: 1000, pollInterval: 50 });
363
+ await input.selectQuickPick(0);
338
364
  await this.waitForCursorPositionAt(line, column, timeout);
339
365
  }
340
366
  /**
@@ -386,7 +412,8 @@ class TextEditor extends Editor_1.Editor {
386
412
  }
387
413
  await inputArea.getDriver().actions().clear();
388
414
  await inputArea.sendKeys(lineKey);
389
- await inputArea.getDriver().sleep(50);
415
+ // Minimal delay for key processing - this is a tight loop with position checking
416
+ await this.getWaitHelper().sleep(20);
390
417
  }
391
418
  }
392
419
  /**
@@ -405,7 +432,8 @@ class TextEditor extends Editor_1.Editor {
405
432
  }
406
433
  await inputArea.getDriver().actions().clear();
407
434
  await inputArea.sendKeys(columnKey);
408
- await inputArea.getDriver().sleep(50);
435
+ // Minimal delay for key processing - this is a tight loop with position checking
436
+ await this.getWaitHelper().sleep(20);
409
437
  if ((await this.getCoordinates())[0] !== coordinates[0]) {
410
438
  throw new Error(`Column number ${column} is not accessible on line ${coordinates[0]}`);
411
439
  }
@@ -538,7 +566,11 @@ class TextEditor extends Editor_1.Editor {
538
566
  if (breakPoint.length > 0) {
539
567
  if (this.breakPoints.indexOf(line) !== -1) {
540
568
  await breakPoint[this.breakPoints.indexOf(line)].click();
541
- await new Promise((res) => setTimeout(res, 200));
569
+ // Wait for breakpoint to be removed
570
+ await this.getWaitHelper().forCondition(async () => {
571
+ const remaining = await breakpointContainer.findElements(TextEditor.locators.TextEditor.breakpoint.generalSelector);
572
+ return remaining.length < breakPoint.length;
573
+ }, { timeout: 1000, pollInterval: 50 });
542
574
  this.breakPoints.splice(this.breakPoints.indexOf(line), 1);
543
575
  return false;
544
576
  }
@@ -546,7 +578,11 @@ class TextEditor extends Editor_1.Editor {
546
578
  const noBreak = await breakpointContainer.findElements(TextEditor.locators.TextEditor.debugHint);
547
579
  if (noBreak.length > 0) {
548
580
  await noBreak[0].click();
549
- await new Promise((res) => setTimeout(res, 200));
581
+ // Wait for breakpoint to be added
582
+ await this.getWaitHelper().forCondition(async () => {
583
+ const added = await breakpointContainer.findElements(TextEditor.locators.TextEditor.breakpoint.generalSelector);
584
+ return added.length > breakPoint.length;
585
+ }, { timeout: 1000, pollInterval: 50 });
550
586
  this.breakPoints.push(line);
551
587
  return true;
552
588
  }
@@ -587,8 +623,16 @@ class TextEditor extends Editor_1.Editor {
587
623
  async getBreakpoint(line) {
588
624
  const breakpoints = await this.getBreakpoints();
589
625
  for (const breakpoint of breakpoints) {
590
- if ((await breakpoint.getLineNumber()) === line) {
591
- return breakpoint;
626
+ try {
627
+ if ((await breakpoint.getLineNumber()) === line) {
628
+ return breakpoint;
629
+ }
630
+ }
631
+ catch (e) {
632
+ if (e.name === 'StaleElementReferenceError') {
633
+ continue;
634
+ }
635
+ throw e;
592
636
  }
593
637
  }
594
638
  return undefined;
@@ -605,15 +649,23 @@ class TextEditor extends Editor_1.Editor {
605
649
  : this;
606
650
  const breakpointsSelectors = await breakpointContainer.findElements(breakpointLocators.generalSelector);
607
651
  for (const breakpointSelector of breakpointsSelectors) {
608
- let lineElement;
609
- if ((0, compare_versions_1.satisfies)(TextEditor.versionInfo.version, '>=1.80.0')) {
610
- const styleTopAttr = await breakpointSelector.getCssValue('top');
611
- lineElement = await this.findElement(TextEditor.locators.TextEditor.marginArea).findElement(TextEditor.locators.TextEditor.lineElement(styleTopAttr));
652
+ try {
653
+ let lineElement;
654
+ if ((0, compare_versions_1.satisfies)(TextEditor.versionInfo.version, '>=1.80.0')) {
655
+ const styleTopAttr = await breakpointSelector.getCssValue('top');
656
+ lineElement = await this.findElement(TextEditor.locators.TextEditor.marginArea).findElement(TextEditor.locators.TextEditor.lineElement(styleTopAttr));
657
+ }
658
+ else {
659
+ lineElement = await breakpointSelector.findElement(TextEditor.locators.TextEditor.elementLevelBack);
660
+ }
661
+ breakpoints.push(new Breakpoint_1.Breakpoint(breakpointSelector, lineElement));
612
662
  }
613
- else {
614
- lineElement = await breakpointSelector.findElement(TextEditor.locators.TextEditor.elementLevelBack);
663
+ catch (e) {
664
+ if (e.name === 'StaleElementReferenceError') {
665
+ continue;
666
+ }
667
+ throw e;
615
668
  }
616
- breakpoints.push(new Breakpoint_1.Breakpoint(breakpointSelector, lineElement));
617
669
  }
618
670
  return breakpoints;
619
671
  }
@@ -626,7 +678,15 @@ class TextEditor extends Editor_1.Editor {
626
678
  const widgets = await this.findElement(TextEditor.locators.TextEditor.contentWidgets);
627
679
  const items = await widgets.findElements(TextEditor.locators.TextEditor.contentWidgetsElements);
628
680
  for (const item of items) {
629
- lenses.push(await new CodeLens(item, this).wait());
681
+ try {
682
+ lenses.push(await new CodeLens(item, this).wait());
683
+ }
684
+ catch (e) {
685
+ if (e.name === 'StaleElementReferenceError') {
686
+ continue;
687
+ }
688
+ throw e;
689
+ }
630
690
  }
631
691
  return lenses;
632
692
  }
@@ -640,10 +700,18 @@ class TextEditor extends Editor_1.Editor {
640
700
  const lenses = await this.getCodeLenses();
641
701
  if (typeof indexOrTitle === 'string') {
642
702
  for (const lens of lenses) {
643
- const title = await lens.getText();
644
- const match = title.match(indexOrTitle);
645
- if (match && match.length > 0) {
646
- return lens;
703
+ try {
704
+ const title = await lens.getText();
705
+ const match = title.match(indexOrTitle);
706
+ if (match && match.length > 0) {
707
+ return lens;
708
+ }
709
+ }
710
+ catch (e) {
711
+ if (e.name === 'StaleElementReferenceError') {
712
+ continue;
713
+ }
714
+ throw e;
647
715
  }
648
716
  }
649
717
  }
@@ -663,7 +731,33 @@ class Selection extends ElementWithContextMenu_1.ElementWithContextMenu {
663
731
  }
664
732
  async openContextMenu() {
665
733
  const ed = this.getEnclosingElement();
666
- await this.getDriver().actions().contextClick(this).perform();
734
+ // Selection elements can become stale easily. Retry with fresh element if needed.
735
+ let recoveredElement;
736
+ const maxRetries = 3;
737
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
738
+ try {
739
+ await this.getDriver()
740
+ .actions()
741
+ .contextClick(recoveredElement ?? this)
742
+ .perform();
743
+ break;
744
+ }
745
+ catch (e) {
746
+ if (e.name === 'StaleElementReferenceError' && attempt < maxRetries - 1) {
747
+ // Re-fetch the selection element from the editor
748
+ const selections = await ed.findElements(TextEditor.locators.TextEditor.selection);
749
+ if (selections.length > 0) {
750
+ recoveredElement = selections[0];
751
+ }
752
+ else {
753
+ throw e;
754
+ }
755
+ }
756
+ else {
757
+ throw e;
758
+ }
759
+ }
760
+ }
667
761
  const shadowRootHost = await ed.getEnclosingElement().findElements(TextEditor.locators.TextEditor.shadowRootHost);
668
762
  if (shadowRootHost.length > 0) {
669
763
  let shadowRoot;
@@ -858,7 +952,8 @@ class FindWidget extends AbstractElement_1.AbstractElement {
858
952
  }
859
953
  const btn = await element.findElement(FindWidget.locators.FindWidget.button(title));
860
954
  await btn.click();
861
- await this.getDriver().sleep(100);
955
+ // Wait for button action to complete
956
+ await this.getWaitHelper().forStable(element, { timeout: 500 });
862
957
  }
863
958
  async setText(text, composite) {
864
959
  const input = await composite.findElement(FindWidget.locators.FindWidget.input);
@@ -35,8 +35,16 @@ class ContextMenu extends __1.Menu {
35
35
  try {
36
36
  const items = await this.getItems();
37
37
  for (const item of items) {
38
- if ((await item.getLabel()) === name) {
39
- return item;
38
+ try {
39
+ if ((await item.getLabel()) === name) {
40
+ return item;
41
+ }
42
+ }
43
+ catch (e) {
44
+ if (e.name === 'StaleElementReferenceError') {
45
+ continue;
46
+ }
47
+ throw e;
40
48
  }
41
49
  }
42
50
  }
@@ -52,9 +60,17 @@ class ContextMenu extends __1.Menu {
52
60
  const items = [];
53
61
  const elements = await this.findElements(ContextMenu.locators.ContextMenu.itemElement);
54
62
  for (const element of elements) {
55
- const klass = await element.getAttribute('class');
56
- if (klass.indexOf('disabled') < 0) {
57
- items.push(await new ContextMenuItem(element, this).wait());
63
+ try {
64
+ const klass = await element.getAttribute('class');
65
+ if (klass.indexOf('disabled') < 0) {
66
+ items.push(await new ContextMenuItem(element, this).wait());
67
+ }
68
+ }
69
+ catch (e) {
70
+ if (e.name === 'StaleElementReferenceError') {
71
+ continue;
72
+ }
73
+ throw e;
58
74
  }
59
75
  }
60
76
  return items;
@@ -116,7 +132,12 @@ class ContextMenuItem extends __1.MenuItem {
116
132
  }
117
133
  async select() {
118
134
  await this.click();
119
- await new Promise((res) => setTimeout(res, 500));
135
+ // Wait for click action to complete and potential submenu to appear
136
+ await this.getWaitHelper()
137
+ .forStable(this, { timeout: 1000 })
138
+ .catch(() => {
139
+ /* Element may be removed after click */
140
+ });
120
141
  if (await this.isNesting()) {
121
142
  return await new ContextMenu(this).wait();
122
143
  }
@@ -32,7 +32,20 @@ class MenuItem extends AbstractElement_1.AbstractElement {
32
32
  */
33
33
  async select() {
34
34
  await this.click();
35
- await new Promise((res) => setTimeout(res, 500));
35
+ // Wait for click action to complete - menu may close after selection
36
+ await this.getWaitHelper()
37
+ .forCondition(async () => {
38
+ try {
39
+ // Check if element is still visible (menu might close)
40
+ return !(await this.isDisplayed());
41
+ }
42
+ catch {
43
+ return true; // Element removed from DOM
44
+ }
45
+ }, { timeout: 1000, pollInterval: 50 })
46
+ .catch(() => {
47
+ /* Menu item may still be visible if it has submenu */
48
+ });
36
49
  return undefined;
37
50
  }
38
51
  /**
@@ -50,8 +50,16 @@ class TitleBar extends Menu_1.Menu {
50
50
  const items = [];
51
51
  const elements = await this.findElements(TitleBar.locators.TitleBar.itemElement);
52
52
  for (const element of elements) {
53
- if (await element.isDisplayed()) {
54
- items.push(await new TitleBarItem(await element.getAttribute(TitleBar.locators.TitleBar.itemLabel), this).wait());
53
+ try {
54
+ if (await element.isDisplayed()) {
55
+ items.push(await new TitleBarItem(await element.getAttribute(TitleBar.locators.TitleBar.itemLabel), this).wait());
56
+ }
57
+ }
58
+ catch (e) {
59
+ if (e.name === 'StaleElementReferenceError') {
60
+ continue;
61
+ }
62
+ throw e;
55
63
  }
56
64
  }
57
65
  return items;
@@ -87,7 +95,11 @@ class TitleBarItem extends MenuItem_1.MenuItem {
87
95
  await this.getDriver().actions().sendKeys(selenium_webdriver_1.Key.ESCAPE).perform();
88
96
  }
89
97
  await this.click();
90
- await new Promise((res) => setTimeout(res, 500));
98
+ // Wait for context menu to appear
99
+ await this.getWaitHelper().forCondition(async () => {
100
+ const menus = await this.getDriver().findElements(TitleBar.locators.ContextMenu.constructor);
101
+ return menus.length > 0 && (await menus[0].isDisplayed());
102
+ }, { timeout: 2000, pollInterval: 50 });
91
103
  return new __1.ContextMenu(this).wait();
92
104
  }
93
105
  }
@@ -50,7 +50,8 @@ class ViewSection extends AbstractElement_1.AbstractElement {
50
50
  const collapseExpandButton = await header.findElement(ViewSection.locators.ViewSection.headerCollapseExpandButton);
51
51
  await collapseExpandButton.click();
52
52
  await this.getDriver().wait((0, __1.waitForAttributeValue)(header, ViewSection.locators.ViewSection.headerExpanded, 'true'), timeout);
53
- await this.getDriver().sleep(500);
53
+ // Wait for expand animation to complete
54
+ await this.getWaitHelper().forStable(this, { timeout: 1000 });
54
55
  }
55
56
  }
56
57
  /**
@@ -66,7 +67,8 @@ class ViewSection extends AbstractElement_1.AbstractElement {
66
67
  const collapseExpandButton = await header.findElement(ViewSection.locators.ViewSection.headerCollapseExpandButton);
67
68
  await collapseExpandButton.click();
68
69
  await this.getDriver().wait((0, __1.waitForAttributeValue)(header, ViewSection.locators.ViewSection.headerExpanded, 'false'), timeout);
69
- await this.getDriver().sleep(500);
70
+ // Wait for collapse animation to complete
71
+ await this.getWaitHelper().forStable(this, { timeout: 1000 });
70
72
  }
71
73
  }
72
74
  /**
@@ -120,8 +122,16 @@ class ViewSection extends AbstractElement_1.AbstractElement {
120
122
  async getAction(label) {
121
123
  const actions = await this.getActions();
122
124
  for (const action of actions) {
123
- if ((await action.getLabel()) === label) {
124
- return action;
125
+ try {
126
+ if ((await action.getLabel()) === label) {
127
+ return action;
128
+ }
129
+ }
130
+ catch (e) {
131
+ if (e.name === 'StaleElementReferenceError') {
132
+ continue;
133
+ }
134
+ throw e;
125
135
  }
126
136
  }
127
137
  }
@@ -80,8 +80,16 @@ class WelcomeContentSection extends AbstractElement_1.AbstractElement {
80
80
  async getButton(title) {
81
81
  const buttons = await this.getButtons();
82
82
  for (const btn of buttons) {
83
- if ((await btn.getTitle()) === title) {
84
- return btn;
83
+ try {
84
+ if ((await btn.getTitle()) === title) {
85
+ return btn;
86
+ }
87
+ }
88
+ catch (e) {
89
+ if (e.name === 'StaleElementReferenceError') {
90
+ continue;
91
+ }
92
+ throw e;
85
93
  }
86
94
  }
87
95
  return undefined;
@@ -50,8 +50,16 @@ class DebugView extends SideBarView_1.SideBarView {
50
50
  const configs = [];
51
51
  const options = await combo.findElements(DebugView.locators.DebugView.launchOption);
52
52
  for (const option of options) {
53
- if (await option.isEnabled()) {
54
- configs.push(await option.getAttribute('value'));
53
+ try {
54
+ if (await option.isEnabled()) {
55
+ configs.push(await option.getAttribute('value'));
56
+ }
57
+ }
58
+ catch (e) {
59
+ if (e.name === 'StaleElementReferenceError') {
60
+ continue;
61
+ }
62
+ throw e;
55
63
  }
56
64
  }
57
65
  return configs;