Selenium Webdriver (Ruby) : Cannot click on button - element not found - ruby

This is the page code:
<div class="modal-buttons">
<button class="button-orange" ng-click="cancel()">
<span>
Cancel
</span>
<span class="icon cancel"></span>
</button>
<button class="button-orange" ng-click="apply()">
<span>
Apply
</span>
<span class="icon run"></span>
</button>
As you can see - this modal has two buttons and I have tried a dozen different ways - but I just want to click on the button labeled "apply" No matter which route I take - it just keeps saying element not visible.
Here are some of the things I've tried:
# #driver.find_element(:class, "button-orange")[2].click
# #driver.find_element(:xpath, "//div[4]/div/div[2]/div[3]/button[2]").click
# #driver. find_element(:link_text, 'Apply').click
# #driver. find_element(:tag, 'Apply').click
# #driver.find_element(:css, "input[value='Apply']").click();
# #driver.find_element(:css, "input[value='Apply']").click();
# #driver.find_element(:xpath, "//button[contains(text(),'Apply')]").click
# #driver.find_element(:xpath, "//button[contains(text(),'apply')]").click
# #driver.find_element(:xpath, "//input[#value='Apply']").click();
# #driver.find_element(:class, "button-orange.icon-run").click
# #driver.find_element(:css,'a[class$="button-orange"]').click
# #driver.find_element(:xpath, "").clear
The exact error I get is:
Error: test_login_to_chute(LoginToChute)
Selenium::WebDriver::Error::ElementNotVisibleError: element not visible
(Session info: chrome=36.0.1985.125)
(Driver info: chromedriver=2.10.267521,platform=Windows NT 6.3 x86_64)
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/response.rb:51:in `assert_ok'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/response.rb:15:in `initialize'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/http/common.rb:59:in `new'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/http/common.rb:59:in `create_response'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/http/default.rb:66:in `request'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/http/common.rb:40:in `call'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/bridge.rb:634:in `raw_execute'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/bridge.rb:612:in `execute'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/remote/bridge.rb:369:in `clickElement'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.42.0/lib/selenium/webdr
iver/common/element.rb:54:in `click'
C:/Analytics/AutomatedTesting/DevEnv/Wonderland/Full Regression/2.login_to_chute
.rb:165:in `test_login_to_chute'
162: #driver.find_element(:xpath, "(//input[#type='text'])[8]").clear
163: #driver.find_element(:xpath, "(//input[#type='text'])[8]").send_
keys "25"
164:
=> 165: #driver.find_element(:xpath, "//div[4]/div/div[2]/div[3]/button[2]").c
lick

Thank you all so much for helping.
Here's what ended up being the deal.
Because the website is all one angular page, it's loading a bunch of things in the background. Including multiple other buttons that aren't actually visible.
So here's what I ended up using:
#driver.find_elements(:xpath, "//button")[-1].click
And I updated my find_element variable to only find visible elements.
def find_visible_element(how, what)
elems = #driver.find_elements(how, what).select { |e| e.displayed? }
len = elems.length
if len == 0
raise "No matches found."
elsif len > 1
raise "Ambiguous match. Found #{len} matches."
end
elems.first
end

There are several reasons behind this error. I was getting the same error and here is how I narrowed down the possible causes.
The Item can be located in the iframe, in this case you have to switch to the iFrame and locate the element
Another reason can be the element is not yet displayed and you are performing operations on it. In this case you can apply explicit wait as below.
wait = Selenium::WebDriver::Wait.new(:timeout => 10)
wait.until{browser_driver.find_element(:xpath,'***').displayed?}
Using this chunk in your code can help you to wait for maximum 10 seconds till the item is displayed
It is possible that, your xpath is returning more than 1 web element to the driver i.e. more than 1 element can be located with the same xpaths. In this case make sure that your xpath is more specific and returning only one web element. My issue was solved with 3rd possibility.

Wait unti the javascript modal is fully loaded (using a Wait block, for example)
Also, you have to use the correct selector. Frankly, quite of few of your example selectors don't make any sense in the context of the html you posted.
In Chrome's inspector, make sure what it is that a mouseclick would click on. Have you tried selecting the <span>?
Try some of these:
:css => "button.button-orange[ng-click='apply()']"
:xpath => "//button[#ng-click='apply()']"
:xpath => "//button/span[contains(text(),'Apply')]/.."
:css => "button.button-orange[ng-click='apply()'] > span"
:xpath => "//button[#ng-click='apply()']/span"
:xpath => "//button/span[contains(text(),'Apply')]"
By the way, chrome's element inspector has a find function that can search xpath as well as css too for selector testing.

Related

Unable to click download button with web scraper

for whatever reason mechanize is unable to click this export button to automate downloading a public government csv file, i'm trying to automate downloading a fishing report, does anyone have any ideas on how to get it to work?
agent = Mechanize.new
url = 'https://nrm.dfg.ca.gov/FishPlants/'
log :debug, "reading HTML from #{url}"
page = agent.get(url)
log :debug, 'loaded page'
form = page.search('#aspnetForm').first
button = page.search('.application_button').first
log :debug, 'clicking Export button'
response = agent.submit(form, button)
i get a stack trace with the following error
...
form.add_button_to_query(button) if button
^^^^^^^^^^^^^^^^^^^^
/Users/aronlilland/.rvm/gems/ruby-3.1.2/gems/mechanize-2.8.5/lib/mechanize.rb:581:in `submit'
/Users/aronlilland/Documents/dev/fishing-report/tasks/download/fishing_report.rake:27:in `block (2 levels) in <top (required)>'
Tasks: TOP => download:fishing_report
(See full trace by running task with --trace)
the form is confirmed returns successfully, but the button is an input field
the page seems relatively straight forward, so dont know why i'm unable to scrape it - and its also public data
<form method="post" action="./" id="aspnetForm">
<!-- .... -->
<input
type="submit"
name="ctl00$cphContentMiddle$btnExport"
value="Export"
id="ctl00_cphContentMiddle_btnExport"
class="application_button"
>
<!-- .... -->
</form>
agent.submit got the wrong type for form
The issue here could be seen if you had included the beginning of the stack trace:
.../ruby/gems/3.1.0/gems/mechanize-2.8.5/lib/mechanize.rb:581:in `submit': undefined method `add_button_to_query' for #<Nokogiri::XML::Element:0x1ea14 name="form" attributes=
[#<Nokogiri::XML::Attr:0xbd38 name="method" value="post">, ...
Mechanize expects the submit to act on a Mechanize::Form instance, but instead got an instance of Nokogiri::XML::Element, as you can see by adding this to your code:
form.class # => Nokogiri::XML::Element
If you check the docs for the Mechanize::Form class, you can see the example they give to get you the form object is this:
form = page.forms.first # => Mechanize::Form
as opposed to what you used:
form = page.search('#aspnetForm').first
The call to search here is delegated to Nokogiri, and therefore doesn't return the object type you need, but rather a Nokogiri element.
button also has the wrong type
By the way the same applies to this line:
button = page.search('.application_button').first
If you fix the type of form, you'll get into a similar issue with button not being the expected type. Again, there's an example in the docs showing how a button is found:
agent.submit(page.forms.first, page.forms.first.buttons.first)
You'll need to figure out how to find the specific button you need though, I haven't worked with Mechanize before, so I can't offer a suggestion here. Presumably there's a way to convert the button you find through search to a Mechanize::Form::Button instance.

Select2 feature test with Capybara

I'm trying to write a rspec feature test with capybara but,
I have some trouble with a test on a select2 element.
see my test code.
Using feature test with capybara
feature "Backend Landing Pages" do
let!(:landing_page) { create(:landing_page, country: country) }
let!(:country) { create(:country, id: 2) }
let!(:restaurant) { create(:restaurant_with_locale) }
let!(:landing_page_restaurant) { create(:landing_page_restaurant,landing_page: landing_page, restaurant: restaurant) }
before(:each) do
login
click_on("Website")
click_on("Landing Pages")
end
scenario "user creates a landingpage", js: true do
first(:link,"netherlands").click
fill_in "landing_page_domain", with: landing_page.domain
fill_in "landing_page_slug_nl", with: landing_page.slug_nl
fill_in "landing_page_slug_en", with: landing_page.slug_en
fill_in "landing_page_header_title", with: landing_page.header_title
fill_in "landing_page_title", with: landing_page.title
attach_file "landing_page_header_image",( Rails.root + 'app/assets/images/site_builder/image3.jpg')
page.find('#landing_page_restaurant_select').set('Le Garage - Amsterdam')
page.save_screenshot ("test.png")
click_on("Save")
expect(page).to have_content("successfully created")
expect(LandingPage.count).to eq 2
expect(LandingPage.last.landing_page_restaurants.count).to eq 1
end
scenario "user edits a LandingPage", js: true do
click_on("Edit")
expect(page).to have_content 'Edit landing Page '
page.save_screenshot ("edit.png")
end
end
I'm getting the next error.
Failures:
1) Backend Landing Pages user creates a landingpage
Failure/Error: expect(LandingPage.last.landing_page_restaurants.count).to eq 1
expected: 1
got: 0
(compared using ==)
# ./spec/features/backend/landing_pages_spec.rb:33:in `block (2 levels) in <top (required)>'
Who can see what im doing wrong here
cant figure out why the restaurant is not connected to the landingPages
thanks in advance
In the question you mention you are using a select2, but then you are calling set on what I assume is a regular select element. When you are using JS widgets they will often overwrite the values of the hidden elements they build off on a submit event of the form they're in. Because of this it is probable the value you're setting is getting overridden. Rather than set the values of hidden elements (which most capybara drivers don't allow for this specific reason - not sure which driver you're using) you need to replicate the users behaviors. From the select2 examples page it appears the select2 widget is built from a span element and a ul list with the options in it. Therefore something along the lines of
find('span.select2').click # the selector may need to be more specific to locate the exact span, without your html I don't know what that selector would be
find('li', text: 'Le Garage - Amsterdam').click
This would click on the select2 element opening the dropdown, and then click on the li element with the correct text - thereby selecting it.

error when clicking a link using ruby/watir

I am new to ruby/watir and am getting an error when trying to click on a link.
The error is:
C:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-classic-4.0.1/lib/watir-classic/elemen
t.rb:328:in `assert_exists': Unable to locate element, using {:tag_name=>["a"],
:id=>"My Link"} (Watir::Exception::UnknownObjectException)
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-classic-4.0.1/lib/watir-c
lassic/element.rb:474:in `perform_action'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-classic-4.0.1/lib/watir-c
lassic/element.rb:354:in `click!'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-classic-4.0.1/lib/watir-c
lassic/element.rb:157:in `click'
from Login.rb:22:in `<main>'
The link that I am trying to click looks like this:
<TD><DIV id=div style="DISPLAY: inline"><A id=Hyperlink href="javascript:RunFullScreen('myURL')">My Link</A></DIV>
Also, I tried to write out all links on the page using 'puts', but nothing was written out when the script finished
I used this code to write out the links (not sure if this is correct or not):
browser.links.each {|link| puts link.attribute_value("text") if link.visible?}
The error says tat you tried to find an object with the following properties: {:tag_name=>["a"], :id=>"My Link"}, but you have show us HTML code with A id=Hyperlink. So to find out that HTML you need to specify properly its attributes:
#b.element :tag_name => 'a', :id => 'Hyperlink'
or
#b.a :id => 'Hyperlink'

Ruby Watir: when_present not appropriately waiting until things are present?

I'm having an issue getting when_present to actually wait until the element is present in certain cases. The problem occurs only intermittently, but it always happens when changing pages.
Example: I want to click save on the current page, then enter text into a field on the subsequent page.
If I do:
$browser.button(:value => "Save").click
sleep 3
$browser.text_field(:label => "Name").when_present.set "Leelluu"
tt works fine.
However, if I do:
$browser.button(:value => "Save").click
$browser.text_field(:label => "Name").when_present.set "Leelluu"
it times out/crashes immediately after clicking "Save".
How do I fix this?
(I know, it seems like I have fixed it by using sleep, but my boss has told me that the sleeps are wasting time. He says I need to remove all of them from the code and find another way.)
Updating with a real-world example..... I enter a search text, then click the search button, then want to select the checkbox next to the leading checkbox on the returned list.
$browser.text_field(:id => /.*search*./).set "Snow Leopard"
$browser.button(:id => "save_filter_PricebookEntry").when_present.click
$browser.checkbox(:id => "allBox").when_present.set
I get an "element not clickable" (even though it has a when_present) on it.
unknown error: Element is not clickable at point (251, 482). Other element would receive the click:
...
(Session info: chrome=31.0.1650.57) (Driver info: chromedriver=2.2,platform=Windows NT 6.1 SP1 x86_64)
["D:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.35.1/lib/selenium/webdriver/remote/response.rb:51:in `assert_ok'", "D:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.35.1/lib/selenium/webdriver/remote/response.rb:15:in `initialize'", "D:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.35.1/lib/selenium/webdriver/remote/http/common.rb:59:in `new'", "D:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.35.1/lib/selenium/webdriver/remote/http/common.rb:59:in `create_response'", "D:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.35.1/lib/selenium/webdriver/remote/http/default.rb:66:in `request'", "D:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.35.1/lib/selenium/webdriver/remote/http/common.rb:40:in `call'", "D:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.35.1/lib/selenium/webdriver/remote/bridge.rb:634:in `raw_execute'", "D:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.35.1/lib/selenium/webdriver/remote/bridge.rb:612:in `execute'", "D:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.35.1/lib/selenium/webdriver/remote/bridge.rb:369:in `clickElement'", "D:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.35.1/lib/selenium/webdriver/common/element.rb:54:in `click'", "D:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-webdriver-0.6.4/lib/watir-webdriver/elements/checkbox.rb:26:in `set'", "D:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-webdriver-0.6.4/lib/watir-webdriver/wait.rb:100:in `method_missing'", "D:/Desktop/jardine/utils.rb:143:in `addQLIs'", "D:/Desktop/jardine/noSleepTest.rb:31:in `block in '", "D:/Desktop/jardine/framework.rb:39:in `execute'", "D:/Desktop/jardine/framework.rb:61:in `block in runTestCases'", "D:/Desktop/jardine/framework.rb:60:in `each'", "D:/Desktop/jardine/framework.rb:60:in `runTestCases'", "jardine.rb:66:in `
'"]

SyntaxError on pageobject while accessing a select_list within fieldset

I'm trying to access a select_list within a fieldset though Cheezy's pageobject.
The total html is far too long to post (well over 200 lines for just the fieldset), but I can supply the lines with all of the id's and such.
fieldset:
<fieldset class="dartPayer-Insurance" style="width: 730px;">
select_list:
<select id="dartPayer-Payer" style="width: 235px;">
Line in the pageobject I am attempting to use:
select_list(:payer_insurance){ element(:class => "dartPayer-Insurance").select_list_element(:id => "dartPayer-PayerList") }
The error I am getting when I try to run my cucumber test:
(eval):1: syntax error, unexpected '(', expecting $end
{:id=>"dartPayer-Insurance"}(identifier)
^ (SyntaxError)
This error occurs when I try to set the select_list with this line:
self.send(field, input) (Where field is "payer_insurance=" and input is "UMA")
This line works for other pages, so I am fairly certain this is not part of the problem. I'm sure it's a simple bit of syntax in the pageobject line, but I can't find any documentation for using the pageobject quite like I'm trying to. The only reference I can find is within a previous question I asked: Accessing a table within a table (Watir/PageObject)
Could anyone please tell me what I have done wrong?
Thank you in advance for your help.
Update: An example that reproduces the problem:
Given a page with the html:
<fieldset class="dartPayer-Insurance" style="width: 730px;">
<select id="dartPayer-Payer" style="width: 235px;">
<option value="UMA">UMA</option>
</select>
</fieldset>
And a page object defined as:
class MyPage
include PageObject
select_list(:payer_insurance){ element(:class => "dartPayer-Insurance").select_list_element(:id => "dartPayer-PayerList") }
def input_payer(field, input)
self.send(field, input)
end
end
Running the following code:
browser = Watir::Browser.new
browser.goto('C:\Scripts\Misc\Programming\PageObject\test.htm')
page = MyPage.new(browser)
field = "payer_insurance="
input = "UMA"
page.input_payer(field, input)
Generates the following exception:
C:/Ruby193/lib/ruby/gems/1.9.1/gems/page-object-0.9.0/lib/page-object/platforms/watir_webdriver/page_object.rb:968:in `instance_eval': (eval):1: syntax error, unexpected '(', expecting $end (SyntaxError)
{:class=>"dartPayer-Insurance"}(identifier)
^
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/page-object-0.9.0/lib/page-object/platforms/watir_webdriver/page_object.rb:968:in `find_watir_element'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/page-object-0.9.0/lib/page-object/platforms/watir_webdriver/page_object.rb:907:in `element_for'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/page-object-0.9.0/lib/page-object/element_locators.rb:11:in `element'
from pageobject.rb:7:in `block in <class:MyPage>'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/page-object-0.9.0/lib/page-object.rb:379:in `instance_eval'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/page-object-0.9.0/lib/page-object.rb:379:in `call_block'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/page-object-0.9.0/lib/page-object/accessors.rb:1089:in `block in standard_methods'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/page-object-0.9.0/lib/page-object/accessors.rb:246:in `block in select_list'
from pageobject.rb:10:in `input_payer'
from pageobject.rb:25:in `<main>'
Solution
The accessor you want for the select list is:
select_list(:payer_insurance){ element(:fieldset, :class => "dartPayer-Insurance").select_list_element(:id => "dartPayer-Payer") }
Problem
You were getting the syntax error due to the following part:
element(:class => "dartPayer-Insurance")
In the API docs for element, you can see that method definition is:
(Object) element(tag, identifier = {:index => 0})
Finds an element
Parameters:
the (Symbol) — name of the tag for the element
identifier (Hash) (defaults to: {:index => 0}) — how we find an element. You can use a multiple paramaters by combining of any of the following except xpath
The original code was missing the tag parameter, which caused the exception.
Note that the select list id was also incorrect - using dartPayer-PayerList instead of dartPayer-Payer.

Resources