Bringing elements behind fixed element into view with page-object gem - ruby

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

Related

Element is not clickable error Ruby / Watir

In my test, I am attempting to hit etsy.com, do a search, click on a result, and add the item to my cart. I'm able to do everything up until the point where I attempt to click on the 'add to cart' button. The code below actually works in the IRB so I know my locator is solid, but when I run the test I get an element is unclickable at point error
C:/Ruby24-x64/lib/ruby/gems/2.4.0/gems/selenium-webdriver-3.6.0/lib/selenium/webdriver/remote/response.rb:71:in 'assert_ok': unknown error: Element is not clickable at point (930, 586) (Selenium::WebDriver::Error::UnknownError)
(Session info: chrome=61.0.3163.100)
Here's my test
require 'watir'
# test that a user can search for and add an item to shopping cart
b = Watir::Browser.new :chrome
begin
b.goto "http://etsy.com"
b.text_field(:id => 'search-query').set 'bacon is my spirit animal coaster'
b.button(:value => 'Search').present?
b.button(:value => 'Search').click
b.p(:text => /Bacon Spirit Animal Coaster/).click
b.select_list(:id => 'inventory-variation-select-0').option(:text => 'Single ($8.00)').select
b.button(:text => /Add to cart/).click
if b.text.include?("item in your cart")
puts "Test passed!"
else
puts "Test failed!"
end
ensure
b.close
end
And here is the page HTML for the button.
<button class="btn-transaction" type="submit">
<div class="btn-text">Add to cart</div>
<div class="ui-toolkit">
<div class="btn-spinner spinner spinner-small display-none"></div>
</div>
</button>
Depending on the browser width (and likely other factors), there may be dialogs floating over the add to cart button. For example, when the test failed for me, there was a get started dialog on top of the button. Chrome attempts to click by a location. If another element is on top of your element at that location, Chrome will throw the exception.
The easiest solution is to bypass Chrome's check by directly triggering the click event:
# Watir > 6.8.0:
b.button(:text => /Add to cart/).click! # note the exclamation mark
# Watir < 6.8.0:
b.button(:text => /Add to cart/).fire_event(:onclick)
Other solutions that may conditionally work:
Maximize the browser before clicking the button - browser.window.maximize. This can move the floating element away from the button.
Close the floating dialog.

How to retrieve hidden elements when visibility is hidden

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.

Rails print navigation bar using loop and highlight button leading to current page

I'm newbie on rails.
I would like to create nav bar like here on top of page. "CSS" button is highlighted, because it leads to current page.
I would like to store paths and button titles in some place. Arrays would be ok I guess?
For example:
paths = ['root_path', 'articles_path']
titles = ['Home', 'Articles list']
Using those two arrays I would be able to print nav bar using one loop. I could easily remove or add items to it. Also I could include condition inside loop, so button leading to current page would have some html class attached (Changing it's background).
Where should I put those arrays?
Is there better/more intelligent way to achieve same thing?
P.S. I don't want to use javascript.
It is better to use hash.
Declare a ruby hash with a name say "nav_bars"
#nav_bars = { "Home": "root_path", "Articles_list": "articles_path" }
Then you can loop over the hash in your views as
<% #nav_bars.each do |key, value|%>
<h3> <%= link_to key, value %> </h3
<% end%>

how to scroll a web application in watir

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.

Ruby/Selenium WebDriver 2: Find New Elements Fast

Environment:
Ruby 1.8.7
Selenium WebDriver 2
Ubuntu 12.04 Desktop
Firefox 13.0.1
Problem:
I have div tags being used as selects. You click on the div and a dropdown style window shows up with more div tags as the select options. Now I can go in and create a way of clicking on each of these options for each 'div as select' but what I'd really like is to write a piece of code that clicks on the divs I know about and then determines which divs are now displayed and enabled after the click. Then I can click on 1 of the options at random.
So my current code is something like this:
allDivs = brwsr.find_elements(:tag_name, 'div')
origDivs = allDivs
allDivs.each do |e|
if ... # Get the div I want
e.click
newDivs = brwsr.find_elements(:tag_name, 'div')
origDivs.each do |orig|
newDivs.delete(orig)
end
# Do something with remaining new divs here
end
end
The main problem I have is that this is extremely slow. I currently have around 200 divs to spin thru and this method takes a few minutes to complete. Normally, I wouldn't expect spinning thru a couple of hundred array elements to take very long. But it does.
To shorten the time I've attempted filtering the allDivs and newDivs by spinning thru them once and deleting anything that is not displayed and enabled.
So I'm currently stuck with a really slow solution. I don't mind this 'finding of new divs' being a little slower as it should also lead to much shorter script dev times. However, taking minutes to complete is way too long.
First question: Can Selenium return only elements which as displayed and enabled with some extra API string around the find_elements call?
Second question: Does someone have a better way of finding the new div elements without having to go thru the array of elements.
It would be very helpful if you could post some sample HTML of the DOM you are trying to automate.
With that said, the ruby webdriver bindings do support finding elements by chaining. Thus, given some html like this:
<div class="outer">
<div class="one">
<div class="alpha">A</div>
<div class="beta">B</div>
<div class="gamma">C</div>
</div>
<div class="two">
<div class="alpha">A</div>
<div class="beta">B</div>
<div class="gamma">C</div>
</div>
<div class="three">
<div class="alpha">A</div>
<div class="beta">B</div>
<div class="gamma">C</div>
</div>
</div>
You could write something like this to find the "B" div in the second group of divs ("two"):
group = brwsr.find_element(:class => "two")
desired_item = group.find_element(:class => "beta")
Or, even more simply through ruby's chaining capabilities:
desired_item = brwsr.find_element(:class => "two").find_element(:class => "beta")
One other way, which I prefer the most is to select an element via a CSS selector, which selects the element faster with an easy to read syntax:
desired_item = brwsr.find_element(:css => "div.two div.beta")

Resources