Improve location of elements and containers
This commit is contained in:
parent
00f7cda5bf
commit
77c5418f0c
@ -224,13 +224,13 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds elements within a given container.
|
* Finds elements within a given container with exact info.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} container Parent element to search the element within
|
* @param {HTMLElement} container Parent element to search the element within
|
||||||
* @param {string} text Text to look for
|
* @param {string} text Text to look for
|
||||||
* @return {HTMLElement} Elements containing the given text
|
* @return {Array} Elements containing the given text with exact boolean.
|
||||||
*/
|
*/
|
||||||
const findElementsBasedOnTextWithin = (container, text) => {
|
const findElementsBasedOnTextWithinWithExact = (container, text) => {
|
||||||
const elements = [];
|
const elements = [];
|
||||||
const attributesSelector = `[aria-label*="${text}"], a[title*="${text}"], img[alt*="${text}"]`;
|
const attributesSelector = `[aria-label*="${text}"], a[title*="${text}"], img[alt*="${text}"]`;
|
||||||
|
|
||||||
@ -238,7 +238,8 @@
|
|||||||
if (!isElementVisible(foundByAttributes, container))
|
if (!isElementVisible(foundByAttributes, container))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
elements.push(foundByAttributes);
|
const exact = foundByAttributes.title == text || foundByAttributes.alt == text || foundByAttributes.ariaLabel == text;
|
||||||
|
elements.push({ element: foundByAttributes, exact: exact });
|
||||||
}
|
}
|
||||||
|
|
||||||
const treeWalker = document.createTreeWalker(
|
const treeWalker = document.createTreeWalker(
|
||||||
@ -269,7 +270,7 @@
|
|||||||
while (currentNode = treeWalker.nextNode()) {
|
while (currentNode = treeWalker.nextNode()) {
|
||||||
if (currentNode instanceof Text) {
|
if (currentNode instanceof Text) {
|
||||||
if (currentNode.textContent.includes(text)) {
|
if (currentNode.textContent.includes(text)) {
|
||||||
elements.push(currentNode.parentElement);
|
elements.push({ element: currentNode.parentElement, exact: currentNode.textContent.trim() == text });
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
@ -278,7 +279,7 @@
|
|||||||
const labelledBy = currentNode.getAttribute('aria-labelledby');
|
const labelledBy = currentNode.getAttribute('aria-labelledby');
|
||||||
const labelElement = labelledBy && container.querySelector(`#${labelledBy}`);
|
const labelElement = labelledBy && container.querySelector(`#${labelledBy}`);
|
||||||
if (labelElement && labelElement.innerText && labelElement.innerText.includes(text)) {
|
if (labelElement && labelElement.innerText && labelElement.innerText.includes(text)) {
|
||||||
elements.push(currentNode);
|
elements.push({ element: currentNode, exact: labelElement.innerText.trim() == text });
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -296,12 +297,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (childNode.matches(attributesSelector)) {
|
if (childNode.matches(attributesSelector)) {
|
||||||
elements.push(childNode);
|
const exact = childNode.title == text || childNode.alt == text || childNode.ariaLabel == text;
|
||||||
|
elements.push({ element: childNode, exact: exact});
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
elements.push(...findElementsBasedOnTextWithin(childNode, text));
|
elements.push(...findElementsBasedOnTextWithinWithExact(childNode, text));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -309,6 +311,24 @@
|
|||||||
return elements;
|
return elements;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds elements within a given container.
|
||||||
|
*
|
||||||
|
* @param {HTMLElement} container Parent element to search the element within.
|
||||||
|
* @param {string} text Text to look for.
|
||||||
|
* @return {HTMLElement[]} Elements containing the given text.
|
||||||
|
*/
|
||||||
|
const findElementsBasedOnTextWithin = (container, text) => {
|
||||||
|
const elements = findElementsBasedOnTextWithinWithExact(container, text);
|
||||||
|
|
||||||
|
// Give more relevance to exact matches.
|
||||||
|
elements.sort((a, b) => {
|
||||||
|
return b.exact - a.exact;
|
||||||
|
});
|
||||||
|
|
||||||
|
return elements.map(element => element.element);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a list of elements, get the top ancestors among all of them.
|
* Given a list of elements, get the top ancestors among all of them.
|
||||||
*
|
*
|
||||||
@ -365,19 +385,78 @@
|
|||||||
return getClosestMatching(element.parentElement, selector, container);
|
return getClosestMatching(element.parentElement, selector, container);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to find top container element.
|
||||||
|
*
|
||||||
|
* @param {string} containerName Whether to search inside the a container name.
|
||||||
|
* @return {HTMLElement} Found top container element.
|
||||||
|
*/
|
||||||
|
const getCurrentTopContainerElement = function (containerName) {
|
||||||
|
let topContainer;
|
||||||
|
let containers;
|
||||||
|
|
||||||
|
switch (containerName) {
|
||||||
|
case 'html':
|
||||||
|
containers = document.querySelectorAll('html');
|
||||||
|
break;
|
||||||
|
case 'toast':
|
||||||
|
containers = document.querySelectorAll('ion-app ion-toast.hydrated');
|
||||||
|
containers = Array.from(containers).map(container => container.shadowRoot.querySelector('.toast-container'));
|
||||||
|
break;
|
||||||
|
case 'alert':
|
||||||
|
containers = document.querySelectorAll('ion-app ion-alert.hydrated');
|
||||||
|
break;
|
||||||
|
case 'action-sheet':
|
||||||
|
containers = document.querySelectorAll('ion-app ion-action-sheet.hydrated');
|
||||||
|
break;
|
||||||
|
case 'modal':
|
||||||
|
containers = document.querySelectorAll('ion-app ion-modal.hydrated');
|
||||||
|
break;
|
||||||
|
case 'popover':
|
||||||
|
containers = document.querySelectorAll('ion-app ion-popover.hydrated');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Other containerName or not implemented.
|
||||||
|
const containerSelector = 'ion-alert, ion-popover, ion-action-sheet, ion-modal, page-core-mainmenu, ion-app';
|
||||||
|
containers = document.querySelectorAll(containerSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (containers.length > 0) {
|
||||||
|
// Get the one with more zIndex.
|
||||||
|
topContainer = Array.from(containers).reduce((a, b) => {
|
||||||
|
return getComputedStyle(a).zIndex > getComputedStyle(b).zIndex ? a : b;
|
||||||
|
}, containers[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (containerName == 'page' || containerName == 'split-view content') {
|
||||||
|
// Find non hidden pages inside the container.
|
||||||
|
let pageContainers = topContainer.querySelectorAll('.ion-page:not(.ion-page-hidden)');
|
||||||
|
pageContainers = Array.from(pageContainers).filter((page) => {
|
||||||
|
return !page.closest('.ion-page.ion-page-hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (pageContainers.length > 0) {
|
||||||
|
// Get the more general one to avoid failing.
|
||||||
|
topContainer = pageContainers[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (containerName == 'split-view content') {
|
||||||
|
topContainer = topContainer.querySelector('core-split-view ion-router-outlet');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return topContainer;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to find elements based on their text or Aria label.
|
* Function to find elements based on their text or Aria label.
|
||||||
*
|
*
|
||||||
* @param {object} locator Element locator.
|
* @param {object} locator Element locator.
|
||||||
* @param {boolean} insideSplitView Whether to search only inside the split view contents.
|
* @param {string} containerName Whether to search only inside a specific container.
|
||||||
* @return {HTMLElement} Found elements
|
* @return {HTMLElement} Found elements
|
||||||
*/
|
*/
|
||||||
const findElementsBasedOnText = function(locator, insideSplitView) {
|
const findElementsBasedOnText = function(locator, containerName) {
|
||||||
let topContainer = document.querySelector('ion-alert, ion-popover, ion-action-sheet, core-ion-tab.show-tab ion-page.show-page, ion-page.show-page, html');
|
let topContainer = getCurrentTopContainerElement(containerName);
|
||||||
|
|
||||||
if (insideSplitView) {
|
|
||||||
topContainer = topContainer.querySelector('core-split-view ion-router-outlet');
|
|
||||||
}
|
|
||||||
|
|
||||||
let container = topContainer;
|
let container = topContainer;
|
||||||
|
|
||||||
@ -544,20 +623,20 @@
|
|||||||
* Function to find an arbitrary element based on its text or aria label.
|
* Function to find an arbitrary element based on its text or aria label.
|
||||||
*
|
*
|
||||||
* @param {object} locator Element locator.
|
* @param {object} locator Element locator.
|
||||||
* @param {boolean} insideSplitView Whether to search only inside the split view contents.
|
* @param {string} containerName Whether to search only inside a specific container content.
|
||||||
* @return {string} OK if successful, or ERROR: followed by message
|
* @return {string} OK if successful, or ERROR: followed by message
|
||||||
*/
|
*/
|
||||||
const behatFind = function(locator, insideSplitView) {
|
const behatFind = function(locator, containerName) {
|
||||||
log('Action - Find', { locator, insideSplitView });
|
log('Action - Find', { locator, containerName });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const element = findElementsBasedOnText(locator, insideSplitView)[0];
|
const element = findElementsBasedOnText(locator, containerName)[0];
|
||||||
|
|
||||||
if (!element) {
|
if (!element) {
|
||||||
return 'ERROR: No matches for text';
|
return 'ERROR: No matches for text';
|
||||||
}
|
}
|
||||||
|
|
||||||
log('Action - Found', { locator, insideSplitView, element });
|
log('Action - Found', { locator, containerName, element });
|
||||||
return 'OK';
|
return 'OK';
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return 'ERROR: ' + error.message;
|
return 'ERROR: ' + error.message;
|
||||||
|
@ -154,18 +154,22 @@ class behat_app extends behat_base {
|
|||||||
/**
|
/**
|
||||||
* Finds elements in the app.
|
* Finds elements in the app.
|
||||||
*
|
*
|
||||||
* @Then /^I should( not)? find (".+")( inside the split-view content)? in the app$/
|
* @Then /^I should( not)? find (".+")( inside the .+)? in the app$/
|
||||||
* @param bool $not
|
* @param bool $not
|
||||||
* @param string $locator
|
* @param string $locator
|
||||||
* @param bool $insidesplitview
|
* @param string $containerName
|
||||||
*/
|
*/
|
||||||
public function i_find_in_the_app(bool $not, string $locator, bool $insidesplitview = false) {
|
public function i_find_in_the_app(bool $not, string $locator, string $containerName = '') {
|
||||||
$locator = $this->parse_element_locator($locator);
|
$locator = $this->parse_element_locator($locator);
|
||||||
$locatorjson = json_encode($locator);
|
$locatorjson = json_encode($locator);
|
||||||
$insidesplitviewjson = json_encode($insidesplitview);
|
if (!empty($containerName)) {
|
||||||
|
preg_match('/^ inside the (.+)$/', $containerName, $matches);
|
||||||
|
$containerName = $matches[1];
|
||||||
|
}
|
||||||
|
$containerName = json_encode($containerName);
|
||||||
|
|
||||||
$this->spin(function() use ($not, $locatorjson, $insidesplitviewjson) {
|
$this->spin(function() use ($not, $locatorjson, $containerName) {
|
||||||
$result = $this->evaluate_script("return window.behat.find($locatorjson, $insidesplitviewjson);");
|
$result = $this->evaluate_script("return window.behat.find($locatorjson, $containerName);");
|
||||||
|
|
||||||
if ($not && $result === 'OK') {
|
if ($not && $result === 'OK') {
|
||||||
throw new DriverException('Error, found an item that should not be found');
|
throw new DriverException('Error, found an item that should not be found');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user