i'm writing Ruby in Watir webdriver and i would like to test highcharts accuracy of data presented in comparison of a CSV file (which i already read). How can i read the highchart data from the website?
A highchart graph generated with many data dots as you mouseover the dot, a data will be shown in a box.
I cannot locate the element using watir webdriver as what i see from source that each dot are path tags.
I am thinking maybe automate the cursor to move to a x y location but not sure how to do that. Any helps? thank you
Assumptions
Since we only have an image of your chart, rather than the specific html, I will assume the graph is similar in design to the "Basic Line" highcharts demo. The below will hopefully work conceptually for your graph (ie the approach will probably work, but it will likely require some tweaks).
Get Path Elements
In the graph, there is a path element to draw each point, as well as a path element to draw the line.
<g class="highcharts-markers" visibility="visible" zIndex="0.1" transform="translate(62,55) scale(1 1)" clip-path="none">
<path fill="#2f7ed8" d="M 638.25 182.5 C 643.578 182.5 643.578 190.5 638.25 190.5 C 632.922 190.5 632.922 182.5 638.25 182.5 Z"></path>
The g and path elements are not directly supported by watir, so you will need to use the generic element type with a css or xpath locator.
#Get the first line (as there are 4 in the demo)
series1 = browser.element(:css => 'g.highcharts-markers')
#Get the data points (the last point is ignored since it is the line)
all_path_elements = series1.elements(:css => 'path')
points = all_path_elements[0..-2]
Simulate MouseOver
You can simulate mousing over an element using the hover method:
browser.element(:css => 'g.highcharts-markers path').hover
Read the Popup
The html of the popup looks like:
<g opacity="0" transform="translate(146,222)" visibility="hidden" style="cursor:default;padding:0;white-space:nowrap;" zIndex="8" class="highcharts-tooltip">
<text zIndex="1" style="font-family:"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif;font-size:12px;color:#333333;fill:#333333;" y="21" x="8">
<tspan x="8" style="font-size: 10px">Apr</tspan>
<tspan dy="16" x="8" style="fill:#8bbc21">Berlin</tspan>
<tspan dx="0">: </tspan>
<tspan dx="0" style="font-weight:bold">8.4°C</tspan>
</text>
We can get the text of the popup using either of the following:
#All text together
puts browser.element(:css => 'g.highcharts-tooltip').text
#=> "DecTokyo: 9.6°C"
#Each line of the popup
browser.elements(:css => 'g.highcharts-tooltip tspan').each{ |x| puts x.text }
#=> "Dec"
#=> "Tokyo"
#=> ":"
#=> "9.6°C"
Note that the text method only displays visible text, therefore you need to ensure that the popup is displayed before getting the text. Alternatively you could parse the html of the elements.
Search the Data Points
To find the value (ex temperature) for the specific date, we need to iterate over the path elements until we find one that matches the desired date. Using the points variable from before, let us get the value for July.
point = points.find do |p|
#Hover over a point
p.hover
#Get the month from the popup
month = browser.elements(:css => 'g.highcharts-tooltip tspan')[0].text
#Keep going until the month is "Jul"
month == 'Jul'
end
#Get the value of the point
point.hover
puts browser.elements(:css => 'g.highcharts-tooltip tspan')[3].text
#=> "25.2°C"
This value could then be compared to the spreadsheet of expected values.
Running Script
Putting all the points together, gives the final running example.
require 'watir'
browser = Watir::Browser.new :chrome
browser.goto 'http://www.highcharts.com/demo/'
series1 = browser.element(:css => 'g.highcharts-markers')
all_path_elements = series1.elements(:css => 'path')
points = all_path_elements[0..-2]
point = points.find do |p|
p.hover
month = browser.elements(:css => 'g.highcharts-tooltip tspan')[0].text
month == 'Jul'
end
point.hover
puts browser.elements(:css => 'g.highcharts-tooltip tspan')[3].text
#=> "25.2°C"
Related
I want to retrieve hidden text when the visibiility attribute is hidden:
<div id = "tt52433002" class="yui-module yui-overlay yui-tt yui-overlay-hidden" style="z-index: 2; visibility: hidden;">
<div class="bd">Associated with the domain : testci20160503105556.com</div>
</div>
I tried:
browser.hidden(:class, 'bd').text
and
browser.hidden(:class, 'bd').value
But I get this error:
"unable to locate element, using {:class=>"bd", :tag_name=>"input", :type=>"hidden"}"
Watir is designed to act like a user. So if a user can not see the text in an element, then Watir will not return the text of the element.
Also, the element you are looking for is a div not a hidden.
If you need the text you can do:
browser.div(class: 'bd').inner_html
which makes a JavaScript call to provide the result.
This works:
browser.div.attribute_value('id') => tt52433002
as does this:
browser.div(class: 'bd').inner_html[/testci\d{14}/] => testci20160503105556
First things first. The error says that Watir cannot find an element using the criteria you specified. That means either that no such thing exists anywhere in the DOM, or that it might be inside a frame.
Since the element you want is a div, then you should be using the .div method to access it
browser.div(:class => 'bd') #finds first matching div
A potential second problem could occur if that classname is not very unique. Unless you specify an additional parameter, such as index, or perhaps a portion of the text contained by the div, you may not find the div you are looking for. A fast debugging trick (I like to do it from IRB) is to get a collection of matching divs and check the size
div_count = browser.divs(:class => 'bd').size
puts "there are #{divcount} divs of class bd in the dom"
If the count is anything more than 1, then you likely need to change how you are selecting it to ensure you get the right one. for example
browser.div(:class => 'bd', :text => /Associated with the domain/)
If the count is zero, then check for frames
frame_count = browser.frames.size
iframe_count = browser.iframes.size
If there are frames you will have to tell watir to look inside the frame for the div, if more than one frame then be sure you specify the right one
browser.frame.div(:class => 'bd') #looks for div inside first frame
Once you are sure you have the right div, then you ought to be able to use a method like .text or as in another answer .inner_html to get the contents of the div.
My page contains two divs at the top (a header and another section) that are fixed while the rest of the page can be scrolled. I need to hover over a link element and then click on a button that appears when hovering over the link. Since I am using the page-object gem I tried to use scroll_into_view. However the link still remains behind the fixed divs. This prevents the button from showing. Is there anything that can be done to force it into view? Links at the top and bottom of the scrollable area of the page work fine but items in the middle of the page have issues as they appear behind the fixed divs when scrolled. I am using ruby+watir-webdriver with page-object gem.
Unfortunately I can't post the site.
My code looks something like this:
class MyPage
div(:items, :class => 'product_items')
def index_for(product)
index = items_elements.find_index{|x| x.h4_element.text == product}
index
end
def add_product(product)
index = index_for(product)
product = items_elements[index.to_i]
product.link_element(:class => 'product_more_info').when_present.scroll_into_view
product.link_element(:class => 'product_more_info').hover
product.button_element(:class => 'product_info_button').when_present.click
end
end
The links in the middle of the page remain behind the fixed divs. When it hovers it actually triggers a nav dropdown that is in the header since the link is directly behind it. Seems to work for 70% of the links. The 30% in the middle are the issue right now.
I think I have reproduced your problem with the following page. When the div element to hover on is scrolled into view, it appears below the menu. Hovering does not cause the onmouseover to trigger.
<html>
<body>
<div style="position:fixed; left:0; top:0; z-index=99999; border:2px solid red; width:100%">menu</div>
<div class="spacer" style="height:2000px"></div>
<div id="hoverable" onmouseover="document.getElementById('target').style.display = '';">to hover</div>
<button id="target" style="display:none;">the button</button>
<div class="spacer" style="height:2000px"></div>
</body>
</html>
One solution that works (at least for this example page), was to try hovering over the element. If the button did not appear, assume that the menu is in the way, scroll back up the page a bit and try again. Assuming the above page, this could be done with the page object:
class MyPage
include PageObject
div(:hoverable, :id => "hoverable")
button(:target, :id => "target")
def hover()
# Try to hover over the element
hoverable_element.when_present.hover
# If the button element does not appear, the menu must be in the way.
# Scroll back up 100 px so that the div appears below the menu and try again.
unless target_element.visible?
execute_script('window.scrollBy(0,-100);')
hoverable_element.hover
end
# Check that the button appears as expected
p target_element.visible?
#=> true
end
end
Applying the same idea to your page object, the add_product method would become:
def add_product(product)
index = index_for(product)
product = items_elements[index.to_i]
product.link_element(:class => 'product_more_info').hover
unless button_element(:class => 'product_info_button').visible?
execute_script('window.scrollBy(0,-100);')
product.link_element(:class => 'product_more_info').hover
end
product.button_element(:class => 'product_info_button').click
end
I am tring to test pie chart in highcharts with watir web driver. I have the issue of locate a tiny small piece of the pie.
#Get the pie
series1 = browser.element(:css => 'g.highcharts-tracker')
#Get the pieces
all_path_elements = series1.elements(:css => 'path')
#get the second to last
points = all_path_elements[-2..-2]
with range -1 to -1 it will able to get the last piece.
-2 to -2 still last piece.
-3 to -3 will get the third from last.
it will skip the second to last. i think because it is the smallest. but i am able to locate it with my mouse.
is there another way to locate the path elements? so maybe an alternative way can solve my issue.
i made a red dots where the piece gets skip.
http://i.stack.imgur.com/tDAaH.png
I figure out the solution myself. instead of hover on to each of the path elements. i did a fire event of the onmouseover of each path elements.
series1 = browser.element(:css => 'g.highcharts-tracker')
all_path_elements = series1.elements(:css => 'path')
points = all_path_elements[0..-1]
point = points.find do |p|
p.fire_event "onmouseover"
puts browser.elements(:css => 'g.highcharts-tooltip tspan')[3].text
end
How do I scroll a web application in Watir ?
I have tried#browser.send_keys :space
This just brings the whole page down. But I have a scroll within the application, I need to scroll the vertical scroll bar down & up in my automation testing, Please help me !
Thanks!
<div dojoattachpoint="containerNode" class="containerNode tabContentPane typeNavigationSingleChild" style="overflow: auto; left: 5px; top: 10px; width: 1549px; height: 535px;">
<div pageid="lifecycle_theme_home_page_dashboard_pageId" id="lifecycle_theme_home_page_dashboard_pageId" style="height: 535px; padding: 0px; width: 1549px;" widgetid="lifecycle_theme_home_page_dashboard_pageId" title="" role="group" class="dijitContentPane wcs-nullLayout">
Solution 1) Scroll to Last Element
I think Vinay's approach should work. However, in the current form, it assumes that the element already exists on the page. I am guessing the element you want is only visible once you scroll far enough. So what you can do is scroll to the last element in the div.
Watir-Webdriver
In Watir-Webdriver:
div_with_scroll = browser.div(:class => 'containerNode tabContentPane typeNavigationSingleChild')
div_with_scroll.elements.last.wd.location_once_scrolled_into_view
Watir-Classic
In Watir-Classic, it is different since it does not use selenium-webdriver:
div_with_scroll = browser.div(:class => 'containerNode tabContentPane typeNavigationSingleChild')
div_with_scroll.elements.last.document.scrollIntoView
Solution 2) Use ScrollTop Property
As an alternative, if the above does not work, you can set the scrollTop property to move the div element's scrollbar. This worked for an application that I was working on that had content that was only loaded once you scrolled to the bottom.
Watir-Webdriver
To jump the scrollbar to the bottom, which in theory should trigger the below content to load, set the scrollTop property to the scrollHeight:
div_with_scroll = browser.div(:class => 'containerNode tabContentPane typeNavigationSingleChild')
scroll_bottom_script = 'arguments[0].scrollTop = arguments[0].scrollHeight'
div_with_scroll.browser.execute_script(scroll_bottom_script, div_with_scroll)
To jump back to the top, set the scrollTop to zero.
div_with_scroll = browser.div(:class => 'containerNode tabContentPane typeNavigationSingleChild')
scroll_top_script = 'arguments[0].scrollTop = 0'
div_with_scroll.browser.execute_script(scroll_top_script, div_with_scroll)
You can also use any value in between depending on where you need to go to.
Watir-Classic
In Watir-Classic, you can set the scrollHeight more directly:
div_with_scroll = browser.div(:class => 'containerNode tabContentPane typeNavigationSingleChild')
#Go to bottom
div_with_scroll.document.scrollTop = div_with_scroll.document.scrollHeight
#Go to top
div_with_scroll.document.scrollTop = 0
if the element is at the bottom of the page, it will load more content:
browser.element.wd.location_once_scrolled_into_view
Using Watir-Classic, the second method Justin Ko provided works great for iterating through a scrollable section to find something specific. Here's an example of that:
div_with_scroll = browser.div(:class => 'containerNode tabContentPane typeNavigationSingleChild')
scroll_value = 50 # change this number to match how much you want to scroll each iteration
max_loop = div_with_scroll.document.scrollHeight / scroll_value
if div_with_scroll.document.scrollHeight % scroll_value > 0 # accounts for any remainder height
max_loop = max_loop + 1
end
for i in 0..max_loop do
div_with_scroll.document.scrollTop = i * scroll_value # moves the scrollbar
if div_with_scroll.text.include? 'Search Text'
puts 'Search Text found in iteration: ' + i.to_s()
break # exits the loop when found
end
end
There may be a more efficient way to do what I'm doing here, but you get the idea.
Use Javascript (eg. bottom of page):
browser.execute_script("window.scrollTo(0, document.body.scrollHeight);\n")
use a correct javascript executor to achieve this result - below i have written some code to show you the 'in my opinion' best and most reliable way to achieve this:
BOTTOM OF PAGE:
((IJavaScriptExecutor)webapplication).ExecuteScript("window.scrollTo(0, document.body.scrollHeight - 5)");
TOP OF PAGE:
((IJavaScriptExecutor)webapplication).ExecuteScript("window.scrollTo(0, document.body.scrollHeight 0)");
you can also set different values to scroll to different heights - for example the scroll to bottom code i have set to 5px from the bottom of the page. good luck, hope this is of somewhat use to you.
I'm trying to build a PDF from user-generated content and I have a chunk of information that should be grouped together. I know of the group method to make sure text all gets rendered together, but this doesn't seem to work with a mix of text and images. Is there something that can do this with Prawn, or do I need to try to calculate cursor position and manually linebreak?
Edit: For illustration of what I'm looking to do:
pdf = PDF::Document.new
20.times do
pdf.group do
pdf.text "Something"
pdf.image "path/to/image.jpg"
pdf.text Time.now.to_s
end
end
And I would expect to not ever have "Something" on one page and the image on the next, but that is what I see. Is there some way I can achieve what I want?
Okay, I've figured it out. Prawn does not seem to take the image height into account when grouping, but you can hack your way around it:
pdf.group do
pdf.text "Best regards, (...)"
pdf.image "#{Rails.root}/vendor/signature.jpg", {
:height => 30,
:at => [0, pdf.y.to_i - #bottom_margin]
}
pdf.move_down(35)
pdf.text " "
end
The trick is to use absolute positioning for the image, and move the text cursor down manually.