import { getBrowserObject } from '@wdio/utils' import { hasElementId } from '../../utils/index.js' import isElementDisplayedLegacyScript from '../../scripts/isElementDisplayed.js' import isElementInViewportScript from '../../scripts/isElementInViewport.js' /** * * Return true if the selected DOM-element is displayed (even when the element is outside the viewport). It is using * the [`checkVisibility`](https://developer.mozilla.org/en-US/docs/Web/API/Element/checkVisibility#visibilityproperty) * method provided by the browser to determine if an element is being displayed or not. Since WebdriverIO acts as a * real user, the default values for the `contentVisibilityAuto`, `opacityProperty`, and `visibilityProperty` flags * are set to `true` to default to a more strict behavior. This means that the command will check if the element is * visible due to the value of its `content-visibility`, `opacity`, and `visibility` properties. * * If you want to also verify that the element is also within the viewport, provide the `withinViewport` flag to the command. * * :::info * * As opposed to other element commands WebdriverIO will not wait for the element * to exist to execute this command. * * ::: * * WebdriverIO, when conducting browser tests, utilizes a [custom script](https://github.com/webdriverio/webdriverio/blob/59d349ca847950354d02b9e548f60cc50e7871f0/packages/webdriverio/src/scripts/isElementDisplayed.ts) * specifically designed to assess the visibility of elements. This script is key in determining whether an * element is displayed on the page. Conversely, for native mobile testing scenarios with Appium, WebdriverIO * defers to the [`isElementDisplayed`](https://appium.io/docs/en/2.1/reference/interfaces/appium_types.ExternalDriver/#elementdisplayed) * command provided by Appium. This command evaluates the visibility of elements using criteria established by the * underlying Appium driver, ensuring accurate and driver-specific assessments for mobile applications. * * :index.html
Hello World!
:isDisplayed.js it('should detect if an element is displayed', async () => { elem = await $('#notExisting'); isDisplayed = await elem.isDisplayed(); console.log(isDisplayed); // outputs: false let elem = await $('#noSize'); let isDisplayed = await elem.isDisplayed(); console.log(isDisplayed); // outputs: false let elem = await $('#noSizeWithContent'); let isDisplayed = await elem.isDisplayed(); console.log(isDisplayed); // outputs: true let elem = await $('#notDisplayed'); let isDisplayed = await elem.isDisplayed(); console.log(isDisplayed); // outputs: false elem = await $('#notVisible'); isDisplayed = await elem.isDisplayed(); console.log(isDisplayed); // outputs: false elem = await $('#zeroOpacity'); isDisplayed = await elem.isDisplayed(); console.log(isDisplayed); // outputs: false elem = await $('#notInViewport'); isDisplayed = await elem.isDisplayed(); console.log(isDisplayed); // outputs: true }); isDisplayedWithinViewport.js it('should detect if an element is visible within the viewport', async () => { let isDisplayedInViewport = await $('#notDisplayed').isDisplayed({ withinViewport: true }); console.log(isDisplayedInViewport); // outputs: false isDisplayedInViewport = await $('#notVisible').isDisplayed({ withinViewport: true }); console.log(isDisplayedInViewport); // outputs: false isDisplayedInViewport = await $('#notExisting').isDisplayed({ withinViewport: true }); console.log(isDisplayedInViewport); // outputs: false isDisplayedInViewport = await $('#notInViewport').isDisplayed({ withinViewport: true }); console.log(isDisplayedInViewport); // outputs: false isDisplayedInViewport = await $('#zeroOpacity').isDisplayed({ withinViewport: true }); console.log(isDisplayedInViewport); // outputs: false }); *
* * @alias element.isDisplayed * @param {Boolean} [withinViewport=false] `true` to check if the element is within the viewport. `false` by default. * @param {Boolean} [contentVisibilityAuto=true] `true` to check if the element content-visibility property has (or inherits) the value auto, and it is currently skipping its rendering. `true` by default. * @param {Boolean} [opacityProperty=true] `true` to check if the element opacity property has (or inherits) a value of 0. `true` by default. * @param {Boolean} [visibilityProperty=true] `true` to check if the element is invisible due to the value of its visibility property. `true` by default. * @return {Boolean} true if element is displayed * @uses protocol/elements, protocol/elementIdDisplayed * @type state */ export async function isDisplayed ( this: WebdriverIO.Element, commandParams: IsDisplayedParams = DEFAULT_PARAMS ) { const browser = getBrowserObject(this) if (!await hasElementId(this)) { return false } /** * For mobile sessions with Appium we continue to use the elementDisplayed command * as we can't run JS in native apps */ if (browser.isMobile && (browser.isNativeContext || browser.isWindowsApp || browser.isMacApp)) { /** * there is no support yet for checking if an element is displayed within the * viewport for native apps. We can only check if it's displayed at all. */ if (commandParams?.withinViewport) { throw new Error( 'Cannot determine element visibility within viewport for native mobile apps ' + 'as it is not feasible to determine full vertical and horizontal application bounds. ' + 'In most cases a basic visibility check should suffice.' ) } return await this.isElementDisplayed(this.elementId) } let hadToFallback = false const [isDisplayed, displayProperty] = await Promise.all([ browser.execute(function checkVisibility (elem, params) { if (typeof elem.checkVisibility === 'function') { return elem.checkVisibility(params) } // Fallback to legacy script if checkVisibility is not available return null }, this as unknown as HTMLElement, { ...DEFAULT_PARAMS, ...commandParams }).then((result) => { if (result === null) { hadToFallback = true return browser.execute(isElementDisplayedLegacyScript, this as unknown as HTMLElement) } return result }), /** * don't fail if element is not existing */ this.getCSSProperty('display').catch(() => ({ value: '' })) ]) /** * If the element is displayed with `display: contents` we need to recheck * the visibility as the element itself is not visible but its children are * (if there are any). Hence, we run the legacy script for it. */ const hasDisplayContentsCSSProperty = displayProperty.value === 'contents' const shouldRecheckContentVisibility = !hadToFallback && hasDisplayContentsCSSProperty const finalResponse = shouldRecheckContentVisibility ? await browser.execute(isElementDisplayedLegacyScript, this as unknown as HTMLElement).catch(() => false) : isDisplayed if (finalResponse && commandParams?.withinViewport) { return browser.execute(isElementInViewportScript, this as unknown as HTMLElement) } return finalResponse } const DEFAULT_PARAMS: IsDisplayedParams = { withinViewport: false, contentVisibilityAuto: true, opacityProperty: true, visibilityProperty: true } interface IsDisplayedParams { /** * `true` to check if the element is within the viewport. false by default. */ withinViewport?: boolean /** * `true` to check if the element content-visibility property has (or inherits) the value auto, * and it is currently skipping its rendering. `true` by default. * @default true */ contentVisibilityAuto?: boolean /** * `true` to check if the element opacity property has (or inherits) a value of 0. `true` by default. * @default true */ opacityProperty?: boolean /** * `true` to check if the element is invisible due to the value of its visibility property. `true` by default. * @default true */ visibilityProperty?: boolean }