@redhat-developer/page-objects 1.17.0 → 1.18.1

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
@@ -16,6 +16,17 @@
16
16
  */
17
17
  import { WebElement, WebDriver, Locator } from 'selenium-webdriver';
18
18
  import { Locators } from '../locators/locators';
19
+ import { WaitHelper } from '../utils/WaitHelper';
20
+ import { type ErrorContext } from '../errors/ExTesterError';
21
+ /**
22
+ * Information needed to relocate an element after it becomes stale.
23
+ */
24
+ export interface LocatorInfo {
25
+ /** The locator used to find this element */
26
+ locator: Locator;
27
+ /** The parent element or locator this element was found within */
28
+ enclosingLocator?: Locator | WebElement;
29
+ }
19
30
  /**
20
31
  * Default wrapper for webElement
21
32
  */
@@ -27,7 +38,13 @@ export declare abstract class AbstractElement extends WebElement {
27
38
  version: string;
28
39
  browser: string;
29
40
  };
41
+ protected static waitHelper: WaitHelper;
30
42
  protected enclosingItem: WebElement;
43
+ /**
44
+ * Locator information for auto-recovery when element becomes stale.
45
+ * Stored when element is created with a Locator (not a WebElement).
46
+ */
47
+ protected locatorInfo?: LocatorInfo;
31
48
  /**
32
49
  * Constructs a new element from a Locator or an existing WebElement
33
50
  * @param base WebDriver compatible Locator for the given element or a reference to an existing WebElement
@@ -43,11 +60,70 @@ export declare abstract class AbstractElement extends WebElement {
43
60
  * @returns thenable self reference
44
61
  */
45
62
  wait(timeout?: number): Promise<this>;
63
+ /**
64
+ * Wait for the element to become stable (position/size stops changing).
65
+ * Useful after animations or dynamic content loading.
66
+ * @param timeout Maximum time to wait in milliseconds
67
+ * @returns thenable self reference
68
+ */
69
+ waitForStable(timeout?: number): Promise<this>;
46
70
  /**
47
71
  * Return a reference to the WebElement containing this element
48
72
  */
49
73
  getEnclosingElement(): WebElement;
74
+ /**
75
+ * Get the stored locator information for this element, if available.
76
+ */
77
+ getLocatorInfo(): LocatorInfo | undefined;
78
+ /**
79
+ * Check if this element can be automatically recovered when stale.
80
+ */
81
+ canAutoRecover(): boolean;
82
+ /**
83
+ * Get the WaitHelper instance for condition-based waiting.
84
+ */
85
+ protected getWaitHelper(): WaitHelper;
86
+ /**
87
+ * Create error context for this element.
88
+ * Useful for creating detailed error messages.
89
+ */
90
+ protected createErrorContext(action: string, details?: string): ErrorContext;
50
91
  static init(locators: Locators, driver: WebDriver, browser: string, version: string): void;
51
- withRecovery<T>(fn: (self: this) => Promise<T>): Promise<T>;
92
+ /**
93
+ * Execute a function with automatic recovery from stale element references.
94
+ * If the element becomes stale during execution, it will be relocated and
95
+ * the function will be retried once.
96
+ *
97
+ * @param fn The async function to execute
98
+ * @param maxRetries Maximum number of recovery attempts (default: 1)
99
+ * @returns The result of the function
100
+ */
101
+ withRecovery<T>(fn: (self: this) => Promise<T>, maxRetries?: number): Promise<T>;
102
+ /**
103
+ * Re-locate this element using stored locator information.
104
+ * This is called automatically by withRecovery() when an element becomes stale.
105
+ *
106
+ * Subclasses can override this for custom recovery logic, but the default
107
+ * implementation works for most cases when the element was created with a locator.
108
+ *
109
+ * @returns A new instance of this element type pointing to the relocated DOM element
110
+ * @throws ElementRecoveryError if the element cannot be recovered
111
+ */
52
112
  protected reinitialize(): Promise<this>;
113
+ /**
114
+ * Safely click on the element with automatic retry on stale/interactable errors.
115
+ */
116
+ safeClick(): Promise<void>;
117
+ /**
118
+ * Safely send keys to the element with automatic retry on stale/interactable errors.
119
+ */
120
+ safeSendKeys(...keys: (string | Promise<string>)[]): Promise<void>;
121
+ /**
122
+ * Safely get text from the element with automatic retry on stale errors.
123
+ */
124
+ safeGetText(): Promise<string>;
125
+ /**
126
+ * Safely get attribute from the element with automatic retry on stale errors.
127
+ */
128
+ safeGetAttribute(name: string): Promise<string>;
53
129
  }
@@ -18,6 +18,8 @@
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.AbstractElement = void 0;
20
20
  const selenium_webdriver_1 = require("selenium-webdriver");
21
+ const WaitHelper_1 = require("../utils/WaitHelper");
22
+ const ExTesterError_1 = require("../errors/ExTesterError");
21
23
  /**
22
24
  * Default wrapper for webElement
23
25
  */
@@ -26,13 +28,24 @@ class AbstractElement extends selenium_webdriver_1.WebElement {
26
28
  static driver;
27
29
  static locators;
28
30
  static versionInfo;
31
+ static waitHelper;
29
32
  enclosingItem;
33
+ /**
34
+ * Locator information for auto-recovery when element becomes stale.
35
+ * Stored when element is created with a Locator (not a WebElement).
36
+ */
37
+ locatorInfo;
30
38
  /**
31
39
  * Constructs a new element from a Locator or an existing WebElement
32
40
  * @param base WebDriver compatible Locator for the given element or a reference to an existing WebElement
33
41
  * @param enclosingItem Locator or a WebElement reference to an element containing the element being constructed
34
42
  * this will be used to narrow down the search for the underlying DOM element
35
43
  */
44
+ /*
45
+ * Note: The findElement calls below use selenium-webdriver's ThenableWebElement pattern
46
+ * for lazy element resolution. The WebElement base class is designed to work with these
47
+ * thenables, making this an intentional pattern.
48
+ */
36
49
  constructor(base, enclosingItem) {
37
50
  let item = AbstractElement.driver.findElement(AbstractElement.locators.AbstractElement.tag);
38
51
  if (!enclosingItem) {
@@ -46,11 +59,17 @@ class AbstractElement extends selenium_webdriver_1.WebElement {
46
59
  }
47
60
  if (base instanceof selenium_webdriver_1.WebElement) {
48
61
  super(AbstractElement.driver, base.getId());
62
+ // Cannot store locator info for WebElement-based construction
49
63
  }
50
64
  else {
51
65
  const toFind = item.findElement(base);
52
66
  const id = toFind.getId();
53
67
  super(AbstractElement.driver, id);
68
+ // Store locator info for potential recovery
69
+ this.locatorInfo = {
70
+ locator: base,
71
+ enclosingLocator: enclosingItem,
72
+ };
54
73
  }
55
74
  this.enclosingItem = item;
56
75
  }
@@ -69,34 +88,172 @@ class AbstractElement extends selenium_webdriver_1.WebElement {
69
88
  await this.getDriver().wait(selenium_webdriver_1.until.elementIsVisible(this), timeout);
70
89
  return this;
71
90
  }
91
+ /**
92
+ * Wait for the element to become stable (position/size stops changing).
93
+ * Useful after animations or dynamic content loading.
94
+ * @param timeout Maximum time to wait in milliseconds
95
+ * @returns thenable self reference
96
+ */
97
+ async waitForStable(timeout = 2000) {
98
+ await AbstractElement.waitHelper.forStable(this, { timeout });
99
+ return this;
100
+ }
72
101
  /**
73
102
  * Return a reference to the WebElement containing this element
74
103
  */
75
104
  getEnclosingElement() {
76
105
  return this.enclosingItem;
77
106
  }
107
+ /**
108
+ * Get the stored locator information for this element, if available.
109
+ */
110
+ getLocatorInfo() {
111
+ return this.locatorInfo;
112
+ }
113
+ /**
114
+ * Check if this element can be automatically recovered when stale.
115
+ */
116
+ canAutoRecover() {
117
+ return this.locatorInfo !== undefined;
118
+ }
119
+ /**
120
+ * Get the WaitHelper instance for condition-based waiting.
121
+ */
122
+ getWaitHelper() {
123
+ return AbstractElement.waitHelper;
124
+ }
125
+ /**
126
+ * Create error context for this element.
127
+ * Useful for creating detailed error messages.
128
+ */
129
+ createErrorContext(action, details) {
130
+ return {
131
+ component: this.constructor.name,
132
+ action,
133
+ vscodeVersion: AbstractElement.versionInfo?.version,
134
+ locator: this.locatorInfo?.locator ? JSON.stringify(this.locatorInfo.locator) : undefined,
135
+ details,
136
+ };
137
+ }
78
138
  static init(locators, driver, browser, version) {
79
139
  AbstractElement.locators = locators;
80
140
  AbstractElement.driver = driver;
81
141
  AbstractElement.versionInfo = { version: version, browser: browser };
142
+ AbstractElement.waitHelper = new WaitHelper_1.WaitHelper(driver);
143
+ }
144
+ /**
145
+ * Execute a function with automatic recovery from stale element references.
146
+ * If the element becomes stale during execution, it will be relocated and
147
+ * the function will be retried once.
148
+ *
149
+ * @param fn The async function to execute
150
+ * @param maxRetries Maximum number of recovery attempts (default: 1)
151
+ * @returns The result of the function
152
+ */
153
+ async withRecovery(fn, maxRetries = 1) {
154
+ let lastError;
155
+ let recoveredElement;
156
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
157
+ try {
158
+ return await fn(recoveredElement ?? this);
159
+ }
160
+ catch (err) {
161
+ const isRecoverable = err.name === 'StaleElementReferenceError' ||
162
+ err.name === 'ElementNotInteractableError' ||
163
+ /element.*(detached|not interactable)/i.test(err.message);
164
+ if (isRecoverable && attempt < maxRetries) {
165
+ try {
166
+ recoveredElement = await this.reinitialize();
167
+ }
168
+ catch (error_) {
169
+ // Recovery failed, throw original error with context
170
+ throw (0, ExTesterError_1.wrapError)(err, this.createErrorContext('withRecovery', `Recovery failed: ${error_.message}`));
171
+ }
172
+ lastError = err;
173
+ }
174
+ else {
175
+ throw (0, ExTesterError_1.wrapError)(err, this.createErrorContext('withRecovery'));
176
+ }
177
+ }
178
+ }
179
+ // Should not reach here, but just in case
180
+ throw lastError || new Error('Unexpected error in withRecovery');
82
181
  }
83
- async withRecovery(fn) {
182
+ /**
183
+ * Re-locate this element using stored locator information.
184
+ * This is called automatically by withRecovery() when an element becomes stale.
185
+ *
186
+ * Subclasses can override this for custom recovery logic, but the default
187
+ * implementation works for most cases when the element was created with a locator.
188
+ *
189
+ * @returns A new instance of this element type pointing to the relocated DOM element
190
+ * @throws ElementRecoveryError if the element cannot be recovered
191
+ */
192
+ async reinitialize() {
193
+ if (!this.locatorInfo) {
194
+ throw new ExTesterError_1.ElementRecoveryError(`Cannot auto-recover ${this.constructor.name}: no locator information stored. ` +
195
+ `Element was likely created from a WebElement reference. ` +
196
+ `Override reinitialize() in ${this.constructor.name} for custom recovery logic.`, this.createErrorContext('reinitialize'));
197
+ }
84
198
  try {
85
- return await fn(this);
199
+ // Resolve the parent element
200
+ let parentElement;
201
+ if (this.locatorInfo.enclosingLocator instanceof selenium_webdriver_1.WebElement) {
202
+ parentElement = this.locatorInfo.enclosingLocator;
203
+ }
204
+ else if (this.locatorInfo.enclosingLocator) {
205
+ parentElement = await AbstractElement.driver.findElement(this.locatorInfo.enclosingLocator);
206
+ }
207
+ else {
208
+ parentElement = await AbstractElement.driver.findElement(AbstractElement.locators.AbstractElement.tag);
209
+ }
210
+ // Find the element again
211
+ const newElement = await parentElement.findElement(this.locatorInfo.locator);
212
+ // Create a new instance of the same class
213
+ const Constructor = this.constructor;
214
+ const recovered = new Constructor(newElement, parentElement);
215
+ // Copy over the locator info so subsequent recoveries work
216
+ recovered.locatorInfo = this.locatorInfo;
217
+ return recovered;
86
218
  }
87
219
  catch (err) {
88
- if (err.name === 'StaleElementReferenceError' ||
89
- err.name === 'ElementNotInteractableError' ||
90
- /element.*(detached|not interactable)/i.test(err.message)) {
91
- const reinit = await this.reinitialize();
92
- return await fn(reinit);
93
- }
94
- throw err;
220
+ throw new ExTesterError_1.ElementRecoveryError(`Failed to relocate ${this.constructor.name}`, {
221
+ ...this.createErrorContext('reinitialize'),
222
+ cause: err,
223
+ });
95
224
  }
96
225
  }
97
- async reinitialize() {
98
- // Implement generic fallback if possible — otherwise force override in subclasses
99
- throw new Error(`reinitialize() not implemented in ${this.constructor.name}`);
226
+ /**
227
+ * Safely click on the element with automatic retry on stale/interactable errors.
228
+ */
229
+ async safeClick() {
230
+ await this.withRecovery(async (self) => {
231
+ await self.click();
232
+ });
233
+ }
234
+ /**
235
+ * Safely send keys to the element with automatic retry on stale/interactable errors.
236
+ */
237
+ async safeSendKeys(...keys) {
238
+ await this.withRecovery(async (self) => {
239
+ await self.sendKeys(...keys);
240
+ });
241
+ }
242
+ /**
243
+ * Safely get text from the element with automatic retry on stale errors.
244
+ */
245
+ async safeGetText() {
246
+ return await this.withRecovery(async (self) => {
247
+ return await self.getText();
248
+ });
249
+ }
250
+ /**
251
+ * Safely get attribute from the element with automatic retry on stale errors.
252
+ */
253
+ async safeGetAttribute(name) {
254
+ return await this.withRecovery(async (self) => {
255
+ return await self.getAttribute(name);
256
+ });
100
257
  }
101
258
  }
102
259
  exports.AbstractElement = AbstractElement;
@@ -23,14 +23,26 @@ const selenium_webdriver_1 = require("selenium-webdriver");
23
23
  class ActionButtonElementDropdown extends AbstractElement_1.AbstractElement {
24
24
  async open() {
25
25
  await this.click();
26
- await this.getDriver().sleep(500);
26
+ // Wait for dropdown to open
27
+ await this.getWaitHelper()
28
+ .forCondition(async () => {
29
+ const expanded = await this.getAttribute('aria-expanded');
30
+ return expanded === 'true';
31
+ }, { timeout: 2000, pollInterval: 50 })
32
+ .catch(() => {
33
+ /* May not have aria-expanded attribute */
34
+ });
27
35
  const shadowRootHost = await this.enclosingItem.findElements(ActionButtonElementDropdown.locators.ContextMenu.shadowRootHost);
28
36
  const actions = this.getDriver().actions();
29
37
  await actions.clear();
30
38
  if (shadowRootHost.length > 0) {
31
39
  if ((await this.getAttribute('aria-expanded')) !== 'true') {
32
40
  await this.click();
33
- await this.getDriver().sleep(500);
41
+ // Wait for dropdown to expand after second click
42
+ await this.getWaitHelper().forCondition(async () => {
43
+ const expanded = await this.getAttribute('aria-expanded');
44
+ return expanded === 'true';
45
+ }, { timeout: 2000, pollInterval: 50 });
34
46
  }
35
47
  const shadowRoot = await shadowRootHost[0].getShadowRoot();
36
48
  return new ContextMenu_1.ContextMenu(await shadowRoot.findElement(selenium_webdriver_1.By.className('monaco-menu-container'))).wait();
@@ -37,7 +37,11 @@ class ViewControl extends ElementWithContextMenu_1.ElementWithContextMenu {
37
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
- await ViewControl.driver.sleep(500);
40
+ // Wait for view to be marked as active
41
+ await this.getWaitHelper().forCondition(async () => {
42
+ const newKlass = await this.getAttribute(ViewControl.locators.ViewControl.attribute);
43
+ return newKlass.includes(ViewControl.locators.ViewControl.klass);
44
+ }, { timeout: 2000, pollInterval: 100 });
41
45
  }
42
46
  const view = await new __1.SideBarView().wait();
43
47
  if ((await view.findElements(ViewControl.locators.ViewControl.scmId)).length > 0) {
@@ -123,16 +123,16 @@ class TerminalView extends AbstractViews_1.ChannelView {
123
123
  // try clearing, ignore if not available
124
124
  }
125
125
  await input.sendKeys(command, selenium_webdriver_1.Key.ENTER);
126
- let timer = 0;
127
- let style = await input.getCssValue('left');
128
- do {
129
- if (timeout > 0 && timer > timeout) {
130
- throw new Error(`Timeout of ${timeout}ms exceeded`);
131
- }
132
- await new Promise((res) => setTimeout(res, 500));
133
- timer += 500;
134
- style = await input.getCssValue('left');
135
- } while (style === '0px');
126
+ // Wait for command to complete by monitoring cursor position
127
+ const effectiveTimeout = timeout > 0 ? timeout : 30000;
128
+ await this.getWaitHelper().forCondition(async () => {
129
+ const style = await input.getCssValue('left');
130
+ return style !== '0px';
131
+ }, {
132
+ timeout: effectiveTimeout,
133
+ pollInterval: 200,
134
+ message: `Terminal command did not complete within ${effectiveTimeout}ms`,
135
+ });
136
136
  }
137
137
  /**
138
138
  * Get all text from the internal terminal
@@ -152,7 +152,16 @@ class TerminalView extends AbstractViews_1.ChannelView {
152
152
  }
153
153
  const workbench = new __1.Workbench();
154
154
  await workbench.executeCommand('terminal select all');
155
- await workbench.getDriver().sleep(500);
155
+ // Wait for clipboard to be updated
156
+ await this.getWaitHelper().forCondition(async () => {
157
+ try {
158
+ const current = clipboard.readSync();
159
+ return current !== originalClipboard;
160
+ }
161
+ catch {
162
+ return false;
163
+ }
164
+ }, { timeout: 2000, pollInterval: 100 });
156
165
  const text = clipboard.readSync();
157
166
  if (originalClipboard.length > 0) {
158
167
  clipboard.writeSync(originalClipboard);
@@ -191,7 +200,11 @@ class TerminalView extends AbstractViews_1.ChannelView {
191
200
  }
192
201
  const list = await this.findElement(TerminalView.locators.TerminalView.tabList);
193
202
  const row = await list.findElement(TerminalView.locators.TerminalView.selectedRow);
194
- await this.getDriver().sleep(1000);
203
+ // Wait for row to have a valid aria-label
204
+ await this.getWaitHelper().forCondition(async () => {
205
+ const label = await row.getAttribute('aria-label');
206
+ return label && label.split(' ').length >= 3;
207
+ }, { timeout: 2000, pollInterval: 100 });
195
208
  const label = (await row.getAttribute('aria-label')).split(' ');
196
209
  return `${label[1]}: ${label[2]}`;
197
210
  }
@@ -51,7 +51,8 @@ class ContentAssist extends __1.Menu {
51
51
  }
52
52
  if (!lastItem) {
53
53
  await scrollable.sendKeys(selenium_webdriver_1.Key.PAGE_DOWN);
54
- await new Promise((res) => setTimeout(res, 100));
54
+ // Minimal delay for scroll to render new items
55
+ await this.getWaitHelper().sleep(100);
55
56
  }
56
57
  }
57
58
  }
@@ -70,7 +70,8 @@ class EditorView extends AbstractElement_1.AbstractElement {
70
70
  }
71
71
  while (groups.length > 0 && (await groups[0].getOpenEditorTitles()).length > 0) {
72
72
  await groups[0].closeAllEditors();
73
- await new Promise((res) => setTimeout(res, 1000));
73
+ // Brief wait for DOM to settle after closing editors
74
+ await this.getWaitHelper().sleep(500);
74
75
  groups = await this.getEditorGroups();
75
76
  }
76
77
  }
@@ -86,7 +87,15 @@ class EditorView extends AbstractElement_1.AbstractElement {
86
87
  }
87
88
  const titles = [];
88
89
  for (const group of groups) {
89
- titles.push(...(await group.getOpenEditorTitles()));
90
+ try {
91
+ titles.push(...(await group.getOpenEditorTitles()));
92
+ }
93
+ catch (e) {
94
+ if (e instanceof selenium_webdriver_1.error.StaleElementReferenceError) {
95
+ continue;
96
+ }
97
+ throw e;
98
+ }
90
99
  }
91
100
  return titles;
92
101
  }
@@ -112,7 +121,15 @@ class EditorView extends AbstractElement_1.AbstractElement {
112
121
  }
113
122
  const tabs = [];
114
123
  for (const group of groups) {
115
- tabs.push(...(await group.getOpenTabs()));
124
+ try {
125
+ tabs.push(...(await group.getOpenTabs()));
126
+ }
127
+ catch (e) {
128
+ if (e instanceof selenium_webdriver_1.error.StaleElementReferenceError) {
129
+ continue;
130
+ }
131
+ throw e;
132
+ }
116
133
  }
117
134
  return tabs;
118
135
  }
@@ -123,8 +140,16 @@ class EditorView extends AbstractElement_1.AbstractElement {
123
140
  async getActiveTab() {
124
141
  const tabs = await this.getOpenTabs();
125
142
  for (const tab of tabs) {
126
- if (await tab.isSelected()) {
127
- return tab;
143
+ try {
144
+ if (await tab.isSelected()) {
145
+ return tab;
146
+ }
147
+ }
148
+ catch (e) {
149
+ if (e instanceof selenium_webdriver_1.error.StaleElementReferenceError) {
150
+ continue;
151
+ }
152
+ throw e;
128
153
  }
129
154
  }
130
155
  return undefined;
@@ -305,8 +330,16 @@ class EditorGroup extends AbstractElement_1.AbstractElement {
305
330
  async getActiveTab() {
306
331
  const tabs = await this.getOpenTabs();
307
332
  for (const tab of tabs) {
308
- if (await tab.isSelected()) {
309
- return tab;
333
+ try {
334
+ if (await tab.isSelected()) {
335
+ return tab;
336
+ }
337
+ }
338
+ catch (e) {
339
+ if (e instanceof selenium_webdriver_1.error.StaleElementReferenceError) {
340
+ continue;
341
+ }
342
+ throw e;
310
343
  }
311
344
  }
312
345
  return undefined;
@@ -336,8 +369,16 @@ class EditorGroup extends AbstractElement_1.AbstractElement {
336
369
  const predicate = typeof predicateOrTitle === 'string' ? async (action) => (await action.getTitle()) === predicateOrTitle : predicateOrTitle;
337
370
  const actions = await this.getActions();
338
371
  for (const action of actions) {
339
- if (await predicate(action)) {
340
- return action;
372
+ try {
373
+ if (await predicate(action)) {
374
+ return action;
375
+ }
376
+ }
377
+ catch (e) {
378
+ if (e instanceof selenium_webdriver_1.error.StaleElementReferenceError) {
379
+ continue;
380
+ }
381
+ throw e;
341
382
  }
342
383
  }
343
384
  return undefined;
@@ -28,7 +28,15 @@ class ExtensionEditorDetailsSection extends __1.ExtensionEditorView {
28
28
  const container = await this.findElement(ExtensionEditorDetailsSection.locators.ExtensionEditorDetailsSection.categoriesContainer);
29
29
  const categoriesInContainer = await container.findElements(ExtensionEditorDetailsSection.locators.ExtensionEditorDetailsSection.category);
30
30
  for (const cat of categoriesInContainer) {
31
- categories.push(await cat.getText());
31
+ try {
32
+ categories.push(await cat.getText());
33
+ }
34
+ catch (e) {
35
+ if (e.name === 'StaleElementReferenceError') {
36
+ continue;
37
+ }
38
+ throw e;
39
+ }
32
40
  }
33
41
  return categories;
34
42
  }
@@ -41,7 +49,15 @@ class ExtensionEditorDetailsSection extends __1.ExtensionEditorView {
41
49
  const container = await this.findElement(ExtensionEditorDetailsSection.locators.ExtensionEditorDetailsSection.resourcesContainer);
42
50
  const resourcesInContainer = await container.findElements(ExtensionEditorDetailsSection.locators.ExtensionEditorDetailsSection.resource);
43
51
  for (const res of resourcesInContainer) {
44
- resources.push(await res.getText());
52
+ try {
53
+ resources.push(await res.getText());
54
+ }
55
+ catch (e) {
56
+ if (e.name === 'StaleElementReferenceError') {
57
+ continue;
58
+ }
59
+ throw e;
60
+ }
45
61
  }
46
62
  return resources;
47
63
  }
@@ -54,11 +70,19 @@ class ExtensionEditorDetailsSection extends __1.ExtensionEditorView {
54
70
  const container = await this.findElement(ExtensionEditorDetailsSection.locators.ExtensionEditorDetailsSection.moreInfoContainer);
55
71
  const moreInfoInContainer = await container.findElements(ExtensionEditorDetailsSection.locators.ExtensionEditorDetailsSection.moreInfo);
56
72
  for (const entry of moreInfoInContainer) {
57
- const elmnts = await entry.findElements(ExtensionEditorDetailsSection.locators.ExtensionEditorDetailsSection.moreInfoElements);
58
- const name = await elmnts.at(0)?.getText();
59
- const value = await elmnts.at(1)?.getText();
60
- if (name !== undefined && value !== undefined) {
61
- moreInfo[name] = value;
73
+ try {
74
+ const elmnts = await entry.findElements(ExtensionEditorDetailsSection.locators.ExtensionEditorDetailsSection.moreInfoElements);
75
+ const name = await elmnts.at(0)?.getText();
76
+ const value = await elmnts.at(1)?.getText();
77
+ if (name !== undefined && value !== undefined) {
78
+ moreInfo[name] = value;
79
+ }
80
+ }
81
+ catch (e) {
82
+ if (e.name === 'StaleElementReferenceError') {
83
+ continue;
84
+ }
85
+ throw e;
62
86
  }
63
87
  }
64
88
  return moreInfo;