My Watir acceptance tests tend to fail in cases where I don't use Ruby's sleep function. Here is an example:
it 'Edit case: should edit display name of TEST' do
all_row = #browser.trs(:css => ".ui-sortable-data-row")
# note: this displays a detail view of the selected item of a table
all_row[0].td.fire_event :click
# click on edit button (activate edit mode)
#sleep 1
#browser.span(:css => "p-header a > span.fa-pen").fire_event :click
# set name value
#browser.text_field(:id => "name").set("test name")
# ...
end
If the sleep 1 (sleep for 1 second) function call is missing, the test will fail with the following message:
Failure/Error: #browser.text_field(:id => "name").set("test name")
Watir::Exception::ObjectDisabledException:
element present, but timed out after 10 seconds, waiting for #<Watir::TextField: located: true; {:id=>"name", :tag_name=>"input"}> to be enabled
However, if I explicitly call sleep 1, the text field ('name') is enabled and can be set.
So my question is: Is using the sleep function a good practice or is there a better way to avoid these problems?
Related
I am dealing with Capybara + Ruby script and have an issue with it. When I run the script, it clicks buttons, fulfills the fields, and saves results. After execution, it starts to run again.
The script starts by reaching the specific page. On this page, there is the Start new questionnaire button which has to be clicked, and then all the main processes happen. After clicking the Apply rating button, the browser automatically redirects to the page where Start new questionnaire appears again (that's OK). But I don't need it to be clicked the second time. I need my script to continue the execution.
What is the reason my script is trying to run 2 times one part of the code?
content_rating = browser.find_link(title: 'Content rating')
content_rating.click
puts 'Content Rating'
begin
start_new_questionary_btn = browser.all(:button, text: 'Start new questionnaire')
start_new_questionary_btn[0].click
sleep 2
rescue
continue_btn = browser.all(:button, text: 'Continue')
continue_btn[0].click
end
email_address = browser.all(:label, "Email address")
email_address[0].fill_in(type: 'email', with: 'e-mail', wait: 100)
email_address[1].fill_in(type: 'email', with: 'e-mail', wait: 100)
content_questions = browser.all(:xpath, './/div[#class="iarcPageOne"]')
content_questions[2].click
##All the buttons have costant names
btn_names = ['775', '789', '817', '805', '1036', '1017', '1018', '1019']
for btn_name in btn_names
current_btn = browser.all(:radio_button, name: btn_name, visible: :all)
if btn_name == '1036'
current_btn[0].click
else
current_btn[1].click
end
end
browser.click_on('Save questionnaire', wait: 100)
browser.click_on('Calculate rating', wait: 100)
browser.click_on('Apply rating', wait: 100)
sleep 3
### Now the code should continue to execute and click ```App content``` (below) but it clicks ```Start new questionnaire``` again ###
### App content
app_content = browser.find_link(title: 'App content')
app_content.click
# It skips next two lines and continues with Privacy policy URL
# If I run the code below separately - everything works perfect
start_buttons = browser.all(:button, text: 'Start')
start_buttons[0].click
privacy_button = browser.find_field('Privacy policy URL', type: 'text')
privacy_button.fill_in(with: 'privacy')
sleep 1
browser.click_on('Save')
####### Continuation of the script #######
It isn't clicking it again - my guess is there a validation error on the page somewhere and it's opening the same questionnaire again for you to fix the error. Capybara only does what you tell it to, it's not going to start randomly clicking links.
Also when writing scripts like this there is no need to separate the finds and clicks, prefer something like below
browser.click_link(title: 'Content rating')
puts 'Content Rating'
begin
start_new_questionary_btn = browser.first(:button, text: 'Start new questionnaire').click
sleep 2
rescue
browser.first(:button, text: 'Continue').click
end
email_address = browser.all(:label, "Email address")
email_address[0].fill_in(type: 'email', with: 'e-mail', wait: 100)
email_address[1].fill_in(type: 'email', with: 'e-mail', wait: 100)
browser.all(:xpath, 'div.iarcPageOne', minimum: 3]')[2].click
##All the buttons have costant names
['775', '789', '817', '805', '1036', '1017', '1018', '1019'].each do |btn_name|
current_btn = browser.all(:radio_button, name: btn_name, visible: :all)
current_btn(btn_name == '1036' ? 0 : 1).click
end
browser.using_wait_time(100) do
browser.click_on('Save questionnaire')
browser.click_on('Calculate rating')
browser.click_on('Apply rating')
end
sleep 3
### Now the code should continue to execute and click ```App content``` (below) but it clicks ```Start new questionnaire``` again ###
### App content
browser.click_link(title: 'App content')
####### Continuation of the script #######
i have a problem in my ruby watir script.
I want to click through all next pages until the last page, and then puts some first name and last name. I know that the last "next" link is called with one more class "disabled" stop = b.link(class: 'next-pagination page-link disabled').
I try to loop until this classes is reached break if stop.exists?
loop do
link = b.link(class: 'next-pagination page-link')
name_array = b.divs(class: 'name-and-badge-container').map { |e| e.div(class:'name-container').link(class: 'name-link profile-link').text.split("\n") }
puts name_array
stop = b.link(class: 'next-pagination page-link disabled')
break if stop.exists?
link.click
end
I have this error :
This code has slept for the duration of the default timeout waiting for an Element to exist. If the test is still passing, consider using Element#exists? instead of rescuing UnknownObjectException
/Users/vincentcheloudiakoff/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/watir-6.2.1/lib/watir/elements/element.rb:496:in rescue in wait_for_exists': timed out after 30 seconds, waiting for #<Watir::Div: located: false; {:class=>"name-and-badge-container", :tag_name=>"div", :index=>13}> to be located (Watir::Exception::UnknownObjectException)
from /Users/vincentcheloudiakoff/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/watir-6.2.1/lib/watir/elements/element.rb:486:inwait_for_exists'
from /Users/vincentcheloudiakoff/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/watir-6.2.1/lib/watir/elements/element.rb:487:in wait_for_exists'
from /Users/vincentcheloudiakoff/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/watir-6.2.1/lib/watir/elements/element.rb:487:inwait_for_exists'
from /Users/vincentcheloudiakoff/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/watir-6.2.1/lib/watir/elements/element.rb:639:in element_call'
from /Users/vincentcheloudiakoff/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/watir-6.2.1/lib/watir/elements/element.rb:91:intext'
from /Users/vincentcheloudiakoff/Travail/Automation/lib/linkedin.rb:24:in block (2 levels) in start'
from /Users/vincentcheloudiakoff/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/watir-6.2.1/lib/watir/element_collection.rb:28:ineach'
from /Users/vincentcheloudiakoff/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/watir-6.2.1/lib/watir/element_collection.rb:28:in each'
from /Users/vincentcheloudiakoff/Travail/Automation/lib/linkedin.rb:24:inmap'
from /Users/vincentcheloudiakoff/Travail/Automation/lib/linkedin.rb:24:in block in start'
from /Users/vincentcheloudiakoff/Travail/Automation/lib/linkedin.rb:22:inloop'
from /Users/vincentcheloudiakoff/Travail/Automation/lib/linkedin.rb:22:in start'
from start.rb:3:in'
It clicks on the next page, but does not find the next disabled button.
Use the text to locate that element
b.span(text: 'Suivant').click
You don't have to use parent link and then span like b.link().span() instead you can directly locate span the way I have explained.
I wrote a basic program to test the ruby metriks gem
require 'metriks'
require 'metriks/reporter/logger'
#registry = Metriks::Registry.new
#logger = Logger.new('/tmp/metrics.log')
#reporter = Metriks::Reporter::Logger.new(:logger => #logger)
#reporter.start
#registry.meter('tasks').mark
print "Hello"
#registry.meter('tasks').mark
#reporter.stop
After i execute the program, there is nothing in the log other than it got created.
$ cat /tmp/metrics.log
# Logfile created on 2015-06-15 14:23:40 -0700 by logger.rb/44203
You should either pass in your own registry while instantiating Metriks::Reporter::Logger or use the deafult registry (Metrics::Resgitry.default) if you are using a logger to log metrics.
Also the default log write interval is 60 seconds, your code completes before that so even if everything is setup okay it won't get recorded. So, since you want to use your own registry, this should work for you (I'm adding a little sleep since I'm gonna use an interval of 1 second) :
require 'metriks'
require 'metriks/reporter/logger'
#registry = Metriks::Registry.new
#logger = Logger.new('/tmp/metrics.log')
#reporter = Metriks::Reporter::Logger.new(:logger => #logger,
:registry => #registry
:interval => 1)
#reporter.start
#registry.meter('tasks').mark
print "Hello"
#registry.meter('tasks').mark
# Just giving it a little time so the metrics will be recorded.
sleep 2
#reporter.stop
But I don't really think short intervals are good.
UPDATE : Also I think #reporter.write will help you write down the logs instantly regardless of the time interval. So you don't have to use sleep (better).
How do we make some_element.present? or some_element.visible? wait for less than 5 secs.? Because I think some_element.present? alone will wait for default value of 30 secs before timing out.
Thanks
The Element#present? (and Element#visible? and Element#exists?) method does not wait at all. You can see this by checking the time before and after attempting to locate an element that is not present:
puts Time.now
#=> 2014-07-31 22:14:08 -0400
puts browser.element(id: 'does_not_exist').present?
#=> false
puts Time.now
#=> 2014-07-31 22:14:08 -0400
As you can see, the time before and after checking the prescence of the element is a negligible amount.
It should be noted that the above was executed against a tiny page. For a very large page, which would require more inspection, the method could take longer to execute. However, that would be an issue of execution time rather than being Watir is actually waiting.
I believe you are asking how to shorten the length of time before timeout, by default its set to 30 seconds, see below on how to customize that time.
According to http://watirwebdriver.com/waiting/
Explicit waits
There are four built in methods that you can use to make your waiting experience more pleasant (and remove those evil sleep statements from your code)
Watir::Wait.until { ... }: where you can wait for a block to be true
object.when_present.set: where you can do something when it’s present
object.wait_until_present:; where you just wait until something is present
object.wait_while_present:; where you just wait until something disappears
The default timeout for all these methods is 30 seconds, but your can pass an argument to any of these to increase (or decrease) it as needed.
and http://rdoc.info/gems/watir-webdriver/Watir/EventuallyPresent
- (Object) wait_until_present(timeout = nil)
Waits until the element is present.
Examples:
browser.button(:id => 'foo').wait_until_present
Parameters:
timeout (Fixnum) (defaults to: nil) — seconds to wait before timing out
- (Object) wait_while_present(timeout = nil)
Waits while the element is present.
Examples:
browser.button(:id => 'foo').wait_while_present
Parameters:
timeout (Integer) (defaults to: nil) — seconds to wait before timing out
- (Object) when_present(timeout = nil)
Waits until the element is present.
Examples:
browser.button(:id => 'foo').when_present.click
browser.div(:id => 'bar').when_present { |div| ... }
browser.p(:id => 'baz').when_present(60).text
Parameters:
timeout (Fixnum) (defaults to: nil) — seconds to wait before timing out
I am very new to Ruby and Selenium-Webdriver, so please, help :)
I am trying to open email campaign , sent to my inbox, that has images and take a screenshot in the firefox. But i can not make it wait until images is fully loaded. Once i click on 'Show images' , screenshot is already taken , but image is not loaded at that time. How can i pause the script and take screenshot some time later, after all images is displayed?
Please, help :(
Bellow is my script:
enter code here
require 'selenium-webdriver'
browser = Selenium::WebDriver.for :firefox
#==========================================================================================
wait = browser.manage.timeouts.implicit_wait = 15
#==========================================================================================
url = 'https://login.yahoo.com/config/login_verify2?.intl=us&.src=ym'
# Open browser (firefox)
browser.navigate.to url
browser.find_element(:id, 'username').send_keys "some yahoo id"
browser.find_element(:id, 'passwd').send_key "some password"
browser.find_element(:id, ".save").click
browser.find_element(:id, "inbox-label").click
browser.find_element(:xpath, "//div[#class='subj']").click
browser.find_element(:xpath, "//a[#title='Display blocked images']").click
result_page_title = browser.find_element(:tag_name, 'title')
puts "Title of the page: \t\t: #{result_page_title.text}"
browser.save_screenshot "1.jpg"
You can use Implicit Wait and Explicit Wait to wait for a particular Web Element until it appears in the page. The wait period you can define and that is depends upon the application.
Explicit Wait:
An explicit waits is code you define to wait for a certain condition to occur before proceeding further in the code. If the condition achieved it will terminate the wait and proceed the further steps.
Code:
WebDriverWait wait = new WebDriverWait(driver,30);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.id(strEdit)));
Or
WebElement myDynamicElement = (new WebDriverWait(driver, 30))
.until(new ExpectedCondition<WebElement>(){
#Override
public WebElement apply(WebDriver d) {
return d.findElement(By.id("myDynamicElement"));
}});
This waits up to 30 seconds before throwing a TimeoutException or if it finds the element will return it in 0 - 30 seconds. WebDriverWait by default calls the ExpectedCondition every 500 milliseconds until it returns successfully. A successful return is for ExpectedCondition type is Boolean return true or not null return value for all other ExpectedCondition types.
You can use ExpectedConditions class as you need for the application.
Implicit Wait:
An implicit wait is to tell WebDriver to poll the DOM for a certain amount of time when trying to find an element or elements if they are not immediately available
Code:
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
One thing to keep in mind is that once the implicit wait is set - it will remain for the life of the WebDriver object instance
For more info use this link http://seleniumhq.org/docs/04_webdriver_advanced.jsp
The above code is in Java. Change as your language need.
Ruby code from the docs (click on the 'ruby' button):
wait = Selenium::WebDriver::Wait.new(:timeout => 10) # seconds
begin
element = wait.until { driver.find_element(:id => "some-dynamic-element") }
ensure
driver.quit
end
Which works for me
To add to the above answer, here is how I use implicit and explicit wait in Ruby.
Implicit Wait
I pass this option to Selenium::WebDriver after initializing with a couple of lines like this:
browser = Selenium::WebDriver.for :firefox
browser.manage.timeouts.implicit_wait = 10
Just replace "10" with the number of seconds you'd like the browser to wait for page refreshes and other such events.
Explicit Wait
There are two steps to declaring an explicit wait in Selenium. First you set the timeout period by declaring a wait object, and then you invoke the wait with Selenium::Webdriver's .until method. It would look something like this, in your example:
wait = Selenium::WebDriver::Wait.new(:timeout => 10)
wait.until { browser.find_element(:xpath, "//path/to/picture").displayed? }
This would tell the Webdriver to wait a maximum of 10 seconds for the picture element to be displayed. You can also use .enabled? if the element you're waiting for is an interactive element - this is especially useful when you're working with Ajax-based input forms.
You can also declare an explicit wait period at the start of your script, and then reference the object again whenever you need it. There's no need to redeclare it unless you want to set a new timeout. Personally, I like to keep the wait.until wrapped in a method, because I know I'm going to reference it repeatedly. Something like:
def wait_for_element_present( how_long=5, how, what )
wait_for_it = Selenium::WebDriver::Wait.new(:timeout => how_long )
wait_for_it.until { #browser.find_element(how, what) }
end
(I find it's easier to just declare browser as an instance variable so that you don't have to pass it to the method each time, but that part's up to you, I guess?)
ExpectedConditions isn't supported yet in the Ruby Selenium bindings. This snippet below does the same thing as ExpectedConditions.elementToBeClickable — clickable just means "visible" and "enabled".
element = wait_for_clickable_element(:xpath => xpath)
def wait_for_clickable_element(locator)
wait = Selenium::WebDriver::Wait.new(:timeout => 10)
element = wait.until { #driver.find_element(locator) }
wait.until { element.displayed? }
wait.until { element.enabled? }
return element
end