@redhat-developer/page-objects 1.10.0 → 1.12.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.
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Licensed to the Apache Software Foundation (ASF) under one or more
3
+ * contributor license agreements. See the NOTICE file distributed with
4
+ * this work for additional information regarding copyright ownership.
5
+ * The ASF licenses this file to You under the Apache License, Version 2.0
6
+ * (the "License", destination); you may not use this file except in compliance with
7
+ * the License. You may obtain a copy of the License at
8
+ *
9
+ * https://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { ContextMenu } from './menu/ContextMenu';
18
+ import { AbstractElement } from './AbstractElement';
19
+ export declare abstract class ActionButtonElementDropdown extends AbstractElement {
20
+ open(): Promise<ContextMenu>;
21
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ /**
3
+ * Licensed to the Apache Software Foundation (ASF) under one or more
4
+ * contributor license agreements. See the NOTICE file distributed with
5
+ * this work for additional information regarding copyright ownership.
6
+ * The ASF licenses this file to You under the Apache License, Version 2.0
7
+ * (the "License", destination); you may not use this file except in compliance with
8
+ * the License. You may obtain a copy of the License at
9
+ *
10
+ * https://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.ActionButtonElementDropdown = void 0;
20
+ const ElementWithContextMenu_1 = require("./ElementWithContextMenu");
21
+ const ContextMenu_1 = require("./menu/ContextMenu");
22
+ const selenium_webdriver_1 = require("selenium-webdriver");
23
+ const AbstractElement_1 = require("./AbstractElement");
24
+ class ActionButtonElementDropdown extends AbstractElement_1.AbstractElement {
25
+ async open() {
26
+ await this.click();
27
+ const shadowRootHost = await this.enclosingItem.findElements(selenium_webdriver_1.By.className('shadow-root-host'));
28
+ const actions = this.getDriver().actions();
29
+ await actions.clear();
30
+ await actions.sendKeys(selenium_webdriver_1.Key.ESCAPE).perform();
31
+ if (shadowRootHost.length > 0) {
32
+ if ((await this.getAttribute('aria-expanded')) !== 'true') {
33
+ await this.click();
34
+ }
35
+ const shadowRoot = await shadowRootHost[0].getShadowRoot();
36
+ return new ContextMenu_1.ContextMenu(await shadowRoot.findElement(selenium_webdriver_1.By.className('monaco-menu-container'))).wait();
37
+ }
38
+ else {
39
+ await this.click();
40
+ const workbench = await this.getDriver().findElement(ElementWithContextMenu_1.ElementWithContextMenu.locators.Workbench.constructor);
41
+ return new ContextMenu_1.ContextMenu(workbench).wait();
42
+ }
43
+ }
44
+ }
45
+ exports.ActionButtonElementDropdown = ActionButtonElementDropdown;
46
+ //# sourceMappingURL=ActionButtonElementDropdown.js.map
@@ -24,7 +24,7 @@ type Constructor<T = object> = new (...args: any[]) => T;
24
24
  * The interface that a class is required to have in order to use the Webview mixin.
25
25
  */
26
26
  interface WebviewMixable extends AbstractElement {
27
- getViewToSwitchTo(handle: string): Promise<WebElement | undefined>;
27
+ getViewToSwitchTo(): Promise<WebElement | undefined>;
28
28
  }
29
29
  /**
30
30
  * The interface that is exposed by applying this mixin.
@@ -63,7 +63,7 @@ function default_1(Base) {
63
63
  if (!this.handle) {
64
64
  this.handle = await this.getDriver().getWindowHandle();
65
65
  }
66
- const view = await this.getViewToSwitchTo(this.handle);
66
+ const view = await this.getViewToSwitchTo();
67
67
  if (!view) {
68
68
  return;
69
69
  }
@@ -14,14 +14,14 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- import { WebElement } from 'selenium-webdriver';
17
+ import { Locator, WebElement } from 'selenium-webdriver';
18
18
  import { AbstractElement } from '../AbstractElement';
19
19
  /**
20
20
  * Page object representing a user-contributed panel implemented using a Webview.
21
21
  */
22
22
  declare class WebviewViewBase extends AbstractElement {
23
- constructor();
24
- getViewToSwitchTo(handle: string): Promise<WebElement | undefined>;
23
+ constructor(base?: Locator | WebElement, enclosingItem?: WebElement | Locator);
24
+ getViewToSwitchTo(): Promise<WebElement | undefined>;
25
25
  }
26
26
  export declare const WebviewView: new (...args: any[]) => WebviewViewBase & import("../WebviewMixin").WebviewMixinType;
27
27
  export type WebviewView = InstanceType<typeof WebviewView>;
@@ -22,16 +22,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.WebviewView = void 0;
23
23
  const AbstractElement_1 = require("../AbstractElement");
24
24
  const WebviewMixin_1 = __importDefault(require("../WebviewMixin"));
25
+ const locators_1 = require("../../locators/locators");
25
26
  /**
26
27
  * Page object representing a user-contributed panel implemented using a Webview.
27
28
  */
28
29
  class WebviewViewBase extends AbstractElement_1.AbstractElement {
29
- constructor() {
30
- super(WebviewViewBase.locators.Workbench.constructor);
30
+ constructor(base = WebviewViewBase.locators.Workbench.constructor, enclosingItem) {
31
+ super(base, enclosingItem);
31
32
  }
32
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
33
- async getViewToSwitchTo(handle) {
34
- return await this.getDriver().findElement(WebviewViewBase.locators.WebviewView.iframe);
33
+ async getViewToSwitchTo() {
34
+ const frames = await this.getDriver().findElements(WebviewViewBase.locators.WebView.iframe);
35
+ return (0, locators_1.findBestContainingElement)(await this.getRect(), frames);
35
36
  }
36
37
  }
37
38
  exports.WebviewView = (0, WebviewMixin_1.default)(WebviewViewBase);
@@ -14,16 +14,18 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- import { EditorGroup } from './EditorView';
18
- import { ContextMenu, WebElement } from '../..';
19
- import { ElementWithContextMenu } from '../ElementWithContextMenu';
20
- export declare class EditorAction extends ElementWithContextMenu {
21
- constructor(element: WebElement, parent: EditorGroup);
17
+ import { ActionButtonElementDropdown } from '../ActionButtonElementDropdown';
18
+ /**
19
+ * Base class for editor actions that provides a common method to get the title.
20
+ */
21
+ declare abstract class BaseEditorAction extends ActionButtonElementDropdown {
22
22
  /**
23
23
  * Get text description of the action.
24
24
  */
25
25
  getTitle(): Promise<string>;
26
26
  }
27
- export declare class EditorActionDropdown extends EditorAction {
28
- open(): Promise<ContextMenu>;
27
+ export declare class EditorAction extends BaseEditorAction {
28
+ }
29
+ export declare class EditorActionDropdown extends BaseEditorAction {
29
30
  }
31
+ export {};
@@ -17,52 +17,22 @@
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.EditorActionDropdown = exports.EditorAction = void 0;
20
- const __1 = require("../..");
21
- const ElementWithContextMenu_1 = require("../ElementWithContextMenu");
22
- class EditorAction extends ElementWithContextMenu_1.ElementWithContextMenu {
23
- constructor(element, parent) {
24
- super(element, parent);
25
- }
20
+ const ActionButtonElementDropdown_1 = require("../ActionButtonElementDropdown");
21
+ /**
22
+ * Base class for editor actions that provides a common method to get the title.
23
+ */
24
+ class BaseEditorAction extends ActionButtonElementDropdown_1.ActionButtonElementDropdown {
26
25
  /**
27
26
  * Get text description of the action.
28
27
  */
29
28
  async getTitle() {
30
- return await this.getAttribute(EditorAction.locators.EditorView.attribute);
29
+ return await this.getAttribute(BaseEditorAction.locators.EditorView.attribute);
31
30
  }
32
31
  }
32
+ class EditorAction extends BaseEditorAction {
33
+ }
33
34
  exports.EditorAction = EditorAction;
34
- class EditorActionDropdown extends EditorAction {
35
- async open() {
36
- await this.click();
37
- const shadowRootHost = await this.enclosingItem.findElements(EditorAction.locators.EditorAction.shadowRootHost);
38
- const actions = this.getDriver().actions();
39
- await actions.clear();
40
- await actions.sendKeys(__1.Key.ESCAPE).perform();
41
- const webdriverCapabilities = await this.getDriver().getCapabilities();
42
- const chromiumVersion = webdriverCapabilities.getBrowserVersion();
43
- if (shadowRootHost.length > 0) {
44
- if ((await this.getAttribute('aria-expanded')) !== 'true') {
45
- await this.click();
46
- }
47
- let shadowRoot;
48
- const webdriverCapabilities = await this.getDriver().getCapabilities();
49
- const chromiumVersion = webdriverCapabilities.getBrowserVersion();
50
- if (chromiumVersion && parseInt(chromiumVersion.split('.')[0]) >= 96) {
51
- shadowRoot = await shadowRootHost[0].getShadowRoot();
52
- return new __1.ContextMenu(await shadowRoot.findElement(EditorAction.locators.EditorAction.monacoMenuContainer)).wait();
53
- }
54
- else {
55
- shadowRoot = (await this.getDriver().executeScript('return arguments[0].shadowRoot', shadowRootHost[0]));
56
- return new __1.ContextMenu(shadowRoot).wait();
57
- }
58
- }
59
- else if (chromiumVersion && parseInt(chromiumVersion.split('.')[0]) >= 100) {
60
- await this.click();
61
- const workbench = await this.getDriver().findElement(ElementWithContextMenu_1.ElementWithContextMenu.locators.Workbench.constructor);
62
- return new __1.ContextMenu(workbench).wait();
63
- }
64
- return await super.openContextMenu();
65
- }
35
+ class EditorActionDropdown extends BaseEditorAction {
66
36
  }
67
37
  exports.EditorActionDropdown = EditorActionDropdown;
68
38
  //# sourceMappingURL=EditorAction.js.map
@@ -20,7 +20,7 @@ import { Editor } from './Editor';
20
20
  * Page object representing an open editor containing a web view
21
21
  */
22
22
  declare class WebViewBase extends Editor {
23
- getViewToSwitchTo(handle: string): Promise<WebElement | undefined>;
23
+ getViewToSwitchTo(): Promise<WebElement | undefined>;
24
24
  }
25
25
  export declare const WebView: new (...args: any[]) => WebViewBase & import("../WebviewMixin").WebviewMixinType;
26
26
  export type WebView = InstanceType<typeof WebView>;
@@ -22,21 +22,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.WebView = void 0;
23
23
  const WebviewMixin_1 = __importDefault(require("../WebviewMixin"));
24
24
  const Editor_1 = require("./Editor");
25
+ const locators_1 = require("../../locators/locators");
25
26
  /**
26
27
  * Page object representing an open editor containing a web view
27
28
  */
28
29
  class WebViewBase extends Editor_1.Editor {
29
- async getViewToSwitchTo(handle) {
30
- const handles = await this.getDriver().getAllWindowHandles();
31
- for (const handle of handles) {
32
- await this.getDriver().switchTo().window(handle);
33
- if ((await this.getDriver().getTitle()).includes('Virtual Document')) {
34
- await this.getDriver().switchTo().frame(0);
35
- return;
36
- }
37
- }
38
- await this.getDriver().switchTo().window(handle);
39
- return await this.getDriver().findElement(WebViewBase.locators.WebView.iframe);
30
+ async getViewToSwitchTo() {
31
+ const frames = await this.getDriver().findElements(WebViewBase.locators.WebView.iframe);
32
+ return (0, locators_1.findBestContainingElement)(await this.getRect(), frames);
40
33
  }
41
34
  }
42
35
  exports.WebView = (0, WebviewMixin_1.default)(WebViewBase);
@@ -17,6 +17,7 @@
17
17
  import { WebElement } from 'selenium-webdriver';
18
18
  import { ContextMenu, ViewContent, ViewItem, WelcomeContentSection } from '../..';
19
19
  import { AbstractElement } from '../AbstractElement';
20
+ import { ActionButtonElementDropdown } from '../ActionButtonElementDropdown';
20
21
  export type ViewSectionConstructor<T extends ViewSection> = {
21
22
  new (rootElement: WebElement, tree: ViewContent): T;
22
23
  };
@@ -102,13 +103,23 @@ export declare abstract class ViewSection extends AbstractElement {
102
103
  private isHeaderHidden;
103
104
  }
104
105
  /**
105
- * Action button on the header of a view section
106
+ * Base class for action buttons on view sections.
107
+ * Provides shared functionality for both standard and dropdown actions.
106
108
  */
107
- export declare class ViewPanelAction extends AbstractElement {
109
+ declare abstract class BaseViewPanelAction extends ActionButtonElementDropdown {
108
110
  constructor(element: WebElement, viewPart: ViewSection);
109
111
  /**
110
112
  * Get label of the action button
111
113
  */
112
114
  getLabel(): Promise<string>;
115
+ /**
116
+ * Wait for the action button to be located within a given timeout.
117
+ * @param timeout Time in milliseconds (default: 1000ms)
118
+ */
113
119
  wait(timeout?: number): Promise<this>;
114
120
  }
121
+ export declare class ViewPanelAction extends BaseViewPanelAction {
122
+ }
123
+ export declare class ViewPanelActionDropdown extends BaseViewPanelAction {
124
+ }
125
+ export {};
@@ -16,11 +16,12 @@
16
16
  * limitations under the License.
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.ViewPanelAction = exports.ViewSection = void 0;
19
+ exports.ViewPanelActionDropdown = exports.ViewPanelAction = exports.ViewSection = void 0;
20
20
  const selenium_webdriver_1 = require("selenium-webdriver");
21
21
  const __1 = require("../..");
22
22
  const AbstractElement_1 = require("../AbstractElement");
23
23
  const ElementWithContextMenu_1 = require("../ElementWithContextMenu");
24
+ const ActionButtonElementDropdown_1 = require("../ActionButtonElementDropdown");
24
25
  /**
25
26
  * Page object representing a collapsible content section of the side bar view
26
27
  */
@@ -49,6 +50,7 @@ class ViewSection extends AbstractElement_1.AbstractElement {
49
50
  const collapseExpandButton = await header.findElement(ViewSection.locators.ViewSection.headerCollapseExpandButton);
50
51
  await collapseExpandButton.click();
51
52
  await this.getDriver().wait((0, __1.waitForAttributeValue)(header, ViewSection.locators.ViewSection.headerExpanded, 'true'), timeout);
53
+ await this.getDriver().sleep(500);
52
54
  }
53
55
  }
54
56
  /**
@@ -64,6 +66,7 @@ class ViewSection extends AbstractElement_1.AbstractElement {
64
66
  const collapseExpandButton = await header.findElement(ViewSection.locators.ViewSection.headerCollapseExpandButton);
65
67
  await collapseExpandButton.click();
66
68
  await this.getDriver().wait((0, __1.waitForAttributeValue)(header, ViewSection.locators.ViewSection.headerExpanded, 'false'), timeout);
69
+ await this.getDriver().sleep(500);
67
70
  }
68
71
  }
69
72
  /**
@@ -97,16 +100,17 @@ class ViewSection extends AbstractElement_1.AbstractElement {
97
100
  * @returns Promise resolving to array of ViewPanelAction objects
98
101
  */
99
102
  async getActions() {
100
- const actions = [];
101
- if (!(await this.isHeaderHidden())) {
102
- const header = await this.findElement(ViewSection.locators.ViewSection.header);
103
- const act = await header.findElement(ViewSection.locators.ViewSection.actions);
104
- const elements = await act.findElements(ViewSection.locators.ViewSection.button);
105
- for (const element of elements) {
106
- actions.push(await new ViewPanelAction(element, this).wait());
103
+ await this.getDriver().actions().move({ origin: this }).perform(); // move mouse to bring auto-hided buttons visible
104
+ const actions = await this.findElement(ViewSection.locators.ViewSection.actions).findElements(ViewSection.locators.ViewSection.button);
105
+ return Promise.all(actions.map(async (action) => {
106
+ const dropdown = (await action.getAttribute('aria-haspopup')) || (await action.getAttribute('aria-expanded'));
107
+ if (dropdown) {
108
+ return new ViewPanelActionDropdown(action, this);
107
109
  }
108
- }
109
- return actions;
110
+ else {
111
+ return new ViewPanelAction(action, this);
112
+ }
113
+ }));
110
114
  }
111
115
  /**
112
116
  * Retrieve an action button on the sections's header by its label
@@ -161,9 +165,10 @@ class ViewSection extends AbstractElement_1.AbstractElement {
161
165
  }
162
166
  exports.ViewSection = ViewSection;
163
167
  /**
164
- * Action button on the header of a view section
168
+ * Base class for action buttons on view sections.
169
+ * Provides shared functionality for both standard and dropdown actions.
165
170
  */
166
- class ViewPanelAction extends AbstractElement_1.AbstractElement {
171
+ class BaseViewPanelAction extends ActionButtonElementDropdown_1.ActionButtonElementDropdown {
167
172
  constructor(element, viewPart) {
168
173
  super(element, viewPart);
169
174
  }
@@ -171,12 +176,21 @@ class ViewPanelAction extends AbstractElement_1.AbstractElement {
171
176
  * Get label of the action button
172
177
  */
173
178
  async getLabel() {
174
- return await this.getAttribute(ViewSection.locators.ViewSection.buttonLabel);
179
+ return await this.getAttribute(BaseViewPanelAction.locators.ViewSection.buttonLabel);
175
180
  }
176
- async wait(timeout = 1000) {
177
- await this.getDriver().wait(selenium_webdriver_1.until.elementLocated(ViewSection.locators.ViewSection.actions), timeout);
181
+ /**
182
+ * Wait for the action button to be located within a given timeout.
183
+ * @param timeout Time in milliseconds (default: 1000ms)
184
+ */
185
+ async wait(timeout = 1_000) {
186
+ await this.getDriver().wait(selenium_webdriver_1.until.elementLocated(BaseViewPanelAction.locators.ViewSection.actions), timeout);
178
187
  return this;
179
188
  }
180
189
  }
190
+ class ViewPanelAction extends BaseViewPanelAction {
191
+ }
181
192
  exports.ViewPanelAction = ViewPanelAction;
193
+ class ViewPanelActionDropdown extends BaseViewPanelAction {
194
+ }
195
+ exports.ViewPanelActionDropdown = ViewPanelActionDropdown;
182
196
  //# sourceMappingURL=ViewSection.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 { By, WebElement } from 'selenium-webdriver';
17
+ import { By, IRectangle, WebElement } from 'selenium-webdriver';
18
18
  import { PartialDeep } from 'type-fest';
19
19
  import { ViewSection } from '../components/sidebar/ViewSection';
20
20
  type WebElementFunction<E extends WebElement, T> = (element: E) => T | PromiseLike<T>;
@@ -214,8 +214,6 @@ export interface Locators {
214
214
  WebView: {
215
215
  iframe: By;
216
216
  activeFrame: By;
217
- container: (id: string) => By;
218
- attribute: string;
219
217
  };
220
218
  ExtensionEditorView: {
221
219
  constructor: By;
@@ -554,4 +552,5 @@ export declare function hasNotClass(klass: string, locator?: By): (el: WebElemen
554
552
  export declare function hasElement(locatorSelector: (l: Locators) => By): (el: WebElement, locators: Locators) => Promise<boolean>;
555
553
  export declare function fromAttribute(attribute: string, locator?: By): (el: WebElement) => Promise<string>;
556
554
  export declare function fromText(locator?: By): (el: WebElement) => Promise<string>;
555
+ export declare function findBestContainingElement(container: IRectangle, testElements: WebElement[]): Promise<WebElement | undefined>;
557
556
  export {};
@@ -22,6 +22,7 @@ exports.hasNotClass = hasNotClass;
22
22
  exports.hasElement = hasElement;
23
23
  exports.fromAttribute = fromAttribute;
24
24
  exports.fromText = fromText;
25
+ exports.findBestContainingElement = findBestContainingElement;
25
26
  function hasAttribute(attr, value, locator) {
26
27
  return async (el) => {
27
28
  el = locator ? await el.findElement(locator) : el;
@@ -64,4 +65,24 @@ function fromText(locator) {
64
65
  return await el.getText();
65
66
  };
66
67
  }
68
+ async function findBestContainingElement(container, testElements) {
69
+ const areas = await Promise.all(testElements.map(async (value) => {
70
+ const rect = await value.getRect();
71
+ const ax = Math.max(container.x, rect.x);
72
+ const ay = Math.max(container.y, rect.y);
73
+ const bx = Math.min(container.x + container.width, rect.x + rect.width);
74
+ const by = Math.min(container.y + container.height, rect.y + rect.height);
75
+ return (bx - ax) * (by - ay);
76
+ }));
77
+ let bestIdx = -1;
78
+ for (let i = 0; i < testElements.length; i++) {
79
+ if (areas[i] < 0) {
80
+ continue;
81
+ }
82
+ if (bestIdx === -1 || areas[i] > areas[bestIdx]) {
83
+ bestIdx = i;
84
+ }
85
+ }
86
+ return bestIdx === -1 ? undefined : testElements[bestIdx];
87
+ }
67
88
  //# sourceMappingURL=locators.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redhat-developer/page-objects",
3
- "version": "1.10.0",
3
+ "version": "1.12.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",
@@ -41,11 +41,11 @@
41
41
  "clone-deep": "^4.0.1",
42
42
  "compare-versions": "^6.1.1",
43
43
  "fs-extra": "^11.3.0",
44
- "type-fest": "^4.34.1"
44
+ "type-fest": "^4.39.1"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "selenium-webdriver": ">=4.6.1",
48
48
  "typescript": ">=4.6.2"
49
49
  },
50
- "gitHead": "eaf266f43e1dca66cb0d660ecbf8f59e56af4808"
50
+ "gitHead": "006642fd809f6b11cc662ed0f190295efff61602"
51
51
  }