I wrote this script a couple months ago and it has been working great. However, had an issue last night that the page never loads and just spins forever. My script was just timing out. I need to add code to test if the page fully loads within 30 second and if not to exit with a status of 2 and a proper message. Here is the code I have:
#-------------------------------------------------------------#
# Watir Login to XXXX SSO Site
# Written 2017-09-17 for ITC by Jim Clark
#-------------------------------------------------------------#
# the Watir controller
require "watir"
# set a variable
#test_site = "https://oracle.pomeroy.com"
test_site = "http://xxx.xxx"
# open a browser
browser = Watir::Browser.new :phantomjs
#puts "Beginning of test: XXXX SSO Login."
#puts " Step 1: go to the test site: " + test_site
browser.goto test_site
# Test if page fully loaded after 30 seconds, if not, exit with proper status and message
# validate login site loads
if browser.text.include? "Username"
#puts " Step 2: enter username and password text fields."
browser.text_field(:name, "username").set "xxxx"
browser.text_field(:name, "password").set "xxxx"
#puts " Step 3: click login"
browser.button(:text, "Login").click
if browser.text.include? "Logged In As XXXX"
puts "OK: Test Passed. Login worked!"
status = 0
else
puts "CRITICAL: Open a SEV1 - Test login failed!"
status = 2
end
else
puts "CRITICAL: Open a SEV1 - Could not find login page!"
status = 2
end
browser.close
exit status
I have just spent the last 3 hours searching and reading and tried about 20 different things and nothing I did seemed to work.
So while the wait methods have a default timeout, settable thusly: Watir.default_timeout = 30 it appears there is no such setting in Watir itself for the the goto method. However since this is just calling the webdriver navigate.to method, and there IS a timeout settable in webdriver for that, then you can set that using watir's .driver method prior to trying to goto the page. You should be able set that as follows
b.driver.manage.timeouts.page_load = 30
b.goto
In your case since it's the initial load that is failing this ought to work for you, and if you don't like the default error message, you could rescue and provide your own.
In most cases I've seen however, the delays are often with REST services that are slow or overloaded, so just setting that prior to your goto will most often not be sufficient if the delays are with subsequent AJAX calls. What is checked when you use goto is the browser status, which will be 'ready' once the initial page content has been loaded, and often that happens pretty quickly as these days that content is often all static, cached and rapidly served by the webserver. Basically that is the time for loading a web 1.0 web-page.
Typically modern 'web 2.0' pages have a LOT of scripts and AJAX/REST style interactions that are usually necessary for all the page content to appear. It's not uncommon for the browser to report 'ready' when the page is mostly blank as the remaining contents are pulled in via REST or GRAPHQL requests. There is not unfortunately a standard way to tell if the browser is busy making further requests due to scripts.
The most standard thing to do is wait a given amount of time for a specific condition to be true, usually something to appear on the page that is one of the last things loaded/rendered. The wait methods take timeout parameterss, so you could do something along these lines
b.goto 'www.mysite.com'
Watir::Wait.until(timeout=30) { browser.text_field(name: 'username').exists? }
The Watir::Wait.until method (rubydocs here) also accepts a custom message if you want to provide one to make it easier to know what timed out when it fails.
Related
I'm writing a script that registers users for me, but the website got a lot of junk loading (like statistic urls that has to load in etc) so the script is really slow cause its waiting for the site to fully load even if all the elements needed already are loaded, is it possible to disable this wait time=? it would make my script like 10 sec faster.
The waiting for the page to load is controlled by the page load strategy. By default, it is set to "normal", which waits for the document readiness state to be "complete". You can set the strategy to "none" to remove the waiting. Some of the browsers/drivers also support an "eager" strategy that waits for the browser to be in the "interactive" state.
require 'webdrivers'
require 'watir'
browser = Watir::Browser.new :chrome, page_load_strategy: 'none'
browser.goto 'www.google.com'
p browser.ready_state
#=> "loading"
See https://w3c.github.io/webdriver/#navigation for more details.
I have rspec tests and I feel there is a bug with puffing billy or maybe I am misunderstanding the use of whitelists in the settings.
Basically the test is about checking that, if a third party hosted image (on a service called Cloudinary) takes time to be downloaded by a page, then the user sees a loading spinner and after the image is finally loaded the spinner disappears and is not visible anymore.
I'm sure billy is the root cause as
if I don't use puffing billy (by removing billy: true) to stub/fake a 10 seconds response delay for the third party image, as you might guess, I see the image (using save_and_open_page to make sure) too soon and my test can't be implemented.
if I use billy by using billy: true, then the image NEVER appears (checked it by save_and_open_page) and the spinner keeps turning as the actual stubbed image never shows up , it's like it was "blocked" by puffing billy... also making the test not doable :(
Puffing billy settings
Capybara.configure do |config|
# we must force the value of the capybara server port. We have to do that because puffing-billy
# saves everything locally for a specific host and port. But, capybara starts your rack application
# on a new port each time you run your tests. The consequence is puffing-billy
# is not able to reuse the created cache files and tries to do all the API call again.
# source - kevin.disneur.me/archives/2015-03-05-end-to-end-tests-in-javascript.html
config.server_port = 60001
# source: comments in coderwall.com/p/jsutlq, enable to load save_and_open page with css and js
config.asset_host = 'http://localhost:3000'
end
require 'billy/capybara/rspec'
Billy.configure do |c|
c.cache = true
c.cache_request_headers = false
c.persist_cache = true
c.non_successful_cache_disabled = true
c.non_successful_error_level = :warn
c.whitelist = ['localhost', '127.0.0.1', 'https://res.cloudinary.com']
c.ignore_cache_port = true
c.cache_path = "spec/support/http_cache/billy/"
# Only set non_whitelisted_requests_disabled **temporarily**
# to false when first recording a 3rd party interaction. After
# the recording has been stored to cache_path, then set
# non_whitelisted_requests_disabled back to true.
c.non_whitelisted_requests_disabled = true
end
The rspec test:
context "non signed in visitor", js: true, billy: true do
describe "Spinner shows while waiting then disappears" do
it "should work" doproxy.stub("https://res.cloudinary.com/demo/image/upload/sample.jpg").and_return(
Proc.new { |params, headers, body|
sleep 10
{code: 200}
}
)
visit actual_deal_page_path(deal)
# detect spinner
expect(page).to have_css('div#fullPageLoadingSpinner', visible: :visible)
# check subsequent image elements not yet visible
expect(page).to have_no_css("img[src*='https://res.cloudinary.com/demo/image/upload/sample.jpg']")
# then after 15 seconds, the cloudinary image finally is loaded and
# the spinner disappears
sleep 15
expect(page).to have_css('div#fullPageLoadingSpinner', visible: :hidden)
expect(page).to have_css("img[src*='https://res.cloudinary.com/demo/image/upload/sample.jpg']")
end
end
end
The image in the view
<section
id="introImg"
<img src="https://res.cloudinary.com/demo/image/upload/sample.jpg" class="cld-responsive deal-page-bckdImgCover"
</section>
I have tried different variations on the settings, tried also to change https//res.cloudinary to res.cloudinary.com...nothing works
Finally, I think it matters, I keep seeing in my tests logs:
puffing-billy: CACHE KEY for 'https://res.cloudinary.com:443/demo/image/upload/sample.jpg' is 'get_res.cloudinary.com_2c4fefdac8978387ee341535c534e21e2588ed76'
puffing-billy: Connection to https://res.cloudinary.com:443/demo/image/upload/sample.jpg not cached and new http connections are disabled
puffing-billy: CACHE KEY for 'https://res.cloudinary.com:443/demo/image/upload/sample.jpg' is 'get_res.cloudinary.com_2c4fefdac8978387ee341535c534e21e2588ed76'
puffing-billy: Connection to https://res.cloudinary.com:443/demo/image/upload/sample.jpg not cached and new http connections are disabled
puffing-billy: CACHE KEY for 'https://res.cloudinary.com:443/demo/image/upload/sample.jpg' is 'get_res.cloudinary.com_2c4fefdac8978387ee341535c534e21e2588ed76'
puffing-billy: Connection to https://res.cloudinary.com:443/demo/image/upload/sample.jpg not cached and new http connections are disabled
puffing-billy: CACHE KEY for 'https://res.cloudinary.com:443/demo/image/upload/sample.jpg' is 'get_res.cloudinary.com_2c4fefdac8978387ee341535c534e21e2588ed76'
puffing-billy: Connection to https://res.cloudinary.com:443/demo/image/upload/sample.jpg not cached and new http connections are disabled
On these Test logs, first I don't quite understand why there are so many lines for the same resource and second, what does this message mean "not cached and new http connections are disabled". I tried other tickets with similar sounding issues such as #104 or #179 or, but my bug might be different...
We have a website running in Windows Server 2008 + SQLServer 2008 + Ruby + Sinatra + Sequel/Puma
We've developed an API for our website.
When the access points are requested by many clients, at the same time, the clients start getting RequestTimeout exceptions.
I investigated a bit, and I noted that Puma is managing multi threading fine.
But Sequel (or any layer below Sequel) is processing one query at time, even if they came from different clients.
In fact, the RequestTimeout exceptions don't occur if I launch many web servers, each one listening one different port, and I assign one different port to each client.
I don't know yet if the problem is Sequel, ADO, ODBC, Windows, SQLServer or what.
The truth is that I cannot switch to any other technology (like TinyTDS)
Bellow is a little piece of code with screenshots that you can use to replicate the bug:
require 'sinatra'
require 'sequel'
CONNECTION_STRING =
"Driver={SQL Server};Server=.\\SQLEXPRESS;" +
"Trusted_Connection=no;" +
"Database=pulqui;Uid=;Pwd=;"
DB = Sequel.ado(:conn_string=>CONNECTION_STRING)
enable :sessions
configure { set :server, :puma }
set :public_folder, './public/'
set :bind, '0.0.0.0'
get '/delaybyquery.json' do
tid = params[:tid].to_s
begin
puts "(track-id=#{tid}).starting access point"
q = "select p1.* from liprofile p1, liprofile p2, liprofile p3, liprofile p4, liprofile p5"
DB[q].each { |row| # this query should takes a lot of time
puts row[:id]
}
puts "(track-id=#{tid}).done!"
rescue=>e
puts "(track-id=#{tid}).error:#{e.to_s}"
end
end
get '/delaybycode.json' do
tid = params[:tid].to_s
begin
puts "(track-id=#{tid}).starting access point"
sleep(30)
puts "(track-id=#{tid}).done!"
rescue=>e
puts "(track-id=#{tid}).error:#{e.to_s}"
end
end
There are 2 access points in the code above:
delaybyquery.json, that generates a delay by joining the same table 5
times. Note that the table must be about 1000 rows in order to get the
query working really slow; and
delaybycode.json, that generates a delay by just calling the ruby sleep
function.
Both access points receives a tid (tracking-id) parameter, and both write the
outout in the CMD, so you can follow the activity of both process in the same
window and check which access point is blocking incoming requests from other
browsers.
For testing I'm opening 2 tabs in the same chrome browser.
Below are the 2 testings that I'm performing.
Step #1: Run the webserver
c:\source\pulqui>ruby example.app.rb -p 81
I get the output below
Step #2: Testing Delay by Code
I called to this URL:
127.0.0.1:81/delaybycode.json?tid=123
and 5 seconds later I called this other URL
127.0.0.1:81/delaybycode.json?tid=456
Below is the output, where you can see that both calls are working in parallel
click here to see the screenshot
Step #3: Testing Delay by Query
I called to this URL:
127.0.0.1:81/delaybyquery.json?tid=123
and 5 seconds later I called this other URL
127.0.0.1:81/delaybyquery.json?tid=456
Below is the output, where you can see that calls are working 1 at time.
Each call to an access point is finishing with a query timeout exception.
click here to see the screenshot
This is almost assuredly due to win32ole (the driver that Sequel's ado adapter uses). It probably doesn't release the GVL during queries, which would cause the issues you are seeing.
If you cannot switch to TinyTDS or switch to JRuby, then your only option if you want concurrent queries is to run separate webserver processes, and have a reverse proxy server dispatch requests to them.
I have a page that sometimes loads in over a minute. Assume this is the expected behavior and wont change. In these cases, I get Net::ReadTimeout.
Note that this is after navigating to a page by clicking a button on the previous page, not an ajax request. Therefore Capybara.using_wait_time doesn't help.
I have tried a number of radical things (some of which I knew wouldn't work) like:
Setting page.driver.browser.manage.timeouts's implicit_wait, script_timeout and page_load.
Looping through the entire object space and setting all Selenium::WebDriver::Remote::Http::Default's timeout value.
Looping through the entire object space and setting all
Net::HTTP's read_timeout.
page.driver.browser.send(:bridge).http.instance_variable_get(:#http).read_timeout=
None seem to work. This should be very trivial, still I couldn't find a way to do it.
If you know of a webdriver agnostic solution that would be great. If not - I am using selenium.
Selenium has a lot of different timeout settings, some of which can be changed at runtime, others which have to be set when the driver is initialized. You are most likely running into the Http::Default timeout which defaults to 60 seconds. You can override this by passing your own instance into the Selenium driver as http_client
Capybara.register_driver :slow_selenium do |app|
client = Selenium::WebDriver::Remote::Http::Default.new
client.timeout = 120
Capybara::Selenium::Driver.new(app, http_client: client)
end
and then use the :slow_selenium driver for tests which will take over a minute to load the page
I'm using the Selenium WebDriver and Ruby to perform some automation and I ran into a problem of a captcha around step 3 of a 5 step process.
I'm throwing all the automation in a rake script so I'm wondering is there a command to pause or break the script running temporarily until I enter data into the captcha and then continue running on the next page.
To build on seleniumnewbie's answer and assuming that you have access to the console the script is running on:
print "Enter Captcha"
captchaTxt = gets.chomp
yourCaptchaInputWebdriverElement.send_keys captchaTxt
If you just want to pause and enter the captcha in your browser, you can just have it prompt at the console to do that very thing and it'll just sit there.
print "Enter the captcha in your browser"
gets
You could also set the implicit wait to decently long period of time so that Selenium would automatically see the next page and move out. However, this would leave the important Captcha step (for documenting / processes sake) out of your test unless you're pretty anal with your commenting.
Since this is an actively participating test requiring user input I would say that making the tester press "enter" on the console is the way to go.
Since you are writing the test in a script, all you need to do is add a sleep in your test i.e. 'sleep 100' for example.
However, it is bad to add arbitrary sleeps in tests. You can also do something like "Wait for title 'foo'" where 'foo' is the title of the page in Step 4. It need not be title, it can be anything, but you get the idea. Wait for something semantic which indicates that step 3 is done and step 4 is ready to start.
This way, its more targeted wait.
This has been implemented in JAVA, but similar technique.Your solution could be found here