I'm trying to write simple test. My problem is, that i want to wait until the page is loaded completly. At the moment i'm waiting until some elements are presen, but that is not really what i want. Is it possible to make something like this:
driver = Selenium::WebDriver.for :chrome
driver.navigate.to url
driver.wait_for_page_to_load "30000"
With Java isn't problem, but how to make it with ruby?
This is how the Selenium docs () suggest:
require 'rubygems'
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :firefox
driver.get "http://google.com"
element = driver.find_element :name => "q"
element.send_keys "Cheese!"
element.submit
puts "Page title is #{driver.title}"
wait = Selenium::WebDriver::Wait.new(:timeout => 10)
wait.until { driver.title.downcase.start_with? "cheese!" }
puts "Page title is #{driver.title}"
driver.quit
If that is not an option you can try the suggestion from this SO post though it would require some Javascript on top of the Ruby/Rails.
It seems that wait.until is being/has been phased out. The new suggested process it to look for the page to have an element you know will be there:
expect(page).to have_selector '#main_div_id'
As far as I understand webdriver, you dont need to wait for page loads because WebDriver has a blocking API but you can sure set a page load timeout.
driver.manage.timeouts.page_load = 10 # seconds
So in Ruby, whenever you use get to open a URL, the ruby script proceeds ONLY when the page completely loads.
So in your case you would simply do :-
driver.get url
That's not needed with WebDriver anymore.
WebElement click() and Actions click() both "wait for page load" if needed automatically.
You can use imclicit and explicit (in this order) wait instead (described at seleniumhq) if you need to wait for some ajax content for instance.
There have been instances where either AJAX or CSS changes caused my tests to fail at times. I added these methods to my static driver instance so that I can have the test wait for certain conditions if needed. (c#)
TimedWait in the WaitForCssChange Method is basically just a Threading.Thread.Sleep This is not the most beautiful way I guess, but it works well for my needs.
For Ajax wait:
public static void WaitForAjax()
{
var wait = new WebDriverWait(Driver.Instance, TimeSpan.FromSeconds(25));
wait.Until(d => (bool)(d as IJavaScriptExecutor).ExecuteScript("return jQuery.active == 0"));
}
For CSS Changes:
public static void WaitForCssChange(IWebElement element, string value)
{
int counter = 0;
while (true)
{
if(element.GetAttribute("style").Contains(value) || counter > 50)
{
break;
}
TimedWait(20);
counter++;
}
}
Related
I'm using mechanize to fill out a form, but I want to review it on the webpage before submission. The goal is to open a browser with the pre-filled form.
require 'mechanize'
mechanize = Mechanize.new
page = mechanize.get('https://www.linuxtoday.com/contribute.html')
form = page.form_with :name => 'upload'
form.sub_name = "mbb"
form.email = "mbb#mbb.com"
form.author_name = "Mr Potatohead"
form.title = "Mr Potatohead writes Ruby"
form.link = "https://potato.potato"
form.body = "More text"
`open #{page.uri}`
Calling out to the operating system to open the site is, of course, empty form. I don't see a page.open or similar method available. Is there a way to achieve this (using mechanize or other gems)?
That won't work because setting form fields doesn't even update the DOM.
If you want to review the form data you can inspect form.request_data
As others have mentioned in the comments try selenium, you'll need chrome or firefox driver installed, here's example with chrome to get you started:
require 'selenium-webdriver'
require 'pry' # optional
driver = Selenium::WebDriver.for :chrome
driver.navigate.to 'https://www.linuxtoday.com/contribute.html'
form = driver.find_element(id: 'upload')
form.find_element(id: 'sub_name').send_keys 'mbb'
form.find_element(id: 'email').send_keys 'mbb#mbb.com'
binding.pry # or sleep 60
driver.quit
For more instructions see documentation
No matter what I try to do, the browser tries to run the test too fast before it has a chance to find the element that I am looking for. If I put in a simple "sleep 2", it has a chance for the drop down menu to drop and load and successfully find the element. But I am wanting to learn to use the Selenium Wait command. I have tried numerous combinations of the below and looked all over the web for documentation or perhaps examples. I have found plenty of firefox and people say that some of the things below worked perfectly in firefox, but for me and my project team mates, we can not get any of the waits, implicit or explicit, to pause long enough for it to detect the element. The element does not exist until the drop down menu is fully dropped, then it can detect it. It doesn't take 2 seconds but it seems that none of my wait commands will actually make it wait. Like I said, I have tried numerous different things and almost all of them are below. If anyone can help guide me, i would appreciate it. Here is some of the code I have tried:
def setup
#driver = Selenium::WebDriver.for :chrome
#driver.get "https://website.herokuapp.com/"
#wait = Selenium::WebDriver::Wait.new(:timeout => 10) # seconds
# #driver = Selenium::WebDriver.for:chrome
# #driver.manage.timeouts.implicit_wait = 30
# #wait = Selenium::WebDriver::Wait.new(:timeout => 15)
# #wait = Selenium::WebDriver::Wait.new(:timeout => 10)
# #driver.manage.timeouts.implicit_wait = 10
# #wait = Selenium::WebDriver::Wait.new(timeout: 10)
#driver.manage.window.maximize()
# #driver.navigate.to("https://website.herokuapp.com/")
end
def test_user_name_is_present
login()
#driver.find_element(:class, "navbar-toggle").click()
# user = #driver.find_element(:class, "dropdown-toggle").text
# #wait.until{#driver.find_element(:class, "dropdown-toggle")}
#driver.find_element(:class, "dropdown-toggle")
#wait.until { #driver.find_element(:class => "dropdown-toggle") }
user = #driver.find_element(:class, "dropdown-toggle").text
assert_equal(true, user.include?('HEATHER'), "no user")
end
I'm more familiar with JavaScript or Java bindings.
But what about:
def test_user_name_is_present
login()
#driver.find_element(:class, "navbar-toggle").click()
#wait.until { #driver.find_element(:class => "dropdown-toggle").displayed? }
user = #driver.find_element(:class, "dropdown-toggle").text
assert_equal(true, user.include?('HEATHER'), "no user")
end
Our team uses this, just add to your test_helper.rb if using rails
def wait_for_ajax(sleep_sec = 4)
assert page.has_no_content?(:css, 'body.loading')
sleep sleep_sec if sleep_sec
end
I have a Ruby application using Selenium Webdriver and Nokogiri. I want to choose a class, and then for each div corresponding to that class, I want to perform an action based on the contents of the div.
For example, I'm parsing the following page:
https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=puppies
It's a page of search results, and I'm looking for the first result with the word "Adoption" in the description. So the bot should look for divs with className: "result", for each one check if its .description div contains the word "adoption", and if it does, click on the .link div. In other words, if the .description does not include that word, then the bot moves on to the next .result.
This is what I have so far, which just clicks on the first result:
require "selenium-webdriver"
require "nokogiri"
driver = Selenium::WebDriver.for :chrome
driver.navigate.to "https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=puppies"
driver.find_element(:class, "link").click
You can get list of elements that contains "adopt" and "Adopt" by XPath using contains() then use union operator (|) to union results from "adopt" and "Adopt". See code below:
driver = Selenium::WebDriver.for :chrome
driver.navigate.to "https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=puppies"
sleep 5
items = driver.find_elements(:xpath,"//div[#class='g']/div[contains(.,'Adopt')]/h3/a|//div[#class='g']/div[contains(.,'adopt')]/h3/a")
for element in items
linkText = element.text
print linkText
element.click
end
The pattern to handle each iteration will be determined by the type of action executed on each item. If the action is a click, then you can't list all the links to click on each of them since the first click will load a new page, making the elements list obsolete.
So If you wish to click on each link, then one way is to use an XPath containing the position of the link for each iteration:
# iteration 1
driver.find_element(:xpath, "(//h3[#class='r']/a)[1]").click # click first link
# iteration 2
driver.find_element(:xpath, "(//h3[#class='r']/a)[2]").click # click second link
Here is an example that clicks on each link from a result page:
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :chrome
wait = Selenium::WebDriver::Wait.new(timeout: 10000)
driver.navigate.to "https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=puppies"
# define the xpath
search_word = "Puppies"
xpath = ("(//h3[#class='r']/a[contains(.,'%s')]" % search_word) + ")[%s]"
# iterate each result by inserting the position in the XPath
i = 0
while true do
# wait for the results to be loaded
wait.until {driver.find_elements(:xpath, "(//h3[#class='r']/a)[1]").any?}
# get the next link
link = driver.find_elements(:xpath, xpath % [i+=1]).first
break if !link
# click the link
link.click
# wait for a new page
wait.until {driver.find_elements(:xpath, "(//h3[#class='r']/a)[1]").empty?}
# handle the new page
puts "Page #{i}: " + driver.title
# return to the main page
driver.navigate.back
end
puts "The end!"
I don't code in ruby, but one way you could do it in python is:
driver.find_elements
notice how elements is plural, I would grab all the links and put them into an array like.
href = driver.find_elements_by_xpath("//div[#class='rc]/h3/a").getAttribute("href");
Then get all of the descriptions the same way. Do a for loop for every element of description, if the description has the word "Adoption" in it navigate to that website.
for example:
if description[6] has the word adoption find the string href[6] and navigate to href[6].
I hope that makes sense!
I am using webdriver with FF38, but leaving the browser open window after my script is done. I find that dialogs no longer open in that window, if I continue after the testing.
The script is meant to automate forms input rather than doing it by hand, but the website does use dialog boxes to express choices -- (for example, deleting data that the script has just entered, so that I can rerun the script without overwriting information)
Is there a way to disconnect the webdriver dialog handling after I'm done?
I'm feeling a little foolish, but my searches haven't born fruit, so I may be using the wrong words in my search, given my newness to ruby and webdriver.
Example would be this:
require "watir-webdriver"
l_Browser = Watir::Browser.new :firefox
l_Browser.goto "http://www.w3schools.com/js/tryit.asp?filename=tryjs_alert"
# Click the button that opens the dialog
l_Browser.div(:class => "container").div(:class => "iframecontainer"). \
div(:class => "iframewrapper").iframe(:id => "iframeResult"). \
button(:onclick => "myFunction()").click
The result is that a popup will appear, but no popups will appear further attempts to click on the button once the script is done.
This includes even if no popup is triggered during the script (ie:, last line commented out)... Once the script is finished running, no popups appear in a watir webdriver opened window. (They will open if I click on the button while the script is running, but not after)
Based on the answer below, I am using:
begin
b = Watir::Browser.new :firefox
File.open('d:\\MARK.TXT', 'w') {|f| f.write(YAML.dump(b)) }
# Load MessageBox and wait here
b = YAML.load(File.read('d:\\MARK.TXT'))
ensure
if !b.nil?
b.close()
end
end
... but it currently allows for errors that can be ignored... I just don't know how wise it is to ignore them in the long run:
D:/Ruby193/lib/ruby/gems/1.9.1/gems/childprocess-0.5.6/lib/childprocess/windows/handle.rb:50:in `exit_code': The handle is invalid. (6) (ChildProcess::Error)
from D:/Ruby193/lib/ruby/gems/1.9.1/gems/childprocess-0.5.6/lib/childprocess/windows/process.rb:41:in `exited?'
from D:/Ruby193/lib/ruby/gems/1.9.1/gems/childprocess-0.5.6/lib/childprocess/abstract_process.rb:147:in `poll_for_exit'
from D:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.46.2/lib/selenium/webdriver/firefox/binary.rb:59:in `quit'
from D:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.46.2/lib/selenium/webdriver/firefox/launcher.rb:62:in `quit'
from D:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.46.2/lib/selenium/webdriver/firefox/bridge.rb:75:in `quit'
from D:/Ruby193/lib/ruby/gems/1.9.1/gems/selenium-webdriver-2.46.2/lib/selenium/webdriver/common/driver.rb:165:in `quit'
from D:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-webdriver-0.7.0/lib/watir-webdriver/browser.rb:136:in `close'
from D:/Users/risendevil/Documents/Aptana Studio 3 Workspace/Ruby Test/Default.rb:19:in `<main>'
Versions:
Firefox 38.0.5
selenium (0.2.11)
selenium-webdriver (2.46.2, 2.45.0)
watir-webdriver (0.7.0)
I learned something new answering your question: Turning an object into text is called serialization. Turning text into an object is called deserialization.
And here's a gist of you want to do, specifically.
The important part is
my_object = SomeObject.new
my_object.some_method # => returns your expected result
File.open('path/to/some.file', 'w') {|f| f.write(YAML.dump(my_object)) }
# Do whatever you want
my_object_reloaded = YAML.load(File.read('path/to/some.file'))
my_object_reloaded.some_method # => returns your expected result
You could even do this directly to your browser:
b = Watir::Browser.new
b.goto 'http://google.com' # => goes to Google
File.open('path/to/some.file', 'w') {|f| f.write(YAML.dump(b)) }
b = nil
# Do whatever you want, wait as long as you want.
# (Disclaimer: There are probably major limitations to 'as long as you want'.)
b = YAML.load(File.read('path/to/some.file'))
b.goto 'http://yahoo.com' # => goes to Yahoo
require "watir-webdriver"
l_Browser = Watir::Browser.new :firefox
l_Browser.goto "http://www.w3schools.com/js/tryit.asp?filename=tryjs_alert"
l_Browser.iframe(:id => 'iframeResult').button(:xpath => "//button[text()='Try it']").when_present.click # click on "Try it" button
l_Browser.alert.close # closing popup
I am trying to get familiar with the new ruby selenium-webdriver as it appears more intuitive mostly than the previous version of selenium and the ruby driver that went with it. Also, i had trouble getting the old selenium to work with ruby 1.9.1 in windows so I thought i'd look for an alternative.
So far i've done this with my script:
require "selenium-webdriver"
driver = Selenium::WebDriver.for :firefox
driver.get "https://example.com"
element = driver.find_element(:name, 'username')
element.send_keys "mwolfe"
element = driver.find_element(:name, 'password')
element.send_keys "mypass"
driver.find_element(:id, "sign-in-button").click
driver.find_element(:id,"menu-link-my_profile_professional_info").click
driver.find_element(:id,"add_education_btn").click
country_select = driver.find_element(:name, "address_country")
So basically I'm logging into my site and trying to add an education entry to my user profile.. I have a reference to a select box with options (in the country_select variable) and now i want to select an option with a given value.. I don't see how to do this in the new client.. The only thing I can think of doing is looping through all the options until I find the one I want, and then call execute_script:
http://selenium.googlecode.com/svn/trunk/docs/api/rb/Selenium/WebDriver/Driver.html#execute_script-class_method
method to set the selectedIndex.
Is there any other way to do this?
In the java api for selenium 2.0/webdriver here: http://seleniumhq.org/docs/09_webdriver.html
there is an example of doing this
Select select = new Select(driver.findElement(By.xpath("//select")));
select.deselectAll();
select.selectByVisibleText("Edam");
It doesn't appear that the ruby version has this feature though unless I'm missing something.
Any help would be appreciated.
Full disclosure here: I have absolutely no working knowledge of Ruby.
However, I'm pretty good with Selenium so I think I can help. I think what you're looking for is the select method. If ruby is anything like the other drivers you can use the select method to tell one of the options it is selected.
In pseudocode/java terms it would look something like this
WebElement select = driver.findElement(By.name("select"));
List<WebElement> options = select.findElements(By.tagName("option"));
for(WebElement option : options){
if(option.getText().equals("Name you want")) {
option.click();
break;
}
}
The Select object you have above is actually in a special Support package. It only exists for Java and .Net at the moment (Jan 2011)
Please note that none of the above will work anymore. Element#select and Element#toggle have been deprecated. You need to do something like:
my_select.click
my_select.find_elements( :tag_name => "option" ).find do |option|
option.text == value
end.click
I don't know what version Selenium this came about, but it looks like there is the Select class that pnewhook mentioned in Selenium 2.20
http://selenium.googlecode.com/svn-history/r15117/trunk/docs/api/rb/Selenium/WebDriver/Support/Select.html
option = Selenium::WebDriver::Support::Select.new(#driver.find_element(:xpath => "//select"))
option.select_by(:text, "Edam")
pnewhook got it but I'd like to post the ruby version here so everyone can see it:
require "selenium-webdriver"
driver = Selenium::WebDriver.for :firefox
driver.manage.timeouts.implicit_wait = 10
driver.get "https://example.com"
country_select = driver.find_element(:id=> "address_country")
options = country_select.find_elements(:tag_name=>"option")
options.each do |el|
if (el.attributes("value") == "USA")
el.click()
break
end
end
You can use XPath to avoid looping:
String nameYouWant = "Name you want";
WebElement select = driver.findElement(By.id(id));
WebElement option =
select.findElement(By.xpath("//option[contains(text(),'" + nameYouWant + "')]"));
option.click();
or
WebElement option =
select.findElement(By.xpath("//option[text()='" + nameYouWant + "']"));
require "selenium-webdriver"
webdriver = Selenium::WebDriver.for :firefox
driver.navigate.to url
dropdown = webdriver.find_element(:id,dropdownid)
return nil if dropdown.nil?
selected = dropdown.find_elements(:tag_name,"option").detect { |option| option.attribute('text').eql? value}
if selected.nil? then
puts "Can't find value in dropdown list"
else
selected.click
end
In my case this is only one working sample.
For the latest version of Webdriver (RC3) you should use "click()" instead of setSelected(). Also option.getText().equals("Name you want") should be used instead of option.getText()=="Name you want" in JAVA:
<!-- language: lang-java -->
WebElement select = driver.findElement(By.name("select"));
List<WebElement> options = select.findElements(By.tagName("option"));
for(WebElement option : options){
if(option.getText().equals("Name you want"){
option.click();
break;
}
}
Ruby Code with Example:
require "selenium-webdriver"
driver = Selenium::WebDriver.for :ie
driver.navigate.to "http://google.com"
a=driver.find_element(:link,'Advanced search')
a.click
a=driver.find_element(:name,'num')
options=a.find_elements(:tag_name=>"option")
options.each do |g|
if g.text == "20 results"
g.click
break
end
end
#SELECT FROM DROPDOWN IN RUBY USING SELENIUM WEBDRIVER
#AUTHOR:AYAN DATE:14 June 2012
require "rubygems"
require "selenium-webdriver"
begin
#driver = Selenium::WebDriver.for :firefox
#base_url = "http://www.yoururl.com"
#driver.manage.timeouts.implicit_wait = 30
#driver.get "http://www.yoursite.com"
#select Urugway as Country
Selenium::WebDriver::Support::Select.new(#driver.find_element(:id, "country")).select_by(:text, "Uruguay")
rescue Exception => e
puts e
#driver.quit
end
The easiest way I found was:
select_elem.find_element(:css, "option[value='some_value']").click