Skip to content

[🐛 Bug]: Use of nullish coalescing operator breaks compatibility with older browsers #14375

@will-stone

Description

@will-stone

Have you read the Contributing Guidelines on issues?

WebdriverIO Version

9.12.3

Node.js Version

22.14.0

Mode

WDIO Testrunner

Which capabilities are you using?

{
  browserName: "Chrome",
  browserVersion: "64",
  "goog:chromeOptions": {
    args: [
      "--no-sandbox",
      "--disable-gpu",
      "--disable-dev-shm-usage",
      "--disable-extensions",
      "--disable-web-security",
    ],
  },
  platformName: "WINDOWS",
},
{
  browserName: "Edge",
  browserVersion: "80",
  platformName: "WINDOWS",
},
{
  browserName: "Firefox",
  browserVersion: "78",
  platformName: "WINDOWS",
},
{
  browserName: "Safari",
  browserVersion: "12.1",
  platformName: "MAC",
},

What happened?

We run WDIO against Browserstack against some very old browsers. Ever since the introduction of this code, we are unable to run WDIO in browsers that don't support nullish coalescing.

Thanks for looking into this.

What is your expected behavior?

Tests run without error.

How to reproduce the bug.

Upgrade to latest WDIO packages (but any from 9.12.1 onwards) then run a test that tries to click on something in a browser that doesn't support nullish coalescing, e.g. Chrome 79

Relevant log output

[Safari 12.1.2 macOS #3-0] Error: waitUntil condition failed with the following reason: WebDriverError: A JavaScript exception occured: Unexpected token '?' when running "execute/sync" with method "POST" and args "function isElementClickable(elem) {
[Safari 12.1.2 macOS #3-0] Error: waitUntil condition failed with the following reason: WebDriverError: A JavaScript exception occured: Unexpected token '?' when running "execute/sync" with method "POST" and args "function isElementClickable(elem) {
[Safari 12.1.2 macOS #3-0]   if (!elem.getBoundingClientRect || !elem.scrollIntoView || !elem.contains || !elem.getClientRects || !document.elementFromPoint) {
[Safari 12.1.2 macOS #3-0]     return false;
[Safari 12.1.2 macOS #3-0]   }
[Safari 12.1.2 macOS #3-0]   const isOldEdge = !!window.StyleMedia;
[Safari 12.1.2 macOS #3-0]   const scrollIntoViewFullSupport = !(window.safari || isOldEdge);
[Safari 12.1.2 macOS #3-0]   function getOverlappingElement(elem2, context) {
[Safari 12.1.2 macOS #3-0]     context = context || document;
[Safari 12.1.2 macOS #3-0]     const elemDimension = elem2.getBoundingClientRect();
[Safari 12.1.2 macOS #3-0]     const x = elemDimension.left + elem2.clientWidth / 2;
[Safari 12.1.2 macOS #3-0]     const y = elemDimension.top + elem2.clientHeight / 2;
[Safari 12.1.2 macOS #3-0]     return context.elementFromPoint(x, y);
[Safari 12.1.2 macOS #3-0]   }
[Safari 12.1.2 macOS #3-0]   function getOverlappingRects(elem2, context) {
[Safari 12.1.2 macOS #3-0]     context = context || document;
[Safari 12.1.2 macOS #3-0]     const rects = elem2.getClientRects();
[Safari 12.1.2 macOS #3-0]     const rect = rects[0];
[Safari 12.1.2 macOS #3-0]     const x = rect.left + rect.width / 2;
[Safari 12.1.2 macOS #3-0]     const y = rect.top + rect.height / 2;
[Safari 12.1.2 macOS #3-0]     return [context.elementFromPoint(x, y)];
[Safari 12.1.2 macOS #3-0]   }
[Safari 12.1.2 macOS #3-0]   function getOverlappingElements(elem2, context) {
[Safari 12.1.2 macOS #3-0]     return [getOverlappingElement(elem2, context)].concat(getOverlappingRects(elem2, context));
[Safari 12.1.2 macOS #3-0]   }
[Safari 12.1.2 macOS #3-0]   function nodeContains(elem2, otherNode) {
[Safari 12.1.2 macOS #3-0]     if (isOldEdge) {
[Safari 12.1.2 macOS #3-0]       let tmpElement = otherNode;
[Safari 12.1.2 macOS #3-0]       while (tmpElement) {
[Safari 12.1.2 macOS #3-0]         if (tmpElement === elem2) {
[Safari 12.1.2 macOS #3-0]           return true;
[Safari 12.1.2 macOS #3-0]         }
[Safari 12.1.2 macOS #3-0]         tmpElement = tmpElement.parentNode;
[Safari 12.1.2 macOS #3-0]         if (tmpElement && tmpElement.nodeType === 11 && tmpElement.host) {
[Safari 12.1.2 macOS #3-0]           tmpElement = tmpElement.host;
[Safari 12.1.2 macOS #3-0]         }
[Safari 12.1.2 macOS #3-0]       }
[Safari 12.1.2 macOS #3-0]       return false;
[Safari 12.1.2 macOS #3-0]     }
[Safari 12.1.2 macOS #3-0]     return elem2.contains(otherNode);
[Safari 12.1.2 macOS #3-0]   }
[Safari 12.1.2 macOS #3-0]   function isOverlappingElementMatch(elementsFromPoint, elem2) {
[Safari 12.1.2 macOS #3-0]     if (elementsFromPoint.some(function(elementFromPoint) {
[Safari 12.1.2 macOS #3-0]       return elementFromPoint === elem2 || nodeContains(elem2, elementFromPoint);
[Safari 12.1.2 macOS #3-0]     })) {
[Safari 12.1.2 macOS #3-0]       return true;
[Safari 12.1.2 macOS #3-0]     }
[Safari 12.1.2 macOS #3-0]     let elemsWithShadowRoot = [].concat(elementsFromPoint);
[Safari 12.1.2 macOS #3-0]     elemsWithShadowRoot = elemsWithShadowRoot.filter(function(x) {
[Safari 12.1.2 macOS #3-0]       return x && x.shadowRoot && x.shadowRoot.elementFromPoint;
[Safari 12.1.2 macOS #3-0]     });
[Safari 12.1.2 macOS #3-0]     let shadowElementsFromPoint = [];
[Safari 12.1.2 macOS #3-0]     for (let i = 0; i < elemsWithShadowRoot.length; ++i) {
[Safari 12.1.2 macOS #3-0]       const shadowElement = elemsWithShadowRoot[i];
[Safari 12.1.2 macOS #3-0]       shadowElementsFromPoint = shadowElementsFromPoint.concat(
[Safari 12.1.2 macOS #3-0]         getOverlappingElements(elem2, shadowElement.shadowRoot)
[Safari 12.1.2 macOS #3-0]       );
[Safari 12.1.2 macOS #3-0]     }
[Safari 12.1.2 macOS #3-0]     shadowElementsFromPoint = [].concat(shadowElementsFromPoint);
[Safari 12.1.2 macOS #3-0]     shadowElementsFromPoint = shadowElementsFromPoint.filter(function(x) {
[Safari 12.1.2 macOS #3-0]       return !elementsFromPoint.includes(x);
[Safari 12.1.2 macOS #3-0]     });
[Safari 12.1.2 macOS #3-0]     if (shadowElementsFromPoint.length === 0) {
[Safari 12.1.2 macOS #3-0]       return false;
[Safari 12.1.2 macOS #3-0]     }
[Safari 12.1.2 macOS #3-0]     return isOverlappingElementMatch(shadowElementsFromPoint, elem2);
[Safari 12.1.2 macOS #3-0]   }
[Safari 12.1.2 macOS #3-0]   function isElementInViewport(elem2) {
[Safari 12.1.2 macOS #3-0]     if (!elem2.getBoundingClientRect) {
[Safari 12.1.2 macOS #3-0]       return false;
[Safari 12.1.2 macOS #3-0]     }
[Safari 12.1.2 macOS #3-0]     const rect = elem2.getBoundingClientRect();
[Safari 12.1.2 macOS #3-0]     const windowHeight = window.innerHeight || document.documentElement.clientHeight;
[Safari 12.1.2 macOS #3-0]     const windowWidth = window.innerWidth || document.documentElement.clientWidth;
[Safari 12.1.2 macOS #3-0]     const vertInView = rect.top <= windowHeight && rect.top + rect.height > 0;
[Safari 12.1.2 macOS #3-0]     const horInView = rect.left <= windowWidth && rect.left + rect.width > 0;
[Safari 12.1.2 macOS #3-0]     return vertInView && horInView;
[Safari 12.1.2 macOS #3-0]   }
[Safari 12.1.2 macOS #3-0]   function isEnabled(elem2) {
[Safari 12.1.2 macOS #3-0]     return elem2.disabled !== true;
[Safari 12.1.2 macOS #3-0]   }
[Safari 12.1.2 macOS #3-0]   function hasOverlaps(elem2) {
[Safari 12.1.2 macOS #3-0]     return !isOverlappingElementMatch(getOverlappingElements(elem2), elem2);
[Safari 12.1.2 macOS #3-0]   }
[Safari 12.1.2 macOS #3-0]   function isFullyDisplayedInViewport(elem2) {
[Safari 12.1.2 macOS #3-0]     return isElementInViewport(elem2) && !hasOverlaps(elem2);
[Safari 12.1.2 macOS #3-0]   }
[Safari 12.1.2 macOS #3-0]   function getViewportScrollPositions() {
[Safari 12.1.2 macOS #3-0]     return {
[Safari 12.1.2 macOS #3-0]       // Cross-browser compatibility
[Safari 12.1.2 macOS #3-0]       x: window.scrollX ?? window.pageXOffset,
[Safari 12.1.2 macOS #3-0]       y: window.scrollY ?? window.pageYOffset
[Safari 12.1.2 macOS #3-0]     };
[Safari 12.1.2 macOS #3-0]   }
[Safari 12.1.2 macOS #3-0]   let _isFullyDisplayedInViewport = isFullyDisplayedInViewport(elem);
[Safari 12.1.2 macOS #3-0]   if (!_isFullyDisplayedInViewport) {
[Safari 12.1.2 macOS #3-0]     const { x: originalX, y: originalY } = getViewportScrollPositions();
[Safari 12.1.2 macOS #3-0]     elem.scrollIntoView(scrollIntoViewFullSupport ? { block: "center", inline: "center" } : false);
[Safari 12.1.2 macOS #3-0]     _isFullyDisplayedInViewport = isFullyDisplayedInViewport(elem);
[Safari 12.1.2 macOS #3-0]     const { x: currentX, y: currentY } = getViewportScrollPositions();
[Safari 12.1.2 macOS #3-0]     if (currentX !== originalX || currentY !== originalY) {
[Safari 12.1.2 macOS #3-0]       window.scroll(originalX, originalY);
[Safari 12.1.2 macOS #3-0]     }
[Safari 12.1.2 macOS #3-0]   }
[Safari 12.1.2 macOS #3-0]   return _isFullyDisplayedInViewport && isEnabled(elem);
[Safari 12.1.2 macOS #3-0] }"

Code of Conduct

  • I agree to follow this project's Code of Conduct

Is there an existing issue for this?

  • I have searched the existing issues

Metadata

Metadata

Assignees

No one assigned

    Labels

    Bug 🐛help wantedIssues that are free to take by anyone interested

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions