1 package Test::Harness::Selenium;
5 use Socialtext::WikiFixture::Selenese;
6 use HTML::TableExtract;
14 package Test::Builder;
16 use Class::Method::Modifiers;
17 use ExtUtils::MakeMaker qw(prompt);
19 if (!$ENV{AUTOMATED_TESTING}) {
21 my ($orig, $self) = (shift, shift);
22 my $res = $self->$orig(@_);
24 if ('y' eq prompt "Well that didn't work, did it. Bail out?", 'y') {
36 if( $ENV{SELENIUM_RC_HOST} &&
37 $ENV{SELENIUM_RC_PORT} &&
38 $ENV{SELENIUM_RC_START} ) {
39 $args{selenium_rc}{host} = $ENV{SELENIUM_RC_HOST};
40 $args{selenium_rc}{port} = $ENV{SELENIUM_RC_PORT};
41 $args{selenium_rc}{start} = $ENV{SELENIUM_RC_START};
43 $args{selenium_rc}{xvnc_display} //= '0';
48 sub start_selenium_server {
50 if($self->{selenium_rc}{start}) {
51 my $child = Child->new(sub {
52 system('ssh', $self->{selenium_rc}{host}, 'vncserver',
53 ":$self->{selenium_rc}{xvnc_display}");
56 $self->{selenium_rc}{xvnc_server_proc} = $child->start;
57 $child = Child->new(sub {
58 system('ssh', $self->{selenium_rc}{host}, 'env',
59 "DISPLAY=:$self->{selenium_rc}{xvnc_display}", 'selenium-rc', '-port',
60 $self->{selenium_rc}{port} );
63 $self->{selenium_rc}{selenium_server_proc} = $child->start;
68 $self->{src} = Socialtext::WikiFixture::Selenese->new(
69 host => $self->{selenium_rc}{host},
70 port => $self->{selenium_rc}{port},
71 browser => $self->{browser},
72 browser_url => $self->{app_base},
76 if(!defined $self->{src}) {
84 diag "timed out waiting for selenium server to start at
85 http://$self->{selenium_rc}{host}:$self->{selenium_rc}{port}" if $tries == 5;
90 sub stop_selenium_server {
92 # okay, we're done, kill the server.
94 "http://%s:%s/selenium-server/driver/?cmd=shutDownSeleniumServer",
95 $self->{selenium_rc}{host}, $self->{selenium_rc}{port};
98 $self->{selenium_rc}{selenium_server_proc}->kill("KILL");
99 my $child = Child->new(sub {
100 system('ssh', $self->{selenium_rc}{host}, 'vncserver', '-kill',
101 ":$self->{selenium_rc}{xvnc_display}");
104 my $proc = $child->start;
108 sub start_app_server {
110 my $child = Child->new(sub { exec($self->{app_server_cmd}) } );
111 $self->{app_server_proc} = $child->start;
114 sub stop_app_server {
116 $self->{app_server_proc}->kill("KILL");
120 my ($self, $dir) = @_;
121 if(!exists $self->{app_server_proc}) {
122 $self->start_app_server;
124 if($self->{selenium_rc}{start} && !$self->{selenium_rc}{selenium_server_proc}) {
125 $self->start_selenium_server;
127 my @tests = File::Find::Rule->file()->name('*.html')->in($dir);
128 for my $test (@tests) {
129 $self->run_tests_for($test);
134 my ($self, $html_file) = @_;
135 my $rows = $self->get_rows_for($html_file);
136 $self->{src}->run_test_table($rows);
139 my $te = HTML::TableExtract->new;
141 my ($self, $html_file) = @_;
142 my $html = io($html_file)->all;
144 my $table = ($te->tables)[0];
146 [ map { (!defined $_ or $_ eq "\240") ? () : $_ } @$_ ]
147 } grep { defined $_->[1] } $table->rows;
153 if(exists $self->{selenium_rc}{xvnc_server_proc} and
154 exists $self->{selenium_rc}{selenium_server_proc}) {
155 $self->stop_selenium_server;
157 $self->stop_app_server;
166 Test::Harness::Selenium - Test your app with Selenium
171 my $ths = Test::Harness::Selenium->new(
173 host => 'selenium_xvnc_server',
175 start => 1, # start xvnc and selenium RC via ssh(1)
177 browser => '*firefox',
178 # app_base is relative from the machine running selenium_rc
179 app_base => 'http://10.0.0.5:3000/',
180 app_start_cmd => 'script/myapp_server.pl -p 3000',
182 # HTML tables as emitted by the Selenium IDE
183 $ths->test_directory('t/selenium_corpus');
186 # or, if you've got a selenium server already running (say, on a designer's
188 my $ths = Test::Harness::Selenium->new(
190 host => 'designers_machine',
192 start => 0, # they've already got the RC running
194 browser => '*iexplore', # can't live with it, can't live without it
195 app_base => 'http://10.0.0.5:3000/',
196 app_start_cmd => 'script/myapp_server.pl -p 3000',
199 $ths->test_directory('t/selenium_corpus');
203 C<Test::Harness::Selenium> provides an abstracted way of doing Web app testing
204 using the Selenium framework. It will connect to a running Selenium RC server,
205 or start one using ssh(1) to connect to a machine and then launching the RC
206 server and an xvnc(1) instance in which to run the given browser. After the
207 connection is established, Test::Harness::Selenium will read the specified HTML
208 files from disk and massage them into a format that
209 L<Socialtext::WikiFixture::Selenese> can parse and send to the Selenium RC
210 server. The RC server will then script the browser to do the actions described
211 in the HTML files. These actions return results, which Test::Harness::Selenium
212 then interprets as passing or failing as appropriate.
220 =item arguments: %attrs
222 =item Return value: new Test::Harness::Selenium object
226 Constructor. Accepts a list of key/value pairs according to the following:
232 Hashref. Accepts the keys host, port, start. host and port describe the server
233 on which to start/find the Selenium RC server and possibly the xvnc server.
234 start is a Boolean indicating whether to start the Selenium RC server and xvnc
239 Scalar. The browser to use for testing the app. Must be in a form that Selenium
240 RC understands (e.g. '*firefox'); see the Selenium docs for more info.
244 Scalar. The URL, relative to the machine running Selenium RC, for the base of
245 the app. All requests made to the app are relative to this URL.
249 Scalar. This command will be run by start_app_server to run the app server to
254 =head2 test_directory
256 =item arguments: $dir
258 =item Return value: None
260 Object method. test_directory will use L<File::Find::Rule> to find all C<< .html >>
261 files in the given directory, and then formats massages them into data
262 structures that L<Socialtext::WikiFixture::Selenese> can send to the Selenium RC
263 server. test_directory will then output appropriate TAP according to whether the
264 tests and checks passed or failed, respectively.
268 =item arguments: $html_file
270 =item Return value: None
272 run_tests_for is called by test_directory for each C<< .html >> file it finds in
275 =head2 start_app_server, start_app_server
277 =item Arguments: None
279 =item Return value: None
281 Start and stop the app server using the command given to the constructor.
283 =head2 start_selenium_server, start_selenium_server
285 =item Arguments: None
287 =item Return value: None
289 Start and stop the selenium / xvnc servers using the params given to the
294 =head2 SELENIUM_RC_HOST, SELENIUM_RC_PORT, SELENIUM_RC_START
296 These values override the matching values in the selenium_rc hashref passed to
301 Chris Nehren <c.nehren/ths@shadowcat.co.uk>, Matt S. Trout <mst@shadowcat.co.uk>
305 No one, yet. Patches most welcome!
309 Copyright (c) 2011 the Test::Harness::Selenium "AUTHOR" and
310 "CONTRIBUTORS" as listed above.
314 This library is free software and may be distributed under the same terms as