A few years back now I used Watir to automate a batch of queries against a horribly archaic ColdFusion intranet site. I used Watir as this site absolutely required Internet Explorer and so Mechanize, my preferred port of call, was not an option. The intranet site in question would helpfully popup not just one, but two, confirmation windows to let you know a query had failed to find a result, that then had to be manually clicked (initially, at least) through in order for the script to continue with the next query.
At the time (Ruby 1.8.X, Windows XP, IE6) the best option (for me) for handling these popups in an automated fashion was using AutoItx3:
#Be careful as actually targets whatever application is in front. Need to be hands off with this. But DOES WORK.
def check_for_popups # Number 2 from http://wiki.openqa.org/display/WTR/JavaScript+Pop+Ups
autoit = WIN32OLE.new('AutoItX3.Control')
#
# Do forever - assumes popups could occur anywhere/anytime in your application.
loop do
# Look for window with given title. Give up after 1 second.
ret = autoit.WinWait('Message from webpage', '', 1)
#
# If window found, send appropriate keystroke (e.g. {enter}, {Y}, {N}).
if (ret==1) then autoit.Send('{enter}') end
#
# Take a rest to avoid chewing up cycles and give another thread a go.
# Then resume the loop.
sleep(1)
end
end
and running it inside its own thread:
$popup = Thread.new { check_for_popups } # start popup handler
at_exit { Thread.kill($popup) } # kill thread on exit of main application
And this all made sense. Of sorts: Running the popup handler in its own thread behaved exactly as I thought it would and no magic was required.
Sometime later (Ruby 1.9.X, still Windows XP, IE?) and I came across a much easier way of doing things using click_no_wait
and javascript_dialog
:
ie.cell(:id, "findButton").click_no_wait #no_wait so can kill js popup
sleep 1 #Otherwise it will think search is already complete!
#Then do our own manual waiting loop to deal with the popup
until (ie.text_field(:name, "timeTitle").value == "Search Time =") do
sleep 1
#Check for existance of popup
if ie.javascript_dialog.exists?
ie.javascript_dialog.button('OK').click
end
end
This was better, not in that it didn’t require the use of threading, rather that it would target only what it was meant to and not just any window of any application that popped to the front. The use of click_no_wait
ensured that Watir didn’t get stuck waiting if a popup appeared and then I just had to monitor the webpage for a change in a text field to know whether the search had finished or not.
Present day (Ruby 2.0.X, Windows 7, IE8) and Watir has evolved a lot since I’d first used it! For whatever reason, click_no_wait
no longer works (doesn’t actually ‘click’); arguably the cleanest technique I’d since found, overiding the javascript function, also doesn’t work; javascript_dialog
no longer exists; however, there is now a built in Alert class for dealing with the popups (that automatically looks for windows titled such as “Message from webpage”), but because of the two popup windows in quick succession I had to monkey patch the Alert class so I could have a close_no_wait
version to avoid it getting stuck after closing the first:
module Watir
class Alert
def close_no_wait
dialog.close
end
end
end
Then, using the same approach as I’d used initially, I set up a method which used this to close the popups, which I could call from within a thread:
def check_for_popups
loop do
if IE.alert.exists?
IE.alert.close_no_wait
end
end
end
However, this didn’t work; backtracing ultimately lead to a method_missing
error, i.e it didn’t know anything about close_no_wait
, but it did understand about the higher level object; puts IE.methods
worked. After lots of investigation and playing about in irb I found by accident that if I ran the function once first it’d then work in a separate thread.
So ended up making the function like so (pay special attention to my excellent begin and rescue debugging):
def check_for_popups
loop do
begin
puts "begin"
if IE.alert.exists?
IE.alert.close_no_wait
end
rescue => exception
puts "rescue"
puts exception.backtrace
end
sleep 1
#Default number of threads seems to be 2 from a script. In IRB it's 1
#Weirdly this some how ends up at 4 threads.
puts Thread.list.length
#So it quits itself if not running within a thread
if Thread.list.length <= 2
break
end
end
end
This uses Thread.list.length
to determine whether the method is running inside a thread or not, because if it isn’t then I don’t want it running in a loop. This meant I could call it once as a dummy in main program and then call it again in a thread as intended:
#Need to call the check_for_popups first so it'll work in the thread I DO NOT understand why
check_for_popups
#Then ok to start thread
$popup = Thread.new { check_for_popups } # start popup handler
So this works, but I really don’t understand why it’s needed. I first thought it might have been something to do with monkey patching, but it’s not. I wonder if it’s an oddball Watir thing? Or a Windows’ Ruby thing? As I’ve not been able to reproduce the error using similar techniques on a different platform (obviously not with Watir-classic though).