From bf2ae1ece7294ff0577ccc4678123fc3759ed1f9 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Thu, 23 Dec 2021 17:49:20 +0100 Subject: [PATCH] MOBILE-3934 behat: Implement `within` locator --- tests/behat/app_behat_runtime.js | 40 +++++++++++++++++++++++++++++++- tests/behat/behat_app.php | 8 +++---- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/tests/behat/app_behat_runtime.js b/tests/behat/app_behat_runtime.js index 1567ac68a..03e647250 100644 --- a/tests/behat/app_behat_runtime.js +++ b/tests/behat/app_behat_runtime.js @@ -345,6 +345,26 @@ return element.parentElement || (element.getRootNode() && element.getRootNode().host) || null; }; + /** + * Get closest element matching a selector, without traversing up a given container. + * + * @param {HTMLElement} element Element. + * @param {string} selector Selector. + * @param {HTMLElement} container Topmost container to search within. + * @return {HTMLElement} Closest matching element. + */ + const getClosestMatching = function(element, selector, container) { + if (element.matches(selector)) { + return element; + } + + if (element === container || !element.parentElement) { + return null; + } + + return getClosestMatching(element.parentElement, selector, container); + }; + /** * Function to find elements based on their text or Aria label. * @@ -361,6 +381,24 @@ let container = topContainer; + if (locator.within) { + const withinElements = findElementsBasedOnText(locator.within); + + if (withinElements.length === 0) { + throw new Error('There was no match for within text') + } else if (withinElements.length > 1) { + const withinElementsAncestors = getTopAncestors(withinElements); + + if (withinElementsAncestors.length > 1) { + throw new Error('Too many matches for within text'); + } + + topContainer = container = withinElementsAncestors[0]; + } else { + topContainer = container = withinElements[0]; + } + } + if (topContainer && locator.near) { const nearElements = findElementsBasedOnText(locator.near); @@ -382,7 +420,7 @@ do { const elements = findElementsBasedOnTextWithin(container, locator.text); const filteredElements = locator.selector - ? elements.filter(element => element.matches(locator.selector)) + ? elements.map(element => getClosestMatching(element, locator.selector, container)).filter(element => !!element) : elements; if (filteredElements.length > 0) { diff --git a/tests/behat/behat_app.php b/tests/behat/behat_app.php index d36f1f405..3bd63c9f2 100644 --- a/tests/behat/behat_app.php +++ b/tests/behat/behat_app.php @@ -1020,7 +1020,7 @@ class behat_app extends behat_base { * @return object */ public function parse_element_locator(string $text): object { - preg_match('/^"((?:[^"]|\\")*?)"(?: "([^"]*?)")?(?: near "((?:[^"]|\\")*?)"(?: "([^"]*?)")?)?$/', $text, $matches); + preg_match('/^"((?:[^"]|\\")*?)"(?: "([^"]*?)")?(?: (near|within) "((?:[^"]|\\")*?)"(?: "([^"]*?)")?)?$/', $text, $matches); $locator = [ 'text' => str_replace('\\"', '"', $matches[1]), @@ -1028,9 +1028,9 @@ class behat_app extends behat_base { ]; if (!empty($matches[3])) { - $locator['near'] = (object) [ - 'text' => str_replace('\\"', '"', $matches[3]), - 'selector' => $matches[4] ?? null, + $locator[$matches[3]] = (object) [ + 'text' => str_replace('\\"', '"', $matches[4]), + 'selector' => $matches[5] ?? null, ]; }