Determining if an element corresponds to a By-object in Selenium using JavaScript

Is it possible to determine if a given WebElement matches a specific By object? The function should return either true or false.

For example:

foo(myWebElement, By.className("myClass myClass2"));
foo(myWebElement, By.css("div"));
foo(myWebElement, By.id("idA"));

I am currently working on a function that, when provided with a WebElement and a By object, will return the first parent element that matches the specified By object. I have made some progress, but the doesElementMatch function is still missing:

const findParentElement = async (current, by) => {
  try {
    do {
      element = await element.findElement(By.xpath("./.."));
    } while (!doesElementMatch(element, by));

    return element;
  } catch (e) {
    // reached top of DOM
    return null;
  }
}

If you have an alternate solution for this issue, I would appreciate your input as well.

Answer №1

Although I haven't found a direct answer to this question yet, I managed to solve my problem in a different way. Allow me to share my approach here, hoping it might be useful.

Misconception

Initially, I thought about finding all elements with a parent matching the specified By-selector, for example, a 'p' element nested within a div having class myClass.

My initial strategy involved selecting the child element first and then checking if it had a specific parent using the function mentioned in the question.

Effective Approach

A more efficient approach is to first identify all parent elements that match the By-selector and then iterate through each of them to locate the child element. For example:

let parents = await driver.findElement(By.css("div.myClass"));
for (let i = 0; i < parents.length; i++) {
  let child = await parents[i].findElement(By.css("p");
  if (child)
    return child;
}
  

An Alternative Strategy

Alternatively, you can achieve the same outcome using only CSS selectors by incorporating the '>' character. For instance, div.myClass>p. Refer to this resource for further details.

Answer №2

The solution provided by IceRevenge didn't quite meet my needs as I encountered issues with pre-filtering the top level elements due to a FireFox bug. In order to work around this bug, I had to retrieve the entire list of children elements instead of directly accessing the root shadowDom element.

To address this limitation, I came up with a workaround solution. While it may not be perfect, it serves its purpose adequately.

private <T> T handleNoSuchElementException(Supplier<T> supplier) {
    try {
        return supplier.get();
    } catch (NoSuchElementException ex) {
        return null;
    }
}

private WebElement findMatchingShadowDomChild(
        final List<WebElement> children,
        final Function<WebElement, Boolean> topLevelSearch,
        final Function<WebElement, WebElement> grandChildSearch,
        final String errorMessage
) {
    Optional<WebElement> child = children.stream()
            .filter(topLevelSearch::apply)
            .findFirst();
    if (child.isPresent()) {
        return child.get();
    }

    Optional<WebElement> grandChild = children.stream()
            .map(c -> handleNoSuchElementException(() -> grandChildSearch.apply(c)))
            .filter(Objects::nonNull)
            .findFirst();

    return grandChild.orElseThrow(() -> new NoSuchElementException(errorMessage));
}

This approach can be effectively utilized in various scenarios like this:

private List<WebElement> fetchShadowDomChildren(final WebElement rootElement) {
    return (List<WebElement>) ((JavascriptExecutor) browser.getWebDriver())
            .executeScript("return arguments[0].shadowRoot.children", rootElement);
}

private WebElement accessInnerShadowElementByTagName(final WebElement rootElement, 
                                                    final String tagName) {
    List<WebElement> children = fetchShadowDomChildren(rootElement);
    return findMatchingShadowDomChild(
            children,
            c -> StringUtils.equalsIgnoreCase(c.getTagName(), tagName),
            c -> c.findElement(By.tagName(tagName)),
            String.format("Could not locate an inner element with TagName=[%s]", tagName));
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Ways to detach event listener in Vue Component

One of my Vue2 components has a custom eventListener that I added in the mounted lifecycle hook. I am now trying to figure out the correct way to remove this listener when the component is destroyed. <template> <div> ... </di ...

Using Pug/Jade, be sure to call a function during each loop iteration

I'm attempting to develop a basic app, similar to a TO-DO LIST. I want to generate divs dynamically (with incremental ids) when a Button is clicked and then enter some text into an HTML input field. For example: <div id="item1"> <div id="ite ...

Instructions for selecting a checkbox using boolean values

According to the HTML specification, you should use the checked attribute in the <input type="checkbox"> tag to indicate that it is checked. However, I only have a boolean value of either true or false. Unfortunately, I am unable to manipulate the b ...

Guide on choosing the filename for downloads in Front-End applications

UPDATE: Creating a Blob from a base64 string in JavaScript I'm currently working on a feature where a user can click a button to download a file from its DataURL. However, due to Chrome restrictions on building <a> links, I encountered an err ...

What steps should I follow to enable a tooltip in this specific situation using qtip?

In my database, I have tables for venues, venue types, and map icons with the following relationships: A venue belongs to a venue type A venue type belongs to a map icon Each venue result is shown on the index page as a partial. Each partial ...

Manipulate text within a text area using jQuery

Good afternoon. How can I use JavaScript to update the text in a textarea input? I currently have some content in my textarea that includes a PublisherImprintName tag, and I want to remove it using jQuery. <PublisherInfo> <PublisherName>A ...

What is returned by jQuery when it fails to locate a specified class selector within the jQuery object?

Picture this scenario: var $letsTestA = $( '.lets-test-a' ), $letsTestB = $( '.lets-test-b' ); Then, consider this HTML: <div class="lets-test-a"></div> (I omitted .lets-test-b intentionally) Now, what happens if we ...

Utilizing properties from the same object based on certain conditions

Here's a perplexing query that's been on my mind lately. I have this object with all the styles I need to apply to an element in my React app. const LinkStyle = { textDecoration : 'none', color : 'rgba(58, 62, 65, 1)', ...

Choosing text input after adding a dynamic HTML row can be done by identifying the specific characteristics

I am currently working on a CodeIgniter project where I have implemented a purchase form that allows users to search for spare parts items using autocomplete in an input text field. The initial search functionality is working fine, but when I try to add a ...

Automated Chrome conceals text within CodeceptJS WebdriverIO test cases

Recently, I have just set up a new installation of Chrome Version 67.0.3396.87 and followed the steps outlined in to initialize a project and create a test scenario as shown below: Feature('Hidden Text'); Scenario('test something', ( ...

What is the correct way to add a library to webpack configuration?

Currently, I am working with Rails 6 and webpack in my project. I am interested in integrating the library jquery-textcomplete, but I am unsure about how to properly include it in the application.js file. Here are the steps I have taken so far: I instal ...

Can AJAX be exploited by hackers?

Today, I had a fascinating experience with my built systems. Someone managed to "hack" into everything and attributed the issue to AJAX. Here are his exact words: you are relying on AJAX when I have access to user's browser I have acc ...

Directive Template with Dynamic Fields (AngularJS)

I am interested in creating a custom directive that can bind to any object, allowing me to specify the field used in the template for display. Previously, I was limited to using {{item.Name}}, but now I want the flexibility to define the display field. Cu ...

Is there a way to modify the color scheme of my webpage while utilizing Bootstrap?

As I delve into programming with Bootstrap, I encountered an issue while attempting to change the background color of my body. Even though I used the following CSS code: body { background-color: #eba; width: 100%; height: 100%; } The color change ...

Having trouble locating the module 'monaco-editor/esm/vs/editor/editor.worker' while using create react app

I am currently facing some challenges running react-monaco-editor in my project despite following the documentation and making necessary adjustments to my files. Below is a snippet of my package.json file: { "name": "chatbot_compiler", "version": "0. ...

Mastering the art of completing a form using AJAX

I'm working on a checkbox that triggers AJAX to create a new log. It populates the necessary information and automatically clicks the "create" button. However, I'm facing an issue where the hour value is not changing. Any help on what I might be ...

Nested data selection in React

I have been experimenting with creating a nested select optgroup and attempted the following: const data = [ { sectorId: 5, sectorName: "Sector One", departments: [ { deptName: "Production", jobtitles: [ { ...

Obtaining a file that includes a selection of documents that correspond to a specific search criteria, along with the overall number of

Is it possible to query a collection to retrieve both a limited number of documents matching a query and the total amount of documents that match the query at the same time? This is the desired result: { result : [ { _id:null, ...

The efficiency of Ajax JSON timeouts needs improvement

Currently, I'm in the process of developing an interactive map with specific functionalities. Essentially, the user will click on a year (stored as var currentyear) and then select a country (its name stored as var thenameonly). Subsequently, an AJAX ...

Utilizing variable values in Google Charts with AngularJS

Hello everyone, I am attempting to display a chart using data obtained from an API. The output of the API is in the form of List<String> and not in JSON format. Here is the snippet of my JS file: google.load('visualization', '1', ...