The solution for me was to use Selenium. It supports a variety of browsers, platforms, and programming languages - including perl. Selenium has a number of components but the two discussed in this tutorial are the Selenium IDE and Selenium RC. The IDE is a FireFox add-on that allows you to "record" the interaction of a website and generate working test scripts in a variety of languages. The Remote Control (RC) component provides an API controlling Selenium from your favorite language.
I could not get FF to work using any of the default options Selenium RC provides (*firefox and *chrome). I ended up having to start firefox with -profilemanager and creating a custom profile. This means going in and setting the proxy settings to Selenium's default port (4444) as well trusting the CyberVillian's CA for https support (more on that later). The end result looked like:
"*custom C:/path/to/ff/firefox.exe -no-remote -profile C:/selenium/fir +efox/custom_profile"
I also ran into trouble with my software based firewall for Windows (Norton Internet Security). It allowed Java to open a listening port on 4444 but it would not allow any outgoing connections. Since you can configure different applications with different settings - this is something to watch out for.
In order for Selenium to allow interaction with SSL sites, you need to trust the Cybervillians CA certificate. This is all supposed to be hidden under the covers if you use one of the default browser options like *chrome but I was stuck doing everything myself with *custom. Keep in mind that you want to ONLY set up this trust in the custom profile for obvious security reasons. Additionally, the last stable release of Selenium RC has an expired certificate. You will need to get a current one from one of the nightly builds. Lastly, the Selenium core can be started with -trustAllSSLCertificates which may be required for sites that you want to automate that do not have valid certs themselves.
Selenium gives extremely unfriendly error messages ("Permission denied to get property Window.seleniumMarker") if you attempt to access a site with a different base domain then the one you passed in in the constructor. If you log on to https://example.com and it silently redirects you to https://www.example.com you will get that weird error message if you follow a link on the page. Be sure to start a new browser if you need to go to a site with a different base domain name.
Finally, the output of the IDE is a test script where everything ends in _ok. If you are automating and not testing, you will need to drop all of the _oks as well as remember to call $sel->start before any automation.
It is folly to try and use arbitrary pauses such as $sel->pause(5000) or sleep 5. You really should use one of the wait_for alternatives. These methods all follow the same naming scheme such as $sel->wait_for_pop_up($window_id, $timeout) and $sel->wait_for_element_present($locator, $timeout); They can be wrapped in an eval block. This is almost enough, but there are still circumstances when even wait_for will fail you (more on that later).
If you chose not to use the FF IDE to pre-record your session, you may find it difficult trying to figure out what Selenium wants for $locator when waiting for an element. There are a number of different ways to specify a locator (and even an extension to add your own), but xpath seems to be the preferred way. When you are using the IDE, you can right click over just about anything (button, form element, text) and get some additional options such as "assert_text_present" or "assert_element_present". You can then copy/paste the xpath into $locator without having to figure anything else out.
The secret is that Selenium provides a very powerful method called $sel->get_eval(); which will allow you to execute arbitrary JS and return the results. My subclass looked a bit like this:
sub wait_for_JS_function { my ($self, $func, $timeout) = @_; for (1 .. $timeout / 100) { my $res = $self->get_eval("typeof( window.$func )"); return $res if $res ne 'null'; $self->pause(100); } croak "Timed out waiting for JS func: '$func'"; }
# Assumes only 1 currently opened window with target _blank sub select_target_blank_window { my ($self, $timeout) = @_; my $window_name; for (1 .. $timeout / 100) { ($window_name) = grep {/selenium_blank\d+/} $self->get_all_wind +ow_names; last if defined $window_name; $self->pause(100); } croak "Timed out waiting to select blank target window" if ! defin +ed $window_name; return $self->select_window($window_name); }
sub set_hidden_parameter { my ($self, $form, $elem, $val) = @_; my $javascript = "window.document.$form.$elem.value = " . '"' . $v +al . '";'; my $rc = $self->get_eval('$javascript'); return $rc; }
$sel->type($loc, $val); $sel->fire_event($loc, 'onblur');
This module is really deserving of a much larger FAQ and cookbook. I know it would have made things easier for me. I have been in contact with the module author who has been friendly and receptive. If you have any interest in this at all, I highly recommend you share that information.
Cheers - L~R
|
---|