Reputation: 1312
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:
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']";
Upvotes: 30
Views: 93399
Reputation: 193108
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 xpath:
//*[name()='svg']/*[name()='g']/*[name()='g']//*[name()='circle' and contains(@fill, '990000')][starts-with(@id, 'OpenLayers_Geometry_Point')]
Using css-selectors:
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')]")));
You can find a couple of relevant details discussions in:
Upvotes: 7
Reputation: 1
Tried with all sort of techniques to dive Inn but atlast I found out an easy way to handle this situation. When your desired svg doesnt have name and proper xpath changes dynamically then I used "cssSelector"
WebElement DesiredSvg = driver.findElement(By.cssSelector("#scrollable-auto-tabpanel-0 > div > div > form > div:nth-child(1) > div:nth-child(1) > div > div > div.jss10127 > div > div.jss11602 > svg:nth-child(3)"));
DesiredSvg.click();
Upvotes: 0
Reputation: 505
We also faced the similar issue in one of the screens where we have SVG implementation, I resolved using action class.
Action Class Package :
java.lang.Object
org.openqa.selenium.interactions.Actions
Sample Code :
WebElement svgObject= driver.findElement(By.xpath(XPATH));
Actions actionBuilderObj = new Actions(driver);
actionBuilderObj .click(svgObject).build().perform();
Upvotes: 0
Reputation: 353
To get just visibile elements you can use:
wait = new WebDriverWait(driver, 5);
wait.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(By.xpath("bla bla")));
Upvotes: -1
Reputation: 11163
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();
Upvotes: 48
Reputation: 3628
Try @fill
instead of fill
and OpenLayers_Geometry_Point
instead of OpenLayers.Geometry.Point
.
Upvotes: 2