I'm using Capybara with Ruby 1.9.3 using the selenium driver in order to get information off a website. After clicking through a couple of pages I visit the page I want and I put:
all(:css, 'td').each { |td| a_info << td }
a_info.each {|t| puts t.text }
The error I then get after about 10 seconds of waiting:
[remote server] resource://fxdriver/modules/web_element_cache.js:5628:in `unknown': Element not found in the cache - perhaps the page has changed since it was looked up (Selenium::WebDriver::Error::StaleElementReferenceError)
Followed a lot more remote server errors. I've given the page 10-30 seconds of sleep time and it's still not loading and when I do print page.html, I see a javascript script and then all the td's that I'm trying to get info from. I know that the error means an element being found is not the current one but it seems like all the elements have been loaded already so I'm not sure why they wouldn't exist anymore. I've scoured the internet for hours looking for this and would love any kind of help from possible solutions to try and the next steps for trying to figure it out. I can provide any extra information needed, just let me know.
It happens when you make a scope and into that you change the page, for example, but keeps making assertions inside it, like the following:
within 'selector' do
expect(page).to have_link 'Link'
visit 'page'
expect(page).to have_link 'I do not know this whitin, I am stale.'
find('I do not know this within, I am stale.').click
end
Just reopen you scope again over to keep working on the old one.
within 'selector' do
expect(page).to have_link 'Link'
end
visit 'page'
within 'selector' do
expect(page).to have_link 'Now i am fresh'
find('#now-i-am-fresh').click
end
This is my least favorite error. I'm gonna refer you to this exact question on stack overflow asked about a year before Random "Element is no longer attached to the DOM" StaleElementReferenceException
In presented code you are printing text on console, but maybe in your real code you are clicking on these links inside each loop which is wrong.
Solution
Try first to extract href attributes and then go through them in loop.
Code:
a_href = a_node.collect {|a| a[:href] }
a_href.each { |a| visit(a) }
I'm not sure if it fits to your situation, give more info in comments.
Related
I am following SauceLab's Ruby and Selenium guidebook - it's a free book to help teach Selenium in combination with Rspec. In the first real example of the book, the author leads us to write a test script that goes to this link:
http://the-internet.herokuapp.com/login
Our script then will insert the appropriate uname and pwd (listed in text on the page... it's tomsmith and SuperSecretPassword!), and then submit the form.
All of the above works - then it goes a little further and looks to introduce an assert to ensure that we got to the correct page (the page that results from successful login). This assert, even when I do as per the example in the book, does not work. It can't find the element, and says so as here:
Failure/Error: expect(#driver.find_element(css: '.flash.success').displayed?)
Selenium::WebDriver::Error::NoSuchElementError:
Unable to locate element: .flash.success
# ./ruby_spec.rb:20:in `block (2 levels) in <top (required)>'
I was hoping I could get some help from SO in helping me formulate a working assert to ensure I'm on the login page after successful login via test script. Here is my code so far
require 'selenium-webdriver'
describe 'Login' do
before(:each) do
#driver = Selenium::WebDriver.for :firefox
end
after(:each) do
#driver.quit
end
it 'test_to_succeed' do
#driver.get 'http://the-internet.herokuapp.com/login'
#driver.find_element(id: 'username').send_keys('tomsmith')
#driver.find_element(id: 'password').send_keys('SuperSecretPassword!')
#driver.find_element(id: 'login').submit
expect(#driver.find_element(css: '.flash.success').displayed?) #line that fails every run
end
end
Minus a little revised rspec syntax, this is exactly as what is listed in the book. The book has for the failing line as below, with some deprecated syntax:
#driver.find_element(css: '.flash.success').displayed?.should be_true
But this also fails with the exact same error above. To further suss out it being my expect(), I have tested the expect() syntax for other elements to test that it is working/not the source of the problem. The source of the problem, instead, is that my script is not resolving the css element as the book indicates it would be able to. It can't find '.flash.success', in loose terms.
So I went to page source of the successful login page and found some other things I could assert against. This came as attractive since it is unique to the success page:
<div data-alert id='flash' class='flash success'>
You logged into a secure area!
×
</div>
But alas, I found the exact same failures when trying this in my code:
expect(#driver.find_element(class: 'flash success').displayed?) #line that fails every run
or even
expect(#driver.find_element(id: 'flash').displayed?) #line that fails every run
Essentially, I'm a little mystified how I am supposed to get a unique element off of the successful login page to assert correctly. All the efforts above (including the one in the sample text that I'm learning from) would seem reasonable to work, to me... but it is not so. Rather than simply switching texts to one with working examples, I thought it would be more instructive for me to work out and try to discover why the examples above do not work.
I am using Selenium-WebDriver for Ruby and I am trying to verify that text is present on a page. I have done many searches and tried many things and the best answer I have found is to use something like
def check_page(textcheck)
if verify {#driver.find_element(:id=>"body").text.include?(textcheck)}
yield it_to "fail"
else
yield it_to "pass"
end
end
The expected outcome if the value of textcheck is present in the body would be pass and if the value of textcheck is not present in the body it would be fail. What is actually happening is if :id=>"body" is present then it is pass and if it is not present then it is fail regardless of .text.include?(textcheck)
If anyone could point me in the right direction for how to verify text is present on a page using Selenium-WebDriver in Ruby it would be greatly appreciated. I have found workarounds for certain cases where I can do
verify {#driver.find_element(:tag_name, 'h1').text!=(textcheck)}
but the element I am trying to verify I can't get to so easily. I looked into css locators and was very confused on how to simplify the tag so I could use it. Any help would be greatly appreciated. Thank you very much. If you require any more information from me please let me know and I will provide it as soon as possible.
I am using Ruby 1.93 with Selenium-WebDriver 2.25 testing in Firefox 14.0.1
I do it this way
#wait = Selenium::WebDriver::Wait.new(:timeout => 30)
begin
#wait.until { #driver.find_element(:tag_name => "body").text.include?("your text")}
rescue
puts "Failure! text is not present on the page"
#Or do one of the options below
#raise
#assert_match "true","false", "The text is not present"
end
UPDATE
Answer to your question in the comments section.
There are two kind of "waits", implicit wait and explicit wait. You can read more about it here. The reason your code failed was because you were searching by "id"=>"body" and not by "tag_name"=>"body". Usually all text is encompassed within the "body" HTML tags in your DOM.
I'm using selenium-webdriver with ruby to write automated tests.
Chrome and the chromedriver binary work really well, but I have an issue with Firefox that is related to the configuration of the browser and that's making my tests fail, whereas they pass with Chrome.
When executing the tests in Firefox, sometimes I get an alert with this message:
Although this page is encrypted, the information you have entered is
to be sent over an unencrypted connection and could easily be read by
a third party
And it breaks the execution. Is there a way of disabling this warning in recent Firefox versions (10+) or handling this behavior with Selenium?
In the process of finding a solution for this, I think I might have found a bug in Capybara (v1.1.2).
I managed to get around this problem using the following approach, instead of using the click from capybara (which would not allow me to capture an exception), I started using the click method from selenium-webdriver.
It seems that when Firefox triggers this alertbox, a string with the message
Although this page is encrypted, the information you have entered is
to be sent over an unencrypted connection and could easily be read by
a third party
is returned as a result of object.native.click, otherwise the string
ok
is returned.
# Define the click method in ruby and call it when you're executing a 'click'
# Public: Click the object.
#
# object - Capybara::Node::Element object to click.
def click object
return_string = object.native.click
check_firefox_alertbox if return_string == "ok"
end
def check_firefox_alertbox
if #current_browser == :firefox
#session.driver.browser.switch_to.alert.accept
end
rescue Exception => e
puts e
end
Here is what you can do. Type about:config in the firefox. You would be presented a number of options (once you pass through a warning message).
You have to look for security.warn_leaving_secure; and security.warn_leaving_insecure. Make both of them false. And you would be good to go.
Please note: This would work only on the FF instance you have made modification to, so you will need to use firefox profile to launch this instance.
Hope this helps.
Actually this meant to be a comment but I need to go above 50 in order to be able to comment..I suppose by 'breaking' the execution you mean that of the Ruby Script right? What happens to Firefox? Needs a click to proceed? If that is the case you can improvise by capturing the Ruby error after inserting the sensitive code (where it breaks) between a BEGIN and a RESCUE clause..Something like this..
BEGIN
.
.
Code that is giving you a headache
.
.
RESCUE
.
Capture the exception and give Ruby a chance to continue the script normally.
.
.
END
Alternatively if you don't fancy the above solution you can go to Firefox and then type in the address box about:config. Filter by 'security.warn' and set to false all the boolean variables you see there. Good riddance, fingers crossed ;)
I am using Watir-webdriver 0.5.3. Before with versions circa 0.4.x this was never trouble:
while $browser.div(:class => /^expander$/).exists?
$browser.div(:class => /^expander$/).click; sleep 1.5
end
The click causes some javascript to execute that changes the class for the div that was clicked, expanding a treeview control. The objective of the script is to keep expanding nodes until there are none left unexpanded.
I am frequently getting "Element is no longer attached to the DOM" on the .exists? line, which is esp. troubling, because the reason I began using .exists? extensively was to avoid such errors that immediately crash the script in the first place.
visually I do observe the expansion almost right away after the .click, and if I pause things right there, Firebug does confirm that the div with only class='expander' had been replaced with a div in its place with class='expander hasChildren expanded', and only that div.
The error seems to be coming up from webdriver.
Has anyone else had this problem or recommend workarounds?
In an earlier question I needed to switch to the /^expander$/ form to match exactly that class exclusively, after the new watir-webdriver behavior changed.
What about this?
while $browser.div(:class => /^expander$/).exists?
count = $browser.divs(:class => 'expander hasChildren expanded').size
$browser.div(:class => /^expander$/).click
$browser.wait_until{$browser.div(:class => 'expander hasChildren expanded', :index => count).exists?}
end
The other approach I have used where actions are causing things to go away, is get a count, and then work by index, from the last item back towards the first, that way the 'next thing' you are addressing should still exist. That can be more reliable than always addressing 'first one' where your prior action just removed 'first one'. If expanding things opens up new 'expander' classes then you might have to do this in waves, where you end up expanding the first tier, then the second, etc till you see no more of them to expand. it might mean making a method to expand all currently visible expanders, then checking when it is done to see if there are any visible expanders, and if so, call the method again.
Try this one
Watir::Wait.until do
$browser.div(:class => /^expander$/).click
not $browser.div(:class => /^expander$/).exists?
end
Following Chuck's approach this code also first enumerates all candidates that need clicking to get expanded or whatever, and then in a one-line foreach loop, clicks them from the bottom of the page going up:
hitlist = $browser.divs(:class => /^expander$/) # put all candidates into an array
hitlist.to_a.reverse.each {|r| r.click; sleep 0.1} # loop in reverse
$browser.wait_until { $browser.divs(:class => /^expander$/).size < 1} # Wait until they're all AJAXed out of existence
Lines 1 & 2 could even be compacted to simply $browser.divs(:class => /^expander$/).to_a.reverse.each {|r| r.click; sleep 0.1} if you don't think the interim hitlist collection might be handy to have...but nevermind that.
Even though the code looks sound, my main concern here however is the very severe performance penalty using a regex locator like /^expander$/ apparently has...not something they tell you in the docs! Not something I faced pre 0.5.3 when standard, quoted class locators like :class => "expander" were exclusively matched.
Once or twice would be forgivable (in my case finding div(:class => /^expander$/) takes between 5-50 seconds in a <100kB html page each time) but I'm especially worried about line 3: who's to stay how many times the {boolean test block} gets executed to poll if the condition is finally met?
I'm very new to Watir.
I have a bit of Ruby/Watir code that is supposed to detect if an element, exists, and if so, click it, if not, click a different element. Both elements, show up, every time. Unfortunately nothing I've tried works.
if browser.contains_text("/media/images/icons/reviewertools/editreview.jpg")
then browser.image(:src => "/media/images/icons/reviewertools/editreview.jpg").click
else browser.image(:src => "/media/images/icons/reviewertools/savereview.jpg").click
end
This eventually fails with "Unable to locate element, using {:src=>"/media/images/icons/reviewertools/savereview.jpg"} (Watir::Exception::UnknownObjectException)"
It should have clicked /editreview.jpg, which was visible.
I have also tried:
if browser.image("/media/images/icons/reviewertools/savereview.jpg").exists
then browser.image(:src => "/media/images/icons/reviewertools/savereview.jpg").click
as well as:
if browser.image("/media/images/icons/reviewertools/savereview.jpg").exists?
Note that NONE of these cases detect the element, or failing to do that, execute the else clause.
Please, if you respond, provide specific code examples for your suggestions.
There are only methods "exist?" and "exists?". So "exist" won't work. Consult here.
Can you try to identify existence of a different element, such as a link. Does it work for you? There shouldn be no exception. Watir needs to return False in this case.
You need to use the same syntax with both the click and the exists? methods. This has also been pointed out by Kat and Chuck and Kinofrost.
if browser.image(:src => "/media/images/icons/reviewertools/editreview.jpg").exists?
then browser.image(:src => "/media/images/icons/reviewertools/editreview.jpg").click
else browser.image(:src => "/media/images/icons/reviewertools/savereview.jpg").click
end
I'd recommend checking the DOM to double check the src for your image matches what you're putting. Press F12 in IE8, or use whatever tool is relevant to your browser. You could try using IRB to connect to the browser and try and find the image.
If these fail then I'd try locating the image another way. If the image is in a form this can cause problems and you'll have to locate the form before the image.
Or try another way to locate it, just to make sure that it's possible.
browser.image(:index => 3).click
browser.image(:id => 'an_image').click
browser.div(:id => 'image_container').image(:index => 2).click
You can use this link to see what ways you can identify an image, and don't forget that you can use more than one identifier at a time, eg. (:class => /regexofaclass/, :index => 2)
There's nothing wrong with your code as it is (apart from the ? at the end of "exists", and the last line which doesn't contain what you're looking for).
Presuming that this thing really is an 'image' as defined by it's HTML tag, your attempt to identify if the object is there is failing because
you are not using the right method, in Ruby, methods that return a bool end in a question mark
it's not text,
and in the other attempt you provided only a 'what' and not a how, and still used the wrong method.
try this
if browser.image(:src, "/media/images/icons/reviewertools/savereview.jpg").exists?
then browser.image(:src, "/media/images/icons/reviewertools/savereview.jpg").click
Otherwise have a good look at the HTML, are you actually looking at a 'button' perhaps?