Here's the way I've found to control IE popups (asking for
certificates, asking whether to remember passwords, etc.) on Windows
Vista with Watir 1.5.1.1166 and nothing else:
1. Just before sending the click that will generate the popup, popen
() a subprocess ("the watcher").
2. Have the watcher spin, checking for a window with a given title,
then send the right keys to dismiss the window. After that, create a
file to signal completion and exit.
3. Have the parent process spin, waiting for that file to be created,
then continue.
I've not succeeded in doing it a simpler way because:
1. Putting the click() and an @autoit.WinWait in different threads
always seems to lead to a hang. (I'm guessing I/O is happening when
Watir is waiting for the click to finish, so the thread that wants to
WinWait never gets started. Or the WinWait thread causes the click
thread never to be started.)
2. Windows doesn't support fork() and derivatives, so I can't just
fork a watcher, click, and wait for the child to terminate.
3. I can't even have the watcher send the "I'm done" message to its
stdout because the parent hangs reading it, presumably for the same
reason as threads do.
My question is whether I've missed an easier or better way to do this
using stock Watir. I'm assuming that if I dug into Win32api, I could
find the secret to how Windows lets one process create and control
another, but I find myself strangely without the desire to do so. But
if someone tells me it's easy...
---------------------------
Here is a sample script that logs in and deals with a demand for a
digital certificate. Because I want to be cool, I made a little
Domain! Specific! Language! for talking about popups. In the
following, it's marked with
# !W!O!W!
require 'watir'
include Watir
at_exit do
$ie.close
end
class IE
def after(&block)
@block = block
self
end
def grab_window(window_title)
@window_title = window_title
self
end
def and_send(*keys)
dropping = dropping_name_from(@window_title)
File.delete(dropping) if File.exist?(dropping)
commandline = ["/ruby/bin/ruby",
"watcher.rb",
"'" + @window_title + "'",
dropping] +
keys
IO.popen(commandline.join(' '))
instance_eval(&@block) if @block
until File.exists?(dropping)
sleep 1
end
end
private
def dropping_name_from(title)
"got-past-" + title.gsub(/\s/, '-')
end
end
$ie = IE.new
$ie.goto("https://example.com/cmd/logon")
$ie.text_field(:name, "U").set("marick")
$ie.text_field(:name, "P").set("not the real one")
$ie.after {
# !W!O!W!
button(:name, "action").click
}.grab_window("Choose a digital certificate").
and_send("{Tab}", "{Space}")
puts "Th-th-th that's all, folks!"
sleep 5 # display final page in all its glory.
====
And here's the watcher script:
require 'watir'
# Do this in JMock style, just because.
class Watcher
private_class_method :new
def self.after_seeing(title)
new(title)
end
def initialize(title)
@autoit = Watir.autoit
@title = title
end
def send(keys)
@keys = keys
self
end
def and_leave_dropping_in(dropping_file)
@dropping_file = dropping_file
do_await_window
do_send_keys
do_leave_dropping
nil
end
private
def do_await_window
@autoit.WinWait @title, ""
end
def do_send_keys
@keys.each do | key |
sleep 1 # Just to watch it happen.
@autoit.Send key
end
end
def do_leave_dropping
File.open(@dropping_file, "w") do | io |
io.puts("Finished at #{Time.now}.")
end
end
end
if $0 == __FILE__
title = ARGV[0]
dropping_file = ARGV[1]
keys = ARGV[2..-1]
$stderr.puts title, dropping_file, keys.inspect; $stderr.flush
Watcher.after_seeing(title).send(keys).
and_leave_dropping_in(dropping_file)
end
-----
Brian Marick, independent consultant
Mostly on agile methods with a testing slant
www.exampler.com, www.exampler.com/blog
-----
Brian Marick, independent consultant
Mostly on agile methods with a testing slant
www.exampler.com, www.exampler.com/blog
_______________________________________________
Wtr-general mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/wtr-general