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 $self->{selenium_rc}{xvnc_server_proc} = Child->new(sub {
52 system('ssh', $self->{selenium_rc}{host}, 'vncserver',
53 ":$self->{selenium_rc}{xvnc_display}");
56 $self->{selenium_rc}{xvnc_server_proc}->start;
57 $self->{selenium_rc}{selenium_server_proc} = 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}->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}) {
83 die "timed out waiting for selenium server to start" if $tries == 5;
86 sub stop_selenium_server {
88 # okay, we're done, kill the server.
90 "http://%s:%s/selenium-server/driver/?cmd=shutDownSeleniumServer",
91 $self->{selenium_rc}{host}, $self->{selenium_rc}{port};
94 $self->{selenium_rc}{selenium_server_proc}->wait;
95 $self->{selenium_rc}{xvnc_server_proc}->wait;
98 sub start_app_server {
100 $self->{app_server_proc} = Child->new(sub { exec($self->{app_server_cmd}) } );
101 $self->{app_server_proc}->start;
104 sub stop_app_server {
106 $self->{app_server_proc}->complete || $self->{app_server_proc}->kill("KILL");
110 my ($self, $dir) = @_;
111 if($self->{selenium_rc}{start} && !$self->{selenium_rc}{selenium_server_proc}) {
112 $self->start_selenium_server;
114 my @tests = File::Find::Rule->file()->name('*.html')->in($dir);
115 for my $test (@tests) {
116 $self->run_tests_for($test);
121 my ($self, $html_file) = @_;
122 my $rows = $self->get_rows_for($html_file);
123 $self->{src}->run_test_table($rows);
126 my $te = HTML::TableExtract->new;
128 my ($self, $html_file) = @_;
129 my $html = io($html_file)->all;
131 my $table = ($te->tables)[0];
133 [ map { (!defined $_ or $_ eq "\240") ? () : $_ } @$_ ]
134 } grep { defined $_->[1] } $table->rows;
140 if(exists $self->{selenium_rc}{xvnc_server_proc} and
141 exists $self->{selenium_rc}{selenium_server_proc}) {
142 $self->{selenium_rc}{xvnc_server_proc}->complete ||
143 $self->{selenium_rc}{xvnc_server_proc}->kill("KILL");
144 $self->{selenium_rc}{selenium_server_proc}->complete ||
145 $self->{selenium_rc}{selenium_server_proc}->kill("KILL");
147 $self->stop_app_server;
156 Test::Harness::Selenium - Test your app with Selenium
161 my $ths = Test::Harness::Selenium->new(
163 host => 'selenium_xvnc_server',
165 start => 1, # start xvnc and selenium RC via ssh(1)
167 browser => '*firefox',
168 # app_base is relative from the machine running selenium_rc
169 app_base => 'http://10.0.0.5:3000/',
170 app_start_cmd => 'script/myapp_server.pl -p 3000',
172 # HTML tables as emitted by the Selenium IDE
173 $ths->test_directory('t/selenium_corpus');
176 # or, if you've got a selenium server already running (say, on a designer's
178 my $ths = Test::Harness::Selenium->new(
180 host => 'designers_machine',
182 start => 0, # they've already got the RC running
184 browser => '*iexplore', # can't live with it, can't live without it
185 app_base => 'http://10.0.0.5:3000/',
186 app_start_cmd => 'script/myapp_server.pl -p 3000',
189 $ths->test_directory('t/selenium_corpus');
193 C<Test::Harness::Selenium> provides an abstracted way of doing Web app testing
194 using the Selenium framework. It will connect to a running Selenium RC server,
195 or start one using ssh(1) to connect to a machine and then launching the RC
196 server and an xvnc(1) instance in which to run the given browser. After the
197 connection is established, Test::Harness::Selenium will read the specified HTML
198 files from disk and massage them into a format that
199 L<Socialtext::WikiFixture::Selenese> can parse and send to the Selenium RC
200 server. The RC server will then script the browser to do the actions described
201 in the HTML files. These actions return results, which Test::Harness::Selenium
202 then interprets as passing or failing as appropriate.
210 =item arguments: %attrs
212 =item Return value: new Test::Harness::Selenium object
216 Constructor. Accepts a list of key/value pairs according to the following:
222 Hashref. Accepts the keys host, port, start. host and port describe the server
223 on which to start/find the Selenium RC server and possibly the xvnc server.
224 start is a Boolean indicating whether to start the Selenium RC server and xvnc
229 Scalar. The browser to use for testing the app. Must be in a form that Selenium
230 RC understands (e.g. '*firefox'); see the Selenium docs for more info.
234 Scalar. The URL, relative to the machine running Selenium RC, for the base of
235 the app. All requests made to the app are relative to this URL.
239 Scalar. This command will be run by start_app_server to run the app server to
244 =head2 test_directory
246 =item arguments: $dir
248 =item Return value: None
250 Object method. test_directory will use L<File::Find::Rule> to find all C<< .html >>
251 files in the given directory, and then formats massages them into data
252 structures that L<Socialtext::WikiFixture::Selenese> can send to the Selenium RC
253 server. test_directory will then output appropriate TAP according to whether the
254 tests and checks passed or failed, respectively.
258 =item arguments: $html_file
260 =item Return value: None
262 run_tests_for is called by test_directory for each C<< .html >> file it finds in
265 =head2 start_app_server, start_app_server
267 =item Arguments: None
269 =item Return value: None
271 Start and stop the app server using the command given to the constructor.
273 =head2 start_selenium_server, start_selenium_server
275 =item Arguments: None
277 =item Return value: None
279 Start and stop the selenium / xvnc servers using the params given to the
284 =head2 SELENIUM_RC_HOST, SELENIUM_RC_PORT, SELENIUM_RC_START
286 These values override the matching values in the selenium_rc hashref passed to
291 Chris Nehren <c.nehren/ths@shadowcat.co.uk>, Matt S. Trout <mst@shadowcat.co.uk>
295 No one, yet. Patches most welcome!
299 Copyright (c) 2011 the Test::Harness::Selenium "AUTHOR" and
300 "CONTRIBUTORS" as listed above.
304 This library is free software and may be distributed under the same terms as