Capybara runs script twice without a reason - ruby

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 #######

Related

Using sleep in Ruby Watir tests

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?

Print/Export GUI interface at current state Ruby Shoes

Given the simple GUI posted down below, how can I create and save a screenshot of what the results of the code is? I don't care if I need to print it, export it to pdf, or save as a picture but I don't want to ask the user to screenshot or snip the page.
Shoes.app do
# Post and location of logo
#moveme = image 'C:\Users\warnerl\Documents\RubyFiles\TK_Dashboard_Logo.jpg', :width => 200
#moveme.move(400,0)
# Gets users first and last name and saves them as variables
#UserFirstName = ask("Please input your first name.")
#UserLastName = ask("Please input your last name.")
# Create Certificate
stack{
para 'You got 100%'
para "#{#UserFirstName} #{#UserLastName}"
para 'Congratulations you passed!'
}
end

Ruby shoes4 package over 1.5GB

I've made a small CLI script in ruby to manage a small shop for a friend, but then he wanted me to make a GUI for him, so I looked around and found shoes4.
So, I went and download it, created a small test, and run:
./bin/shoes -p swt:jar ./path/to/app.rb
and left it to create the package, then I got a warning from system that I'm running low on disc space, so I went to check the jar file, and it was over 1.5GB and still not done packaging... and the code is very small and basic:
require 'yaml'
Shoes.app do
button "Add client" do
filename = ask_open_file
para File.read(filename)
clients = YAML.load_file(filename)
id = clients[clients.length - 1][0].to_i + 1
name = ask("Enter the client's full name: ")
items = ask("Enter list of items.")
patients[id] = ["ID = "+ id.to_s,"Name = "+ pname,"list of items:\n"+ items]
File.open(filename, 'w') { |f| YAML.dump(clients, f) }
alert ("Added new patient.")
end
button "Exit" do
exit()
end
end
any idea why this small app is more than 1.5GB?? or did I try to package it wrong way??
The packager will include everything in the directory of your shoes script and below.

Selenium-Webdriver Ruby --> How to wait for images to be fully loaded after click

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

cucumber test problems with ajax and json even with ajax-wait-workaround

In a Rails coffee-script I have this click-handler:
addProductToCartHandler = ()->
$('#add_to_cart').click((event)->
$.ajax({
type: 'POST',
url: '/line_items.json'
data: {product_id:product_id}
success: (data)->
$('.carts_number_of_items').html(data.number_of_items)
$('#carts_explanation').effect("pulsate",{times:2}, 200)
error: ()->
});
event.preventDefault()
return false
)
This script invokes via $.ajax the following action:
def create
[...]
format.json{
#cart.save ?
( render json: {number_of_items:#cart.line_items.sum('quantity') } ) :
( render json: { error: t("line_items.error") } )
}
That calculates the number of items in a cart, and returns json-result. This works fine within browser, but doesn't with cucumber test:
#javascript
Scenario: Add a product to a cart via ajax control
And I follow "Cucumbers"
When I follow "Add To Cart"
And I wait for the ajax request to finish
Then I should see "1 item in your cart"
that fails with this report:
Then I should see "1 item in your cart" #features/step_definitions/web_steps.rb:24
expected there to be content "1 item in your cart" in [...]
0 item in your cart\n View Your Cart [...]
(RSpec::Expectations::ExpectationNotMetError)
The step "And I wait for the ajax request to finish" is:
When(/^I wait for the ajax request to finish$/) do
start_time = Time.now
page.evaluate_script( 'jQuery.isReady&&jQuery.active==0' ).
class.should_not eql(String)
until page.evaluate_script('jQuery.isReady&&jQuery.active==0') or
(start_time + 5.seconds) < Time.now do
sleep 1
end
end
Any suggestions?
I'm sure you already figured this out since it was 18 months ago, thoughts for peeps having a similar problem::
Errored out for some reason (in which case you could use 'save_and_open_page' to check the content or look at page.driver.console_messages to see if there was a JS error)
Took more than 5 seconds (in which case you could change your 5.seconds to something much larger to see if it works)
jQuery alias not working? (I see you're using $ - does jQuery.isReady&&jQuery.active==0 return in your console?)
Do you remember what it was?

Resources