use LWP::Simple;
use Child;
+our $VERSION = '0.01';
+
use Test::More;
BEGIN {
package Test::Builder;
sub new {
my $class = shift;
my %args = @_;
- if( $ENV{SELENIUM_RC_HOST} &&
- $ENV{SELENIUM_RC_PORT} &&
- $ENV{SELENIUM_RC_START} ) {
- $args{selenium_rc}{host} = $ENV{SELENIUM_RC_HOST};
- $args{selenium_rc}{port} = $ENV{SELENIUM_RC_PORT};
- $args{selenium_rc}{start} = $ENV{SELENIUM_RC_START};
- }
+ my $selrc = ($args{selenium_rc} ||={});
+ $selrc->{$_} = $ENV{"SELENIUM_RC_${\uc $_}"}
+ for grep exists $ENV{"SELENIUM_RC_${\uc $_}"},
+ qw(host port start start_xvnc xvnc_display);
+ $selrc->{xvnc_display} ||= '0';
+ $selrc->{host} ||= 'localhost';
+ $selrc->{port} ||= 4444;
+ $args{browser} ||= '*firefox';
my $self = \%args;
bless $self, $class;
}
sub start_selenium_server {
my($self) = @_;
- if($self->{selenium_rc}{start}) {
- $self->{selenium_rc}{xvnc_server_proc} = Child->new(sub {
- system('ssh', $self->{selenium_rc}{host}, 'vncserver');
- }
- );
- $self->{selenium_rc}{xvnc_server_proc}->start;
- $self->{selenium_rc}{selenium_server_proc} = Child->new(sub {
- system('ssh', $self->{selenium_rc}{host}, 'env',
- "DISPLAY=:$self->{selenium_rc}{xvnc_display}", 'selenium-rc', '-port',
- $self->{selenium_rc}{port} );
+ my $selrc = $self->{selenium_rc};
+ if($selrc->{start}) {
+ my ($host, $display, $port) = @{$selrc}{qw(host xvnc_display port)};
+ my @do_ssh = $host eq 'localhost' ? () : ('ssh', $host);
+ if ($selrc->{start_xvnc}) {
+ $selrc->{xvnc_server_proc} = Child->new(
+ sub {
+ exec(
+ @do_ssh,
+ 'vncserver', ":${display}",
+ );
+ }
+ )->start;
+ $selrc->{xvnc_started} = 1;
+ sleep 3;
+ }
+ $selrc->{selenium_server_proc} = Child->new(
+ sub {
+ exec(
+ @do_ssh,
+ 'env', "DISPLAY=:${display}", 'selenium-rc', '-port', $port
+ )
}
- );
- $self->{selenium_rc}{selenium_server_proc}->start;
+ )->start;
+ sleep 1;
}
my $tries = 0;
while($tries < 5) {
eval {
+ # if we don't create the ::Selenium object ourselves, then
+ # wikifixture shuts the session down after the first test table
+ # is run, at which point KABOOM when you try and run a second one.
$self->{src} = Socialtext::WikiFixture::Selenese->new(
- host => $self->{selenium_rc}{host},
- port => $self->{selenium_rc}{port},
- browser => $self->{browser},
- browser_url => $self->{app_base},
+ selenium => Test::WWW::Selenium->new(
+ host => $self->{selenium_rc}{host},
+ port => $self->{selenium_rc}{port},
+ browser => $self->{browser},
+ browser_url => $self->{app_base},
+ )
);
};
$tries++;
last;
}
}
- die "timed out waiting for selenium server to start" if $tries == 5;
+ if($tries == 5) {
+ diag "timed out waiting for selenium server to start at
+ http://$self->{selenium_rc}{host}:$self->{selenium_rc}{port}" if $tries == 5;
+ $self->done;
+ die;
+ }
}
sub stop_selenium_server {
my($self) = @_;
- # okay, we're done, kill the server.
- my $url = sprintf
- "http://%s:%s/selenium-server/driver/?cmd=shutDownSeleniumServer",
- $self->{selenium_rc}{host}, $self->{selenium_rc}{port};
- get($url);
- delete $self->{src};
- $self->{selenium_rc}{selenium_server_proc}->wait;
- $self->{selenium_rc}{xvnc_server_proc}->wait;
+ if (my $proc = delete $self->{selenium_rc}{selenium_server_proc}) {
+ my $url = sprintf
+ "http://%s:%s/selenium-server/driver/?cmd=shutDownSeleniumServer",
+ $self->{selenium_rc}{host}, $self->{selenium_rc}{port};
+ eval { get($url); }; # will fail if it never started
+ delete $self->{src};
+ $proc->kill("KILL");
+ }
+ if (delete $self->{selenium_rc}{xvnc_started}) {
+ my $host = $self->{selenium_rc}{host};
+ my @do_ssh = $host eq 'localhost' ? () : ('ssh', $host);
+ Child->new(sub {
+ exec(@do_ssh, 'vncserver', '-kill',
+ ":$self->{selenium_rc}{xvnc_display}");
+ })->start->wait;
+ }
}
sub start_app_server {
my($self) = @_;
- $self->{app_server_proc} = Child->new(sub { exec($self->{app_server_cmd}) } );
- $self->{app_server_proc}->start;
+ return unless $self->{app_server_cmd};
+ my $child = Child->new(sub { exec($self->{app_server_cmd}) } );
+ $self->{app_server_proc} = $child->start;
}
sub stop_app_server {
my($self) = @_;
- $self->{app_server_proc}->complete || $self->{app_server_proc}->kill(9);
+ if (my $proc = $self->{app_server_proc}) {
+ $proc->kill("KILL");
+ }
}
sub test_directory {
my ($self, $dir) = @_;
- my @tests = File::Find::Rule->file()->name('*.html')->in($dir);
+ $self->start_everything;
+ my @tests =
+ sort File::Find::Rule->file->name('*.html')->maxdepth(1)->in($dir);
+
for my $test (@tests) {
- $self->start_server;
- $self->run_tests_for($test);
- $self->stop_server;
+ $self->test_file($test);
}
}
-sub run_tests_for {
+sub test_file {
my ($self, $html_file) = @_;
my $rows = $self->get_rows_for($html_file);
$self->{src}->run_test_table($rows);
}
+# might as well keep this object around.
my $te = HTML::TableExtract->new;
sub get_rows_for {
my ($self, $html_file) = @_;
return \@rows;
}
-sub DESTROY {
+sub done {
my($self) = @_;
- if(exists $self->{xvnc_pid}) {
- kill("KILL", $self->{xvnc_pid});
- }
+ $self->stop_selenium_server;
+ $self->stop_app_server;
}
+sub DESTROY { shift->done }
+
1;
__END__
=head1 SYNOPSIS
- # t/selenium.t
- my $ths = Test::Harness::Selenium->new(
- selenium_rc => {
- host => 'selenium_xvnc_server',
- port => 12345,
- start => 1, # start xvnc and selenium RC via ssh(1)
- },
- browser => '*firefox',
- # app_base is relative from the machine running selenium_rc
- app_base => 'http://10.0.0.5:3000/',
- app_start_cmd => 'script/myapp_server.pl -p 3000',
+ # t/catapp.t
+ my $browser = shift;
+ my $s = Test::Harness::Selenium->new(
+ selenium_rc => {
+ host => '10.0.0.8',
+ port => $< + 6900,
+ start => 1,
+ xvnc_display => 1,
+ },
+ app_base => 'http://10.0.0.5:3000',
+ app_server_cmd => 'examples/THSelenium-Test/script/thselenium_test_server.pl',
+ browser => '*firefox',
);
# HTML tables as emitted by the Selenium IDE
- $ths->test_directory('t/selenium_corpus');
+ eval { $s->test_directory('t/corpus/') };
+ $s->done;
+ done_testing;
# or, if you've got a selenium server already running (say, on a designer's
},
browser => '*iexplore', # can't live with it, can't live without it
app_base => 'http://10.0.0.5:3000/',
- app_start_cmd => 'script/myapp_server.pl -p 3000',
+ app_server_cmd => 'script/myapp_server.pl -p 3000',
);
# otherwise the same
$ths->test_directory('t/selenium_corpus');
=item selenium_rc
-Hashref. Accepts the keys host, port, start. host and port describe the server
-on which to start/find the Selenium RC server and possibly the xvnc server.
-start is a Boolean indicating whether to start the Selenium RC server and xvnc
-server.
+Hashref. Accepts the keys host, port, start, start_xvnc, xvnc_display. host and
+port describe the server on which to start/find the Selenium RC server and
+possibly the xvnc server. start is a Boolean indicating whether to start the
+Selenium RC server, whereas start_xvnc dictates the same for the xvnc server.
+xvnc_display is the X11 display to point browsers at when launching them.
=item browser
Scalar. The URL, relative to the machine running Selenium RC, for the base of
the app. All requests made to the app are relative to this URL.
-=item app_start_cmd
+=item app_server_cmd
Scalar. This command will be run by start_app_server to run the app server to
test.
=item Return value: None
Object method. test_directory will use L<File::Find::Rule> to find all C<< .html >>
-files in the given directory, and then formats massages them into data
-structures that L<Socialtext::WikiFixture::Selenese> can send to the Selenium RC
-server. test_directory will then output appropriate TAP according to whether the
-tests and checks passed or failed, respectively.
+files in the given directory, and then massages them into data structures that
+L<Socialtext::WikiFixture::Selenese> can send to the Selenium RC server.
+test_directory will then output appropriate TAP according to whether the tests
+and checks passed or failed, respectively.
-=head2 run_tests_for
+=head2 test_file
-=item arguments: $html_file
+=item arguments: $file
=item Return value: None
-run_tests_for is called by test_directory for each C<< .html >> file it finds in
-the given directory.
+Object method. Runs the tests given in the specified file.
-=head2 start_app_server, start_app_server
+=head2 start_app_server, stop_app_server
=item Arguments: None
=item Return value: None
-Start and stop the app server using the command given to the constructor.
+Object method. Start and stop the app server using the command given to the
+constructor. References the app_server_cmd key passed to the constructor.
=head2 start_selenium_server, start_selenium_server
=head1 ENVIRONMENT
-=head2 SELENIUM_RC_HOST, SELENIUM_RC_PORT, SELENIUM_RC_START
+=head2 SELENIUM_RC_HOST, SELENIUM_RC_PORT, SELENIUM_RC_START,
+SELENIUM_RC_START_XVNC, SELENIUM_RC_XVNC_DISPLAY
These values override the matching values in the selenium_rc hashref passed to
new.
=head1 CONTRIBUTORS
-No one, yet. Patches most welcome!
+No one, yet. Patches most welcome! We most especially welcome doc patches to
+make our code easier to use. We can write the best code in the world, but it
+doesn't do anyone any good if it's impossible to use because of bad docs.
=head1 COPYRIGHT