Question

I want to write my own locator to access the elements. WebDriver’s API offers currently eight locators allowing to retrieve elements by id, name attribute, tag name, complete or partial link text, XPath, class name, and css selector. However those default locators not enough for me now because I have to access the elements through a new attribute. Let me give an xample so that you can understand what I really want here.

Example: Choose your username:

Now I want to write a code so that I can access the username button using the myLocator locator like:

*driver.findElement(By.myLocator("username")).*

It would be very helpful if anybody can give us some good idea how could I rewrite the BY class to add my own locator.

Thank you in advance for your help.

Était-ce utile?

La solution 2

You would need to subclass the By class and provide an implementation for findElement and findElements methods, since this is where the 'meat' of the actual element finding occurs.

You should then be able to use it with the normal driver.FindElement then.

Autres conseils

using c#

using OpenQA.Selenium;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

public class ImageBy : By
{
    public ImageBy(string imageByString)
    {
        FindElementMethod = (ISearchContext context) =>
        {
            IWebElement mockElement = context.FindElement(By.XPath("//img[@src='" + imageByString + "']"));
            return mockElement;
        };

        FindElementsMethod = (ISearchContext context) =>
        {
            ReadOnlyCollection<IWebElement> mockElements = context.FindElements(By.XPath("//img[@src='" + imageByString + "']"));
            return mockElements;
        };
    }
}

and the usage would be as follows

[FindsBy(How = How.Custom, Using = @"/path/to/img", CustomFinderType = typeof(ImageBy) )]
private IWebElement MenuStartButton = null;

Using Java

import java.util.List;

import org.openqa.selenium.By;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebElement;

public class ByImageSrc extends By 
{
    private final String imageByString;
    public ByImageSrc(String imageByString)
    {
        this.imageByString = imageByString;
    }

    @Override
    public List<WebElement> findElements(SearchContext context) 
    {
         List<WebElement> mockElements = context.findElements(By.xpath("//img[@src='" + imageByString + "']"));
         return mockElements;
    }
}

usage :

WebElement element = driver.findElement(new ByImageSrc("/path/to/image"));

I know this doesn't really answer your question, but why not just use Xpath? It seems like it would be a lot of extra work to build out a new locator instead of just crawling the DOM.

For example: driver.findElement(By.Xpath("//div[@username='YourUsername']")

I could give a better example with some more detail about the attribute and page you're working with.

Much like Michiel answered I feel you can achieve what you want with what Selenium has already provided. If it is a maintenance overhead you are wanting to avoid due to developers altering ids and element names you can create a separate file that keeps track of the elements you need to locate.

//EG. (not Java I know :))
string usernameXPath = "//div[@username='YourUsername']";

Then if there is a change you can maintain this. You could even go a step further and implement a class for each 'other type' of element and just put an XPath around it in the constructor. XPath is very flexible as it offers functions such as 'contains' and 'parent::div'. Maybe look at the W3schools page for some more XPath reading.

EDIT: Also worth noting is that the C# bindings latest release says the following:

.Net: Introduces the Updating the CustomFinderType property to the .NET FindsByAttribute. This allows use of custom By subclasses in the PageFactory. The custom finder must be a subclass of By, and it must expose a public constructor that takes a string argument.

Hopefull this is in the next Java release for you too :)

You can do something like this:

private final String myLocator = "//*[contains(@id,'username') and contains (@type,'text') and contains (text(),'exactly what I want')]";

So you can write up the locator however you wish, depending on the attributes that what you're mapping has, no need to have 5 rows for a click or select.

Also, you can use a wildcard in that locator and just replace whatever you want to use as a parameter with "%s", like this:

private final String myLocator = "//*[contains(@id,'%s') and contains (@type,'text') and contains (text(),'exactly what I want')]";

Then have a dynamic element created from that like:

private WebElement usernameSelectElement(String text) {
    return driver.findElement(By.xpath(String.format(myLocator, text)));
}

And the usage would be something like:

public void clickMyElement(text){
usernameSelectElement(text).click();
}

For the example that you had, it's just overly complicated in my view.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top