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 at
84 http://$self->{selenium_rc}{host}:$self->{selenium_rc}{port}" if $tries == 5;
87 sub stop_selenium_server {
89 # okay, we're done, kill the server.
91 "http://%s:%s/selenium-server/driver/?cmd=shutDownSeleniumServer",
92 $self->{selenium_rc}{host}, $self->{selenium_rc}{port};
95 $self->{selenium_rc}{selenium_server_proc}->wait;
96 $self->{selenium_rc}{xvnc_server_proc}->wait;
99 sub start_app_server {
101 $self->{app_server_proc} = Child->new(sub { exec($self->{app_server_cmd}) } );
102 $self->{app_server_proc}->start;
105 sub stop_app_server {
107 $self->{app_server_proc}->complete || $self->{app_server_proc}->kill("KILL");
111 my ($self, $dir) = @_;
112 if(!exists $self->{app_server_proc}) {
113 $self->start_app_server;
115 if($self->{selenium_rc}{start} && !$self->{selenium_rc}{selenium_server_proc}) {
116 $self->start_selenium_server;
118 my @tests = File::Find::Rule->file()->name('*.html')->in($dir);
119 for my $test (@tests) {
120 $self->run_tests_for($test);
125 my ($self, $html_file) = @_;
126 my $rows = $self->get_rows_for($html_file);
127 $self->{src}->run_test_table($rows);
130 my $te = HTML::TableExtract->new;
132 my ($self, $html_file) = @_;
133 my $html = io($html_file)->all;
135 my $table = ($te->tables)[0];
137 [ map { (!defined $_ or $_ eq "\240") ? () : $_ } @$_ ]
138 } grep { defined $_->[1] } $table->rows;
144 if(exists $self->{selenium_rc}{xvnc_server_proc} and
145 exists $self->{selenium_rc}{selenium_server_proc}) {
146 $self->{selenium_rc}{xvnc_server_proc}->complete ||
147 $self->{selenium_rc}{xvnc_server_proc}->kill("KILL");
148 $self->{selenium_rc}{selenium_server_proc}->complete ||
149 $self->{selenium_rc}{selenium_server_proc}->kill("KILL");
151 $self->stop_app_server;
160 Test::Harness::Selenium - Test your app with Selenium
165 my $ths = Test::Harness::Selenium->new(
167 host => 'selenium_xvnc_server',
169 start => 1, # start xvnc and selenium RC via ssh(1)
171 browser => '*firefox',
172 # app_base is relative from the machine running selenium_rc
173 app_base => 'http://10.0.0.5:3000/',
174 app_start_cmd => 'script/myapp_server.pl -p 3000',
176 # HTML tables as emitted by the Selenium IDE
177 $ths->test_directory('t/selenium_corpus');
180 # or, if you've got a selenium server already running (say, on a designer's
182 my $ths = Test::Harness::Selenium->new(
184 host => 'designers_machine',
186 start => 0, # they've already got the RC running
188 browser => '*iexplore', # can't live with it, can't live without it
189 app_base => 'http://10.0.0.5:3000/',
190 app_start_cmd => 'script/myapp_server.pl -p 3000',
193 $ths->test_directory('t/selenium_corpus');
197 C<Test::Harness::Selenium> provides an abstracted way of doing Web app testing
198 using the Selenium framework. It will connect to a running Selenium RC server,
199 or start one using ssh(1) to connect to a machine and then launching the RC
200 server and an xvnc(1) instance in which to run the given browser. After the
201 connection is established, Test::Harness::Selenium will read the specified HTML
202 files from disk and massage them into a format that
203 L<Socialtext::WikiFixture::Selenese> can parse and send to the Selenium RC
204 server. The RC server will then script the browser to do the actions described
205 in the HTML files. These actions return results, which Test::Harness::Selenium
206 then interprets as passing or failing as appropriate.
214 =item arguments: %attrs
216 =item Return value: new Test::Harness::Selenium object
220 Constructor. Accepts a list of key/value pairs according to the following:
226 Hashref. Accepts the keys host, port, start. host and port describe the server
227 on which to start/find the Selenium RC server and possibly the xvnc server.
228 start is a Boolean indicating whether to start the Selenium RC server and xvnc
233 Scalar. The browser to use for testing the app. Must be in a form that Selenium
234 RC understands (e.g. '*firefox'); see the Selenium docs for more info.
238 Scalar. The URL, relative to the machine running Selenium RC, for the base of
239 the app. All requests made to the app are relative to this URL.
243 Scalar. This command will be run by start_app_server to run the app server to
248 =head2 test_directory
250 =item arguments: $dir
252 =item Return value: None
254 Object method. test_directory will use L<File::Find::Rule> to find all C<< .html >>
255 files in the given directory, and then formats massages them into data
256 structures that L<Socialtext::WikiFixture::Selenese> can send to the Selenium RC
257 server. test_directory will then output appropriate TAP according to whether the
258 tests and checks passed or failed, respectively.
262 =item arguments: $html_file
264 =item Return value: None
266 run_tests_for is called by test_directory for each C<< .html >> file it finds in
269 =head2 start_app_server, start_app_server
271 =item Arguments: None
273 =item Return value: None
275 Start and stop the app server using the command given to the constructor.
277 =head2 start_selenium_server, start_selenium_server
279 =item Arguments: None
281 =item Return value: None
283 Start and stop the selenium / xvnc servers using the params given to the
288 =head2 SELENIUM_RC_HOST, SELENIUM_RC_PORT, SELENIUM_RC_START
290 These values override the matching values in the selenium_rc hashref passed to
295 Chris Nehren <c.nehren/ths@shadowcat.co.uk>, Matt S. Trout <mst@shadowcat.co.uk>
299 No one, yet. Patches most welcome!
303 Copyright (c) 2011 the Test::Harness::Selenium "AUTHOR" and
304 "CONTRIBUTORS" as listed above.
308 This library is free software and may be distributed under the same terms as