How do I test a click on SVG objects using Selenium webdriver? - javascript
I am trying to write a code to check the functionality of a click on an SVG object - for example a US state on this URL
http://www.amcharts.com/svg-maps/?map=usa
This works, but is there a better way to do this? Something without physically moving the mouse?
robert = new Robot();
robert.mouseMove(x, y);
// full click once to get focus on the window
robert.mousePress(MouseEvent.BUTTON1_MASK);
robert.mouseRelease(MouseEvent.BUTTON1_MASK);
// then set the filter
robert.mousePress(MouseEvent.BUTTON1_MASK);
robert.mouseMove(x, y);
robert.mouseRelease(MouseEvent.BUTTON1_MASK);
In SVG, everything displayed is a block defined by coordinates. In your case, every state is a block as well and therefore has an XPath which you will be able to use in your Selenium code.
Use your browser developement tool to analyse the code of the page and find which block corresponds to the state you want to click on.
Here is the element corresponding to California.
<path cs="100,100" d="M371.75,174.28l-1.09,4.03l-1.09,4.03l-1.09,4.03l-1.09,4.03l-1.09,4.03l-1.09,4.03l-1.09,4.03l-1.09,4.02l-1.09,4.03l-1.09,4.02l-1.09,4.02l-1.09,4.02l-1.09,4.02l-1.09,4.02l-1.09,4.02l-1.09,4.02l2.98,4.56l3,4.56l3.02,4.56l3.05,4.55l3.07,4.54l3.09,4.54l3.11,4.53l3.13,4.53l2.41,3.75l2.42,3.75l2.44,3.75l2.45,3.74l2.46,3.74l2.48,3.74l2.49,3.73l2.5,3.73l3.46,5.32l3.48,5.31l3.51,5.3l3.53,5.3l3.56,5.29l3.59,5.28l3.62,5.28l3.9,5.63l-0.39,1.82l0.45,2.66l1.95,5.06l0.13,1.14l0.03,0.03l-0.12,1.14l0.89,1.45l2.36,2.25l0.44,0.95l-0.01,0.07l-0.13,0.6l-0.83,0.59l-3.5,1.82l-2.02,1.65l-2.31,2.87l-0.28,2.66l-0.52,2.14l-0.76,1.62l-0.95,1.44l-1.15,1.26l-1.07,0.67l-0.98,0.09l-0.72,1.83l-0.46,3.58l0.59,2.36l1.64,1.14l0.86,1.39l0.08,1.64l-0.47,1.5l-1.03,1.37l-1.39,0.58l-2.61,-0.31l0,0l-0.11,0.19l-2.16,-0.21l-5.38,-0.65l-5.39,-0.67l-5.38,-0.69l-5.38,-0.7l-5.38,-0.72l-5.38,-0.74l-5.38,-0.76l-5.38,-0.77l-0.01,-0.15l0.44,-2.41l-0.65,-1.04l-1.22,0.26l0.24,-3.21l0.62,-1.39l0.21,-1.45l-0.19,-3.74l-1.69,-4.89l-4.56,-6.68l-2.53,-2.49l-1.78,-2.8l-1.32,-0.98l-1.81,-0.63l-0.79,0.87l-1.93,-1.21l0.94,-2.39l-1.17,-3.95l-1.57,-0.8l-4.25,-0.84l-5.11,-3.34l-1.36,-1.55l-0.04,-2.16l-2.15,-2.43l-2.98,-2.62l-2.02,-0.11l-2.43,-0.93l-3.22,-2.19l-2.03,-0.72l-4.14,-0.74l-1.44,-0.67l-0.97,-1.94l-1.29,-1.19l0.85,-1.82l0.29,-1.78l0.6,-1.28l0.15,-3.13l1.28,-2.58l-0.18,-1.11l-0.63,-0.99l-2.33,-1.86l-0.09,-1.53l0.98,-1.82l-0.33,-1.47l-1.82,-1.8l-1.25,-3.28l-2.13,-2.21l-0.34,-2.78l-1.13,-1.98l-0.15,-1.51l-2.06,-5.84l-2.58,-4.85l0.07,-2.34l0.73,-3.02l1.97,-1.4l1.24,-1.37l0.35,-1.49l0.09,-1.14l-0.71,-2.24l-4.53,-2.54L303,265.5l0.82,-3.6l-0.46,-4.07l0.68,-2.35l0.53,-2.61l1.33,-0.21l0.98,0.51l-0.41,0.98l-0.19,1.92l0.81,1.73l0.99,0.94l0.67,1.64l0.68,0.64l0.8,0.34l-0.19,-0.98l-0.31,-0.68l-0.05,-1.93l-0.42,-2.57l-0.88,-1.61l0.04,-2.45l-0.38,-0.69l-0.09,-0.94l1.5,-0.64l1.85,-0.22l2.25,0.46l6.14,2.16l1.5,-0.19l1.04,0.51l0.83,0.16l-1.52,-1.09l-1.01,-0.08l-1.08,-0.45l-2.26,-0.53l-0.83,-0.52l-0.78,-1l-0.63,-0.26l-2.42,0.63l-0.87,-0.42l-1.77,-1.99l-0.89,-0.47l-1.75,0.31l-1.18,3.25l-0.27,2.6l-0.99,-0.02l-0.76,-1.33l-1.45,-1.09l-1.06,-1.33l-1.37,-2.29l-0.8,-0.93l-1.56,1.08l0.16,-0.66l1.06,-1.48l0.69,-2.82l1.02,2.73l-0.05,-1.72l-0.79,-2.11l-0.82,-0.9l-0.31,-3.45l-2.24,-2.71l-1.33,-3.67l-3.05,-6.35l1.05,-4.41l0.06,-5.98l1.68,-2.88l0.6,-2.23l0.24,-3.58l-0.27,-2.07l-2.08,-6.11l-2.43,-4.46l0.29,-2.69l0.58,-2.62l1.49,-2.02l1.42,-2.17l0.68,-0.48l0.1,0.32l-0.3,0.47l0.44,0.31l0.52,-0.99l0.47,-0.45l-0.5,-0.24l0.16,-0.32l0.52,-0.56l2.08,-2.78l1.15,-3.98l2.69,-4.47l0.46,-1.61l0.37,-3.67l-0.06,-2.29l-0.82,-1.88l1.25,-1.95l0.61,-2.05l-0.15,-0.43l4.31,1.38l4.17,1.32l4.18,1.31l4.18,1.3l4.19,1.28l4.19,1.27l4.19,1.26l4.2,1.25l4.2,1.23l4.2,1.22l4.21,1.21l4.21,1.2l4.21,1.19l4.22,1.17l4.22,1.16L371.75,174.28zM327.88,344.91l3.35,2.08l2.11,0l0.21,0.63l-0.36,0.4l-4.66,-0.35l-1.21,-0.95l0.09,-0.83l-0.25,-0.89L327.88,344.91zM319.97,344.03l-0.97,-0.2l-1.4,-0.63l0.65,-0.36l0.91,-0.14l0.18,0.34L319.97,344.03zM324.06,347.59l-1.34,-0.04l-0.88,-0.55l-0.96,-2.47l3.3,0.61l1.15,1.27l0.12,0.3L324.06,347.59zM351.66,367.08l0.52,1.82l-1.27,-0.53l-1.4,-0.26l-0.2,-0.97l-0.11,-1.31l-0.2,-0.38l-0.92,-0.35l-0.04,-0.13l0.04,-0.61l0.34,-0.21l2.62,2.09L351.66,367.08zM330.94,365.63l-0.82,-0.17l-1.06,-0.49l-0.26,-1.31l0.93,0.16l0.8,0.38l0.43,1.09L330.94,365.63zM348.58,379.15l-1.11,-0.07l-1.07,-0.74l-0.49,-2.35l-0.7,-1.92l0.72,-0.31l0.51,1.8l1.67,2.96L348.58,379.15z" fill="#FFFFFF" fill-opacity="0.8" stroke="#000000" stroke-opacity="0.5" stroke-width="0.5"></path>
When you have found it, right click on the element and "Copy XPath".
//*[#id="chartdiv"]/div/div[1]/svg/g[7]/g/g[1]/path[47]
However, do it two more times and you will see that the last node is never the same.
//*[#id="chartdiv"]/div/div[1]/svg/g[7]/g/g[1]/path[5]
//*[#id="chartdiv"]/div/div[1]/svg/g[7]/g/g[1]/path[34]
So we would like to find the <path> in //*[#id="chartdiv"]/div/div[1]/svg/g[7]/g/g[1]/ which the d attribute value starts with M371.75,174.28l. It should be something like:
//*[#id="chartdiv"]/div/div[1]/svg/g[7]/g/g[1]/path[starts-with(#d, "M371.75,174.28l")]
You shall have to repeat all these steps by yourself, because we might not have the same generated SVG.
But because the SVG is using another namespace:
/g becomes /*[name()="g"]
/svg becomes /*[name()="svg"]
/path becomes /*[name()="path"]
Which gives the following XPath:
//*[#id="chartdiv"]/div/div[1]/*[name()="svg"]/*[name()="g"][7]/*[name()="g"]/*[name()="g"][1]/*[name()="path"][starts-with(#d, "M371.75,174.28l")]
In Selenium:
webDriver.findElement(
By.xpath(
'//*[#id="chartdiv"]/div/div[1]/*[name()="svg"]/*[name()="g"][7]/*[name()="g"]/*[name()="g"][1]/*[name()="path"][starts-with(#d, "M371.75,174.28l")]'
)
).click();
Related
How to inspect element of Zebkit UI
As part of automation testing, we want to inspect the element in a website which is made of using the Zebkit UI framework. We are unable to find the element using zebra.ui examples can be found here Can someone help us on inspecting the element
Zebkit UI components are rendered on HTML5 Canvas. So they are not part of browser DOM tree what can be a problem for a test tool that expects DOM as an input. But it doesn't mean you cannot go over zebkit UI stuff to perform test cases. First of all keep in mind zebkit components are a hierarchy/tree like DOM is. Every rendered on a canvas zebkit UI component has a related JS instance of appropriate class. There are number of API methods you can use to travel over UI components tree. These methods expect path (XPath-like) since path (from my point of view) is less "encrypted" way than CSS selector. The API methods you probably need: byPath(path [,callback]) - traversing UI components tree by the given path var zcanvas = new zebkit.ui.zCanvas(); ... // travel over all UI components in tree zcanvas.byPath("//*", function(comp) { // perform test cases here ... }); properties([path,] properties) applies the specified properties set to component or number of components requested by the given path var zcanvas = new zebkit.ui.zCanvas(); ... // set color property to black value for all labels zcanvas.properties("//zebkit.ui.Label", { color: "black" }); on([eventName], [path], handler) add listener method(s) for the given event (or all events) of the given component or components identified with the path: var zcanvas = new zebkit.ui.zCanvas(); ... // register event listener for all found buttons zcanvas.on("//zebkit.ui.Button", function (src) { // handle button press event here ... }); fire([eventName,] [path,] [argument]) fire the given event to the given component or to components identified with the path: var zcanvas = new zebkit.ui.zCanvas(); ... // fire button pressed event to button with id equals "testButton" zcanvas.fire("//[#id='testButton']"); ... // or the same with a shortcut zcanvas.fire("#testButton");
I'm not sure I understand correctly your issue, but assume you cannot click right button on canvas to open context menu and select "Inspect element" option. You can press F12 in browser switch to "Elements"/"HTML" tab in search field (CTRL + F) print "canvas"/"<canvas" and press Enter Continue pressing Enter until required canvas found (current element should be highlighted)
These controls are implemented using an HTML5 CANVAS tag. Selenium cannot see "inside" this tag because it doesn't contain HTML. It's like an app inside the page. From the page you linked, it looks like you should be able to use JS to access elements inside the control. When I've done things with CANVAS tags in the past, I generally find JS that does or returns what I want and then wrap that code in a function that I can call. It will work but you will likely have to do some research on Zebkit to find out what JS you will need to validate, etc. all the different things you will want to validate... and it may end up that you won't be able to validate some things.
"mouse:over" not firing and "mouse:down" returning undefined target using fabricJS
Here is my issue; I am trying to display "markers" and on mouse over/out/click give some actions. The problem is that no event gets fired on over at all and when on click (down) in the console I get some feedback, but not of the element per say (target == undefined). My different shapes are groups in a group called "marker". And I group everything with the following: marker.add(plateShape, plateLabel, line, indicator); When using marker.addWithUpdate(plateShape, plateLabel, line, indicator); I get some feedback (over being finicky at best) but the layout get completely messed up. You can comment/uncomment Line 86 to check the behaviour in the following fiddle. https://jsfiddle.net/u7x7az1j/5/ Thank you for your help! :)
The problem comes from your use of add. Replace add with addWithUpdate and be aware that addWithUpdate takes one parameter at time. marker.addWithUpdate(plateShape); marker.addWithUpdate(plateLabel); marker.addWithUpdate(line); marker.addWithUpdate(indicator); i tried on your fiddle and it works. Consider replacing rect + triangle + line with a simple parametric fabric.Path you can easily build.
hide one of the axes of a parallel-coordinates plot
i'm representing some data with the parallel coordinates library (based on d3.js) ( https://github.com/syntagmatic/parallel-coordinates#parallel-coordinates ) main functional part of the code is the following: var parcoords = d3.parcoords()("#example") //#example is the div for the drowing .data(eingabe) // eingabe is the var, which contains the data .render() .reorderable() .shadows() .brushMode("1D-axes") so far it works fine :) but now i want to hide one specific axis of the parallel coordinates; i only don't want to show it. I want that the axis is completeley removed, so that it takes no place and the lines don't be affected of the values of this dimension, but i don´t want to do that by manipulating the data. i only want to manipulate the plot (thats the reason i called it hide). i've searched on the api description but didn't find something. I searched in the internet but didn't find anything, too. But i think i remember that i've seen a peace of code, where the first axis was hidden, but i can't find it again. Can anybody tell me, how i can hide an axis or where i can find the solution? thank you, greetings Jones
You're missing a .hideAxis(array) method call, where the array is a list of keys of the exact column names in your dataset that you want to hide. This method does exactly what you said you wanted: "...hide one specific axis of the parallel coordinates; i only don't want to show it." "I want that the axis is completley [sic] removed, so that it takes no place" "the lines dont [sic] be affected of the values of this dimension, but i don´t want to do that by manupulating [sic] the data. i only want to manipulate the plot." To implement this method, your code would need to be var parcoords = d3.parcoords()("#example") //#example is the div for the drowing .data(eingabe) // eingabe is the var, which contains the data .hideAxis(["col1", "col2"]) .render() .reorderable() .shadows() .brushMode("1D-axes") Note: .hideAxis can take in a blank array if you don't want to hide any axis, i.e. .hideAxis([]) You can implement this method in some sort of update/redraw function if you want to allow the user to specify which axis(es) to remove; else, removing it completely via the DOM is impossible. This, performing the axis(es) hide via code, is your only option to implement what you want. To see this method in action, take a look at the API documentation, specifically, their "example1" graph and code at http://syntagmatic.github.io/parallel-coordinates/index.html#example1
Assuming you just want to hide the axes (i.e. make it invisible) and not remove it completely d3.selectAll("#example0 > svg > g > g.dimension:nth-child(3)").attr("opacity", "0"); Assuming example0 is the id of the wrapper around your svg element. You could also do the same thing directly on your svg element if you have it. The above would still leave you able to interact with the filter of the hidden axis. To make it non-interactable, use .attr("display", "none"); Don't have a fiddle, but you should be able to copy paste the above line and run it from the console at http://syntagmatic.github.io/parallel-coordinates/ and see the effect on the chart in the Bundling section. Here is the effect - before and after respectively
d3 get rect attribute by id
I just stuck on a problem when trying to select an attribute of a rect object within a svg. This is the rectangle I want to get a value from: <rect id="2" x="13.761467889908257" y="50" width="49.31192660550459" height="50" fill="rgb(43,0,0)"></rect> in order to calculate the x-position of another rectangle I need the x-value. I tried some different ideas like: svg.selectAll("rect") .select("id",2); // 1st version .select("id","2"); // 2nd version .select("#2"); // found in another d3 tutorial -> not working for me and a lot more. Is there any way to do this? // If you need more of my code just write it in a comment I wasnt sure how much to write in order to NOT write too much
According to HTML 4, id's can't start with a number. HTML 5 did change that, but CSS and d3 still don't really support that. So if it's not necessary, it may be easier to just change the id value to not start with a number. This problem is why the #2 selector isn't working, I think. If you did want to keep id = 2, you can probably use the attribute selector to do it in one line, like this: svg.select("rect[id='2']") You can read what Mike Bostock has to say about id's starting with numbers here. Then, once you have your selection, just do .attr("x").
Try calling directly d3.select('#2');
How to add SVGPan support to raphael
i'm using a library that creates svg content using raphael library. I would now like to add zooming functionality. I found following library: http://www.cyberz.org/blog/2009/12/08/svgpan-a-javascript-svg-panzoomdrag-library/ This is how the svg part looks: <svg xmlns="http://www.w3.org/2000/svg" version="1.1"> //...the image The issue is that this library expects a script tag and a g tag surrounding the whole image. It should look like this after manipulation: <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"> <script xlink:href="SVGPan.js"/> <g id="viewport" transform="translate(200,200)"> ...//the image </g> I would need to add the script tag but the real problem is to put a g element with id="viewport" around the whole rest of the image. How can i do that with JQuery or plan JavaScript? EDIT: I got a bit ahead of myself. It turns out that adding the script tag does not seem to work: var svg = $('svg'); svg.attr('xmlns:xlink', 'http://www.w3.org/1999/xlink'); // else error in firefox with links var scriptElement = document.createElementNS('http://www.w3.org/2000/svg','script'); scriptElement.setAttribute('xlink:href','js/SVGPan.js'); svg.prepend(scriptElement); I don't get an error and the script runs completely but the tag is not added. I have verified that $('svg') selects the desired element and I can do stuff with it except prepending the script tag for whatever resaon i do not understand.
I don't think Raphael allows you to manipulate the SVG element but you could try a similar Raphael-specific library. This one may provide what you are looking for: https://github.com/semiaddict/raphael-zpd Edit: Actually it is possible to manipulate SVG elements: Raphael exposes a Node property. So if you could get hold of the Raphael paper object you could try something like: paper.node.setAttribute("id", "viewport")
Thanks Clafou for pointing me to raphael-zpd. This lead me to the solution. The issue is that I was able to get the raphael object but zpd only works if nothing has been drawn yet which is not the case. I'm using http://www.jsphylosvg.com which makes use of raphael. The solution is to edit the javascript file jsphylosvg.js directly: search for this.svg = Raphael(sDivId, this.canvasSize[0], this.canvasSize[1]); (line 1217 in my case) add: this.svg.ZPD({ zoom: true, pan: true, drag: true }); directly below that line save jsphylosvg.js done. It works! (Of course you need to have a reference to raphael-zpd in your web page)
Have you tried getting it via a jQuery selector? You could for example try setting the id on the child g element by doing this (after your library has constructed the SVG element): $('svg > g').attr('id', 'viewport');