1 package Test::Harness::Selenium;
5 use Socialtext::WikiFixture::Selenese;
6 use HTML::TableExtract;
12 our $VERSION = '0.01';
16 package Test::Builder;
18 use Class::Method::Modifiers;
19 use ExtUtils::MakeMaker qw(prompt);
21 if (!$ENV{AUTOMATED_TESTING}) {
23 my ($orig, $self) = (shift, shift);
24 my $res = $self->$orig(@_);
26 if ('y' eq prompt "Well that didn't work, did it. Bail out?", 'y') {
38 if( $ENV{SELENIUM_RC_HOST} &&
39 $ENV{SELENIUM_RC_PORT} &&
40 $ENV{SELENIUM_RC_START} ) {
41 $args{selenium_rc}{host} = $ENV{SELENIUM_RC_HOST};
42 $args{selenium_rc}{port} = $ENV{SELENIUM_RC_PORT};
43 $args{selenium_rc}{start} = $ENV{SELENIUM_RC_START};
45 $args{selenium_rc}{xvnc_display} //= '0';
50 sub start_selenium_server {
52 if($self->{selenium_rc}{start}) {
53 my $child = Child->new(sub {
54 system('ssh', $self->{selenium_rc}{host}, 'vncserver',
55 ":$self->{selenium_rc}{xvnc_display}");
58 $self->{selenium_rc}{xvnc_server_proc} = $child->start;
59 $child = Child->new(sub {
60 system('ssh', $self->{selenium_rc}{host}, 'env',
61 "DISPLAY=:$self->{selenium_rc}{xvnc_display}", 'selenium-rc', '-port',
62 $self->{selenium_rc}{port} );
65 $self->{selenium_rc}{selenium_server_proc} = $child->start;
70 $self->{src} = Socialtext::WikiFixture::Selenese->new(
71 host => $self->{selenium_rc}{host},
72 port => $self->{selenium_rc}{port},
73 browser => $self->{browser},
74 browser_url => $self->{app_base},
78 if(!defined $self->{src}) {
86 diag "timed out waiting for selenium server to start at
87 http://$self->{selenium_rc}{host}:$self->{selenium_rc}{port}" if $tries == 5;
92 sub stop_selenium_server {
94 # okay, we're done, kill the server.
96 "http://%s:%s/selenium-server/driver/?cmd=shutDownSeleniumServer",
97 $self->{selenium_rc}{host}, $self->{selenium_rc}{port};
100 $self->{selenium_rc}{selenium_server_proc}->kill("KILL");
101 my $child = Child->new(sub {
102 system('ssh', $self->{selenium_rc}{host}, 'vncserver', '-kill',
103 ":$self->{selenium_rc}{xvnc_display}");
106 my $proc = $child->start;
110 sub start_app_server {
112 my $child = Child->new(sub { exec($self->{app_server_cmd}) } );
113 $self->{app_server_proc} = $child->start;
116 sub stop_app_server {
118 $self->{app_server_proc}->kill("KILL");
122 my ($self, $dir) = @_;
123 if(!exists $self->{app_server_proc}) {
124 $self->start_app_server;
126 if($self->{selenium_rc}{start} && !$self->{selenium_rc}{selenium_server_proc}) {
127 $self->start_selenium_server;
129 my @tests = File::Find::Rule->file()->name('*.html')->in($dir);
130 for my $test (@tests) {
131 $self->run_tests_for($test);
136 my ($self, $html_file) = @_;
137 my $rows = $self->get_rows_for($html_file);
138 $self->{src}->run_test_table($rows);
141 my $te = HTML::TableExtract->new;
143 my ($self, $html_file) = @_;
144 my $html = io($html_file)->all;
146 my $table = ($te->tables)[0];
148 [ map { (!defined $_ or $_ eq "\240") ? () : $_ } @$_ ]
149 } grep { defined $_->[1] } $table->rows;
155 if(exists $self->{selenium_rc}{xvnc_server_proc} and
156 exists $self->{selenium_rc}{selenium_server_proc}) {
157 $self->stop_selenium_server;
159 $self->stop_app_server;
168 Test::Harness::Selenium - Test your app with Selenium
173 my $ths = Test::Harness::Selenium->new(
175 host => 'selenium_xvnc_server',
177 start => 1, # start xvnc and selenium RC via ssh(1)
179 browser => '*firefox',
180 # app_base is relative from the machine running selenium_rc
181 app_base => 'http://10.0.0.5:3000/',
182 app_start_cmd => 'script/myapp_server.pl -p 3000',
184 # HTML tables as emitted by the Selenium IDE
185 $ths->test_directory('t/selenium_corpus');
188 # or, if you've got a selenium server already running (say, on a designer's
190 my $ths = Test::Harness::Selenium->new(
192 host => 'designers_machine',
194 start => 0, # they've already got the RC running
196 browser => '*iexplore', # can't live with it, can't live without it
197 app_base => 'http://10.0.0.5:3000/',
198 app_start_cmd => 'script/myapp_server.pl -p 3000',
201 $ths->test_directory('t/selenium_corpus');
205 C<Test::Harness::Selenium> provides an abstracted way of doing Web app testing
206 using the Selenium framework. It will connect to a running Selenium RC server,
207 or start one using ssh(1) to connect to a machine and then launching the RC
208 server and an xvnc(1) instance in which to run the given browser. After the
209 connection is established, Test::Harness::Selenium will read the specified HTML
210 files from disk and massage them into a format that
211 L<Socialtext::WikiFixture::Selenese> can parse and send to the Selenium RC
212 server. The RC server will then script the browser to do the actions described
213 in the HTML files. These actions return results, which Test::Harness::Selenium
214 then interprets as passing or failing as appropriate.
222 =item arguments: %attrs
224 =item Return value: new Test::Harness::Selenium object
228 Constructor. Accepts a list of key/value pairs according to the following:
234 Hashref. Accepts the keys host, port, start. host and port describe the server
235 on which to start/find the Selenium RC server and possibly the xvnc server.
236 start is a Boolean indicating whether to start the Selenium RC server and xvnc
241 Scalar. The browser to use for testing the app. Must be in a form that Selenium
242 RC understands (e.g. '*firefox'); see the Selenium docs for more info.
246 Scalar. The URL, relative to the machine running Selenium RC, for the base of
247 the app. All requests made to the app are relative to this URL.
251 Scalar. This command will be run by start_app_server to run the app server to
256 =head2 test_directory
258 =item arguments: $dir
260 =item Return value: None
262 Object method. test_directory will use L<File::Find::Rule> to find all C<< .html >>
263 files in the given directory, and then formats massages them into data
264 structures that L<Socialtext::WikiFixture::Selenese> can send to the Selenium RC
265 server. test_directory will then output appropriate TAP according to whether the
266 tests and checks passed or failed, respectively.
270 =item arguments: $html_file
272 =item Return value: None
274 run_tests_for is called by test_directory for each C<< .html >> file it finds in
277 =head2 start_app_server, start_app_server
279 =item Arguments: None
281 =item Return value: None
283 Start and stop the app server using the command given to the constructor.
285 =head2 start_selenium_server, start_selenium_server
287 =item Arguments: None
289 =item Return value: None
291 Start and stop the selenium / xvnc servers using the params given to the
296 =head2 SELENIUM_RC_HOST, SELENIUM_RC_PORT, SELENIUM_RC_START
298 These values override the matching values in the selenium_rc hashref passed to
303 Chris Nehren <c.nehren/ths@shadowcat.co.uk>, Matt S. Trout <mst@shadowcat.co.uk>
307 No one, yet. Patches most welcome!
311 Copyright (c) 2011 the Test::Harness::Selenium "AUTHOR" and
312 "CONTRIBUTORS" as listed above.
316 This library is free software and may be distributed under the same terms as