diff --git a/src/client/core/utils/position.js b/src/client/core/utils/position.js index 963fc75aa5..e48616b32a 100644 --- a/src/client/core/utils/position.js +++ b/src/client/core/utils/position.js @@ -4,7 +4,7 @@ import * as domUtils from './dom'; import * as shared from './shared/position'; import AxisValues from '../../../shared/utils/values/axis-values'; -export { isElementVisible } from './shared/visibility'; +export { isElementVisible, isIframeVisible } from './shared/visibility'; export const getElementRectangle = hammerhead.utils.position.getElementRectangle; export const getOffsetPosition = hammerhead.utils.position.getOffsetPosition; diff --git a/src/client/core/utils/shared/visibility.ts b/src/client/core/utils/shared/visibility.ts index b4ecd456cc..3eeb6ca6a6 100644 --- a/src/client/core/utils/shared/visibility.ts +++ b/src/client/core/utils/shared/visibility.ts @@ -1,6 +1,10 @@ import adapter from './adapter/index'; import { isNotVisibleNode, hasDimensions } from './style'; +export function isIframeVisible (el: Node): boolean { + return !hiddenUsingStyles(el as HTMLElement); +} + function hiddenUsingStyles (el: HTMLElement): boolean { return adapter.style.get(el, 'visibility') === 'hidden' || adapter.style.get(el, 'display') === 'none'; diff --git a/src/client/driver/command-executors/client-functions/adapter/initializer.ts b/src/client/driver/command-executors/client-functions/adapter/initializer.ts index 5955a1e192..46352ba757 100644 --- a/src/client/driver/command-executors/client-functions/adapter/initializer.ts +++ b/src/client/driver/command-executors/client-functions/adapter/initializer.ts @@ -25,6 +25,8 @@ const initializer: ClientFunctionAdapter = { getTagName: domUtils.getTagName, isOptionElementVisible: selectElementUI.isOptionElementVisible, isElementVisible: positionUtils.isElementVisible, + isIframeVisible: positionUtils.isIframeVisible, + isIframeElement: domUtils.isIframeElement, getActiveElement: domUtils.getActiveElement, }; diff --git a/src/client/driver/command-executors/client-functions/selector-executor/utils.ts b/src/client/driver/command-executors/client-functions/selector-executor/utils.ts index 8ad73c63e7..f6a0e91331 100644 --- a/src/client/driver/command-executors/client-functions/selector-executor/utils.ts +++ b/src/client/driver/command-executors/client-functions/selector-executor/utils.ts @@ -2,6 +2,9 @@ import adapter from '..//adapter/index'; export function visible (el: Node): boolean { + if (adapter.isIframeElement(el)) + return adapter.isIframeVisible(el); + if (!adapter.isDomElement(el) && !adapter.isTextNode(el)) return false; diff --git a/src/client/driver/command-executors/client-functions/types.d.ts b/src/client/driver/command-executors/client-functions/types.d.ts index 1cbfe1f426..bd267ff42e 100644 --- a/src/client/driver/command-executors/client-functions/types.d.ts +++ b/src/client/driver/command-executors/client-functions/types.d.ts @@ -70,5 +70,7 @@ export interface ClientFunctionAdapter { getTagName (el: Element): string; isOptionElementVisible (el: Node): boolean; isElementVisible (el: Node): boolean; + isIframeElement (el: Node): boolean; + isIframeVisible (el: Node): boolean; getActiveElement (): Node; } diff --git a/src/client/driver/driver-link/iframe/child.js b/src/client/driver/driver-link/iframe/child.js index 28054db2da..7cb69027dc 100644 --- a/src/client/driver/driver-link/iframe/child.js +++ b/src/client/driver/driver-link/iframe/child.js @@ -44,7 +44,7 @@ export default class ChildIframeDriverLink { if (!domUtils.isElementInDocument(this.driverIframe)) return Promise.reject(new CurrentIframeNotFoundError()); - return waitFor(() => positionUtils.isElementVisible(this.driverIframe) ? this.driverIframe : null, + return waitFor(() => positionUtils.isIframeVisible(this.driverIframe) ? this.driverIframe : null, CHECK_IFRAME_VISIBLE_INTERVAL, this.iframeAvailabilityTimeout) .catch(() => { throw new CurrentIframeIsInvisibleError(); diff --git a/src/client/driver/driver.js b/src/client/driver/driver.js index 81bfd7bde9..c4ff529311 100644 --- a/src/client/driver/driver.js +++ b/src/client/driver/driver.js @@ -102,7 +102,11 @@ import ChildWindowDriverLink from './driver-link/window/child'; import ParentWindowDriverLink from './driver-link/window/parent'; import sendConfirmationMessage from './driver-link/send-confirmation-message'; import DriverRole from './role'; -import { CHECK_CHILD_WINDOW_CLOSED_INTERVAL, WAIT_FOR_WINDOW_DRIVER_RESPONSE_TIMEOUT } from './driver-link/timeouts'; +import { + CHECK_CHILD_WINDOW_CLOSED_INTERVAL, + WAIT_FOR_IFRAME_DRIVER_RESPONSE_TIMEOUT, + WAIT_FOR_WINDOW_DRIVER_RESPONSE_TIMEOUT, +} from './driver-link/timeouts'; import sendMessageToDriver from './driver-link/send-message-to-driver'; import getExecutorResultDriverStatus from './command-executors/get-executor-result-driver-status'; import SelectorExecutor from './command-executors/client-functions/selector-executor'; @@ -961,8 +965,14 @@ export default class Driver extends serviceUtils.EventEmitter { if (!domUtils.isIframeElement(iframe)) throw new ActionElementNotIframeError(); + // NOTE: RG-4558 Previously we waited for iframe become visible when execute selector + // We need to add a timeout to be sure that iframe driver is initialized + const childLinkResponseTimeout = hasSpecificTimeout + ? commandSelectorTimeout + : Math.max(commandSelectorTimeout, WAIT_FOR_IFRAME_DRIVER_RESPONSE_TIMEOUT); + return this._ensureChildIframeDriverLink(nativeMethods.contentWindowGetter.call(iframe), - iframeErrorCtors.NotLoadedError, commandSelectorTimeout); + iframeErrorCtors.NotLoadedError, childLinkResponseTimeout); }) .then(childDriverLink => { childDriverLink.availabilityTimeout = commandSelectorTimeout; diff --git a/src/client/proxyless/client-fn-adapter-initializer.ts b/src/client/proxyless/client-fn-adapter-initializer.ts index f9939c46f7..92dd22f153 100644 --- a/src/client/proxyless/client-fn-adapter-initializer.ts +++ b/src/client/proxyless/client-fn-adapter-initializer.ts @@ -2,7 +2,7 @@ import { ClientFunctionAdapter } from '../driver/command-executors/client-functi import nativeMethods from './native-methods'; import * as domUtils from './utils/dom'; import * as styleUtils from './utils/style'; -import { isElementVisible } from '../core/utils/shared/visibility'; +import { isElementVisible, isIframeVisible } from '../core/utils/shared/visibility'; const initializer: ClientFunctionAdapter = { @@ -19,6 +19,8 @@ const initializer: ClientFunctionAdapter = { isOptionElement: domUtils.isOptionElement, getTagName: domUtils.getTagName, getActiveElement: domUtils.getActiveElement, + isIframeVisible: isIframeVisible, + isIframeElement: domUtils.isIframeElement, isOptionElementVisible: styleUtils.isOptionElementVisible, isElementVisible: isElementVisible, diff --git a/src/client/proxyless/utils/dom.ts b/src/client/proxyless/utils/dom.ts index 3ed9a48bdf..a26cd2e173 100644 --- a/src/client/proxyless/utils/dom.ts +++ b/src/client/proxyless/utils/dom.ts @@ -214,6 +214,10 @@ export function isElementInIframe (el: Element | Document, currentDocument?: Doc return window.document !== doc; } +export function isIframeElement (el: Element): boolean { + return el instanceof HTMLIFrameElement; +} + export function isWindow (el: Node | Window | Document): el is Window { return 'pageYOffset' in el; } diff --git a/test/functional/fixtures/api/es-next/iframe-switching/pages/index.html b/test/functional/fixtures/api/es-next/iframe-switching/pages/index.html index 49a32e6b89..828ff131b5 100644 --- a/test/functional/fixtures/api/es-next/iframe-switching/pages/index.html +++ b/test/functional/fixtures/api/es-next/iframe-switching/pages/index.html @@ -13,7 +13,7 @@