AdnanEK
AdnanEK

Reputation: 198

How to find unique selectors for elements on pages with ExtJS for use with Selenium?

I am using Selenium with Firefox Webdriver to work with elements on a page that has unique CSS IDs (on every page load) but the IDs change every time so I am unable to use them to locate an element. This is because the page is a web application built with ExtJS.

I am trying to use Firebug to get the element information.

I need to find a unique xPath or selector so I can select each element individually with Selenium.

When I use Firebug to copy the xPath I get a value like this:

//*[@id="ext-gen1302"]

However, the next time the page is loaded it looks like this:

//*[@id="ext-gen1595"]

On that page every element has this ID format, so the CSS ID can not be used to find the element.

I want to get the xPath that is in terms of its position in the DOM, but Firebug will only return the ID xPath since it is unique for that instance of the page.

/html/body/div[4]/div[3]/div[4]/div/div/div/span[2]/span

How can I get Firebug (or another tool that would work with similar speed) to give me a unique selector that can be used to find the element with Selenium even after the ext-gen ID changes?

Upvotes: 4

Views: 17017

Answers (3)

Scott Izu
Scott Izu

Reputation: 2309

Although it is not desired in some cases as mentioned above, you can parse through the elements and generate xpath ids.

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

public class XPATHDriverWrapper {
Map xpathIDToWebElementMap = new LinkedHashMap();
Map webElementToXPATHIDMap = new LinkedHashMap();
public XPATHDriverWrapper(WebDriver driver){
    WebElement htmlElement = driver.findElement(By.xpath("/html"));
    iterateThroughChildren(htmlElement, "/html");
}

private void iterateThroughChildren(WebElement parentElement, String parentXPATH) {
    Map siblingCountMap = new LinkedHashMap();

    List childrenElements = parentElement.findElements(By.xpath(parentXPATH+"/*"));
    for(int i=0;i<childrenElements.size(); i++) {
        WebElement childElement = childrenElements.get(i);
        String childTag = childElement.getTagName();
        String childXPATH = constructXPATH(parentXPATH, siblingCountMap, childTag);
        xpathIDToWebElementMap.put(childXPATH, childElement);
        webElementToXPATHIDMap.put(childElement, childXPATH);
        iterateThroughChildren(childElement, childXPATH);
//          System.out.println("childXPATH:"+childXPATH);
    }
}

public WebElement findWebElementFromXPATHID(String xpathID) {
    return xpathIDToWebElementMap.get(xpathID);
}

public String findXPATHIDFromWebElement(WebElement webElement) {
    return webElementToXPATHIDMap.get(webElement);
}

private String constructXPATH(String parentXPATH,
        Map siblingCountMap, String childTag) {
    Integer count = siblingCountMap.get(childTag);
    if(count == null) {
        count = 1;
    } else {
        count = count + 1;
    }
    siblingCountMap.put(childTag, count);
    String childXPATH = parentXPATH + "/" + childTag + "[" + count + "]";
    return childXPATH;
}
}

Another wrapper to generate ids from Document is posted at: http://scottizu.wordpress.com/2014/05/12/generating-unique-ids-for-webelements-via-xpath/

Upvotes: 1

Yi Zeng
Yi Zeng

Reputation: 32855

Another victim of ExtJS UI automation testing, here are my tips specifically for testing ExtJS. (However, this won't answer the question described in your title)

Tip 1: Don't ever use unreadable XPath like /div[4]/div[3]/div[4]/div/div/div/span[2]/span. One tiny change of source code may lead to DOM structure change. This will cause huge maintenance costs.

Tip 2: Take advantage of meaningful auto-generated partial ids and class names.

For example, this ExtJS grid example: By.cssSelector(".x-grid-view .x-grid-table") would be handy. If there are multiple of grids, try index them or locate the identifiable ancestor, like By.cssSelector("#something-meaningful .x-grid-view .x-grid-table").

Tip 3: Create meaningful class names in the source code. ExtJS provides cls and tdCls for custom class names, so you can add cls:'testing-btn-cancel' in your source code, and get it by By.cssSelector(".testing-btn-cancel").

Tip3 is the best and the final one. If you don't have access the source code, talk to your manager, Selenium UI automation should really be a developer job for someone who can modify the source code, rather than a end-user-like tester.

Upvotes: 5

Nathan Merrill
Nathan Merrill

Reputation: 8386

I would recommend using CSS in this instance by doing By.cssSelector("span[id^='ext-gen'])

The above statement means "select a span element with an id that starts with ext-gen". (If it needs to be more specific, you can reply, and I'll see if I can help you).

Alternatively, if you want to use XPath, look at this answer: Xpath for selecting html id including random number

Upvotes: 1

Related Questions