How to use xPath in Selenium WebDriver to grab SVG elements?

60,410

Solution 1

May be you need to use the Actions with name attribute in Xpath. In your XPath use it -

"/*[name()='svg']/*[name()='SVG OBJECT']"  

Then try the following code snippet -

WebElement svgObj = driver.findElement(By.xpath(XPATH));
Actions actionBuilder = new Actions(driver);
actionBuilder.click(svgObj).build().perform();

Solution 2

To locate the red points i.e. the elements with attribute fill="#990000" and id attribute containing OpenLayers_Geometry_Point you can use either of the following Locator Strategies:

  • Using :

    //*[name()='svg']/*[name()='g']/*[name()='g']//*[name()='circle' and contains(@fill, '990000')][starts-with(@id, 'OpenLayers_Geometry_Point')]
    
  • Using :

    svg > g > g circle[fill$='990000'][id^='OpenLayers_Geometry_Point']
    

Ideally, you need to induce WebDriverWait for the visibilityOfAllElementsLocatedBy() and you can use either of the following Locator Strategies:

  • Using cssSelector:

    List<WebElement> vertices = new WebDriverWait(driver, 20).until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.cssSelector("svg > g > g circle[fill$='990000'][id^='OpenLayers_Geometry_Point']")));
    
  • Using xpath:

    List<WebElement> vertices = new WebDriverWait(driver, 20).until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.xpath("//*[name()='svg']/*[name()='g']/*[name()='g']//*[name()='circle' and contains(@fill, '990000')][starts-with(@id, 'OpenLayers_Geometry_Point')]")));
    

References

You can find a couple of relevant details discussions in:

Solution 3

Try @fill instead of fill and OpenLayers_Geometry_Point instead of OpenLayers.Geometry.Point.

Share:
60,410

Related videos on Youtube

joaorodr84
Author by

joaorodr84

BY DAY: Programmer at Ubiquity Technology BY NIGHT: Master Degree is Technologies and Web Systems FOR FUN: watching shows, programming, ... "Code is Art" J.R.

Updated on September 10, 2020

Comments

  • joaorodr84
    joaorodr84 almost 4 years

    I am testing an API, based on OpenLayers, with Selenium WebDriver (Java version).

    I want to test a functionality that uses OpenLayers.Control.ModifyFeature. I want to click on drawn features (SVG), drag then and check if they are present, visible or hidden.

    I have drawn a polygon, and I have selected it. See the image below:

    polygon_and_handles

    The HTML of these SVG elements is here:

    <svg id="OpenLayers_Layer_Vector_161_svgRoot" width="1235" height="495" viewBox="0 0 1235 495" style="display: block;">
        <g id="OpenLayers_Layer_Vector_161_root" transform="" style="visibility: visible;">
            <g id="OpenLayers_Layer_Vector_161_vroot">
                <path id="OpenLayers_Geometry_Polygon_200" d=" M 393.0000000000964,213.9999999999891 486.0000000003338,275.9999999997126 384.00000000036925,284.9999999994434 393.0000000000964,213.9999999999891 z" fill-rule="evenodd" fill="blue" fill-opacity="0.4" stroke="blue" stroke-opacity="1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none" pointer-events="visiblePainted" cursor="pointer" />
                <circle id="OpenLayers_Geometry_Point_619" cx="439.50000000021464" cy="244.99999999985084" r="6" fill="#009900" fill-opacity="0.5" stroke="#ee9900" stroke-opacity="1" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none" pointer-events="visiblePainted" cursor="inherit" />
                <circle id="OpenLayers_Geometry_Point_621" cx="435.00000000035106" cy="280.49999999958163" r="6" fill="#009900" fill-opacity="0.5" stroke="#ee9900" stroke-opacity="1" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none" pointer-events="visiblePainted" cursor="inherit" />
                <circle id="OpenLayers_Geometry_Point_623" cx="388.50000000023283" cy="249.4999999997126" r="6" fill="#009900" fill-opacity="0.5" stroke="#ee9900" stroke-opacity="1" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none" pointer-events="visiblePainted" cursor="inherit" />
                <circle id="OpenLayers_Geometry_Point_202" cx="393.0000000000964" cy="213.9999999999891" r="6" fill="#990000" fill-opacity="1" stroke="#ee9900" stroke-opacity="1" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none" pointer-events="visiblePainted" cursor="inherit" />
                <circle id="OpenLayers_Geometry_Point_203" cx="486.0000000003338" cy="275.9999999997126" r="6" fill="#990000" fill-opacity="1" stroke="#ee9900" stroke-opacity="1" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none" pointer-events="visiblePainted" cursor="inherit" />
                <circle id="OpenLayers_Geometry_Point_204" cx="384.00000000036925" cy="284.9999999994434" r="6" fill="#990000" fill-opacity="1" stroke="#ee9900" stroke-opacity="1" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="none" pointer-events="visiblePainted" cursor="inherit" />
            </g>
            <g id="OpenLayers_Layer_Vector_161_troot" />
        </g>
    </svg>
    

    Suppose I want to select the red points.

    I tried:

    String xpath = "//circle[contains(@id, 'OpenLayers_Geometry_Point') AND fill = '#990000']";
    List<WebElement> vertices = driver.findElements(By.xpath(xpath));
    

    But it always returns an empty list [].

    What am I doing wrong here? Could anybody help me, please?

    Thanks a lot.

    EDIT 1 - Function: verticesAreVisible

    Before the clicking actions, I want to get the elements and check if they are visible. I am using this function.

    public static boolean verticesAreVisible(WebDriver driver, String xpath) {
        List<WebElement> list = driver.findElements(By.xpath(xpath));
        if (list.isEmpty()) {
            return false;
        }
        boolean visible = true;
        for (int i = 0; i < list.size(); i++) {
            visible = visible && list.get(i).isDisplayed();
        }
        return !verticesAreNotVisible(driver) && visible;
    }
    

    EDIT 2 - Correct xPath

    // This solution from Razib is valid if the SVG is on the root node
    String xpath = "/*[name()='svg']/*[name()='circle']";
    // I changed it so that any descendant is valid "//"
    String xpath = "//*[name()='svg']//*[name()='circle']";
    // Since I wanted only the red vertices, I added this
    String xpath = "//*[name()='svg']//*[name()='circle' and @fill='#990000']";
    
    • Huibin Zhang
      Huibin Zhang about 5 years
      Hi I find this xpath is more easy to use, actions is not needed. //*[local-name()='svg']//*[local- name()='g' and and @fill='#990000'']
  • joaorodr84
    joaorodr84 almost 9 years
    Hi @peetya. Thanks for the 'OpenLayers_Geometry_Point' tip. I completely forgot to replace dots with underscores. Anyway, the '@fill' tip did not work. If I use this xPath '//*[contains(@id, 'OpenLayers_Geometry_Point')]', I get the 6 points. But I only want the 3 red.
  • joaorodr84
    joaorodr84 almost 9 years
    Hi @Razib. I struggled some time to get this working, but thanks to your tip, it works now! :) Thank you very much!!! See the edits on my post for more details on the solution.