local $SIG{CHLD} = 'IGNORE';
-my $DEBUG = $ENV{CATALYST_DEBUG};
-my $app; # app name (MyApp)
-my $sel_pid; # pid of selenium server
-my $app_pid; # pid of myapp server
+our $DEBUG = $ENV{CATALYST_DEBUG};
+our $app; # app name (MyApp)
+our $sel_pid; # pid of selenium server
+our $app_pid; # pid of myapp server
+our $www_selenium;
=head1 NAME
-Test::WWW::Selenium::Catalyst - Test your Catalyst app with Selenium
+Test::WWW::Selenium::Catalyst - Test your Catalyst application with Selenium
=cut
-our $VERSION = '0.00_01';
+our $VERSION = '0.03';
+
+=head1 DEVELOPERISH RELEASE
+
+This is still a test release. It's working for me in production, but
+it depends on a Java application (SeleniumRC), which can be
+unreliable. On my Debian system, I had to put C<firefox-bin> in my
+path, and add C</usr/lib/firefox> to C<LD_LIBRARY_PATH>. Every distro
+and OS is different, so I'd like some feedback on how this works on
+your system. I would like to find a clean solution that lets this
+module "Just Work" for everyone, but I have a feeling that it's going
+to look more like C<if(gentoo){ ... } elsif (debian) { ... }> and so
+on. I can live with that, but I need your help to get to that stage!
+
+Please report any problems to RT, the Catalyst mailing list, or the
+#catalyst IRC channel on L<irc.perl.org>. Thanks!
=head1 SYNOPSIS
- use Test::WWW::Selenium::Catalyst 'MyApp';
+ use Test::WWW::Selenium::Catalyst 'MyApp', 'command line to selenium';
use Test::More tests => 2;
my $sel = Test::WWW::Selenium::Catalyst->start;
$sel->open_ok('/');
$sel->is_text_present_ok('Welcome to MyApp');
-=head1 FUNCTIONS
+This module starts the SeleniumRC server and your Catalyst app so that
+you can test it with SeleniumRC. Once you've called
+C<< Test::WWW::Selenium::Catalyst->start >>, everything is just like
+L<Test::WWW::Selenium|Test::WWW:Selenium>.
+
+=head1 METHODS
+
+=head2 start(\%args)
+
+Starts the Selenium and Catalyst servers, and returns a pre-initialized,
+ready-to-use Test::WWW::Selenium object.
+
+Arguments:
+
+=over
+
+=item app_uri
+
+URI at which the application can be reached. If this is specified then no
+application server will be started.
+
+=item port
+
+Port on which to run the catalyst application server. The C<MYAPP_PORT>
+environment variable is also respected.
+
+
+=item selenium_class
-=head2 start
+B<Default>: Test::WWW::Selenium
-Starts the Selenium and Catalyst servers, and returns a
-pre-initialized, ready-to-use Test::WWW::Selenium object.
+Classname of Selenium object to create. Use this if you want to subclass
+selenium to add custom logic.
-[NOTE] The selenium server is actually started when you C<use> this
-module, and it's killed when your test exits.
+=item selenium_host
+
+=item selenium_port
+
+Location of externally running selenium server if you do not wish this module
+to control one. See also below for details.
+
+=back
+
+All other options passed verbatim to the selenium constructor.
+
+B<NOTE>: By default a selenium server is started when you C<use> this module, and
+it's killed when your test exits. If wish to manage a selenium server yourself,
+(for instance you wish to start up a server once and run a number of tests
+against it) pass C<-no_selenium_server> to import:
+
+ use Test::WWW::Selenium 'MyApp'
+ -no_selenium_server = 1
+
+Along a similar vein you can also pass command line arguments to the selenium
+server via C<-selenium_args>:
+
+ use Test::WWW::Selenium 'MyApp'
+ -selenium_args = "-singleWindow -port 4445"
=head2 sel_pid
sub _start_server {
+ my ($class, $args) = @_;
# fork off a selenium server
my $pid;
if(0 == ($pid = fork())){
- local $SIG{TERM} = sub {
- diag("Selenium server $$ going down (TERM)");
- exit 0;
- };
-
- chdir '/';
-
- if(!$DEBUG){
- close *STDERR;
- close *STDOUT;
- close *STDIN;
- }
-
- diag("Selenium running in $$") if $DEBUG;
- Alien::SeleniumRC->start()
- or croak "Can't start Selenium server";
- diag("Selenium server $$ going down") if $DEBUG;
- exit 1;
+ local $SIG{TERM} = sub {
+ diag("Selenium server $$ going down (TERM)") if $DEBUG;
+ exit 0;
+ };
+
+ chdir '/';
+
+ if(!$DEBUG){
+ close *STDERR;
+ close *STDOUT;
+ #close *STDIN;
+ }
+
+ diag("Selenium running in $$") if $DEBUG;
+ $class->_start_selenium($args);
+ diag("Selenium server $$ going down") if $DEBUG;
+ exit 1;
}
$sel_pid = $pid;
}
+# Moved out to be subclassable seperately to the fork logic
+sub _start_selenium {
+ my ($class, $arg) = @_;
+ $arg = '' unless defined $arg;
+ Alien::SeleniumRC::start($arg)
+ or croak "Can't start Selenium server";
+}
+
sub sel_pid {
return $sel_pid;
}
}
sub import {
- my ($class, $appname) = @_;
+ my ($class, $appname, %args) = @_;
+
croak q{Specify your app's name} if !$appname;
$app = $appname;
my $d = $ENV{Catalyst::Utils::class2env($appname). "_DEBUG"}; # MYAPP_DEBUG
- if(defined $d && $d){
- $DEBUG = 1;
+ if(defined $d){
+ $DEBUG = $d;
}
- elsif(defined $d && $d == 0){
- $DEBUG = 0;
+
+ unless ($args{-no_seleniun_server}) {
+ $class->_start_server($args{-selenium_args}) or croak "Couldn't start selenium server";
}
- # if it's something else, leave the CATALYST_DEBUG setting in tact
-
- _start_server() or croak "Couldn't start selenium server";
return 1;
}
sub start {
my $class = shift;
my $args = shift || {};
-
- # start a Catalyst MyApp server
- eval("use $app");
- croak "Couldn't load $app: $@" if $@;
-
- my $pid;
- if(0 == ($pid = fork())){
- local $SIG{TERM} = sub {
- diag("Catalyst server $$ going down (TERM)") if $DEBUG;
- exit 0;
- };
- diag("Catalyst server running in $$") if $DEBUG;
- $app->run('3000', 'localhost');
- exit 1;
+
+ my $port = delete $args->{port};
+ $port ||= $ENV{Catalyst::Utils::class2env($app). "_PORT"} # MYAPP_PORT
+ || 3000;
+
+ my $uri;
+
+ # Check for CATALYST_SERVER env var like TWMC does.
+ if ( $ENV{CATALYST_SERVER} ) {
+ $uri = $ENV{CATALYST_SERVER};
+ } elsif ( $args->{app_uri} ) {
+ $uri = delete $args->{app_uri}
+ } else {
+ # start a Catalyst MyApp server
+ eval("use $app");
+ croak "Couldn't load $app: $@" if $@;
+
+ my $pid;
+ if(0 == ($pid = fork())){
+ local $SIG{TERM} = sub {
+ diag("Catalyst server $$ going down (TERM)") if $DEBUG;
+ exit 0;
+ };
+ diag("Catalyst server running in $$") if $DEBUG;
+ $app->run($port, 'localhost');
+ exit 1;
+ }
+ $uri = 'http://localhost:' . $port;
+ $app_pid = $pid;
}
- $app_pid = $pid;
my $tries = 5;
my $error;
+ my $sel_class = delete $args->{selenium_class} || 'Test::WWW::Selenium';
my $sel;
+
while(!$sel && $tries--){
- sleep 1;
- diag("Waiting for selenium server to start")
- if $DEBUG;
-
- eval {
- $sel = Test::WWW::Selenium->
- new(host => 'localhost',
- port => 4444,
- browser => $args->{browser} || '*firefox',
- browser_url => 'http://localhost:3000/'
- );
- };
- $error = $@;
+ sleep 1;
+ diag("Waiting for selenium server to start")
+ if $DEBUG;
+
+ eval {
+ $sel = $sel_class->new(
+ host => delete $args->{selenium_host} || 'localhost',
+ port => delete $args->{selenium_port} || 4444,
+ browser => '*firefox',
+ browser_url => $uri,
+ auto_stop => 0,
+ %$args
+ );
+ };
+ $error = $@;
}
+ croak "Can't start selenium: $error" if $error;
- eval { $sel->start }
- or croak "Can't start selenium: $@ (previous error: $error)";
-
- return $sel;
+ return $www_selenium = $sel;
}
END {
if($sel_pid){
- diag("Killing Selenium Server $sel_pid") if $DEBUG;
- kill 15, $sel_pid or diag "Killing Selenium: $!";
- undef $sel_pid;
+ if($www_selenium){
+ diag("Shutting down Selenium Server $sel_pid") if $DEBUG;
+ $www_selenium->do_command('shutDown');
+ undef $www_selenium;
+ }
+ diag("Killing Selenium Server $sel_pid") if $DEBUG;
+ kill 15, $sel_pid or diag "Killing Selenium: $!";
+ undef $sel_pid;
}
if($app_pid){
- diag("Killing catalyst server $app_pid") if $DEBUG;
- kill 15, $app_pid or diag "Killing MyApp: $!";
- undef $app_pid;
+ diag("Killing catalyst server $app_pid") if $DEBUG;
+ kill 15, $app_pid or diag "Killing MyApp: $!";
+ undef $app_pid;
}
diag("Waiting for children to die") if $DEBUG;
waitpid $sel_pid, 0 if $sel_pid;
are set. C<MYAPP> is the name of your application, uppercased. (This
is the same syntax as Catalyst itself.)
+C<CATALYST_SERVER> can be set to test against an externally running server,
+in a similar manner to how L<Test::WWW::Mechanize::Catalyst> behaves.
+
+The port that the application sever runs on can be affected by C<MYAPP_PORT>
+in addition to being specifiable in the arguments passed to start.
+
=head1 DIAGNOSTICS
=head2 Specify your app's name
C<MyApp> is the name of your Catalyst app.
-=head1 AUTHOR
+=head1 SEE ALSO
-Jonathan Rockway, C<< <jrockway at cpan.org> >>
+=over 4
-=head1 BUGS
+=item *
-Please report any bugs or feature requests to
-C<bug-test-www-selenium-catalyst at rt.cpan.org>, or through the web interface at
-L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-WWW-Selenium-Catalyst>.
-I will be notified, and then you'll automatically be notified of progress on
-your bug as I make changes.
+Selenium website: L<http://www.openqa.org/>
-=head1 SUPPORT
+=item *
-You can find documentation for this module with the perldoc command.
+Description of what you can do with the C<$sel> object: L<Test::WWW::Selenium>
- perldoc Test::WWW::Selenium::Catalyst
+=item *
-You can also look for information at:
+If you don't need a real web browser: L<Test::WWW::Mechanize::Catalyst>
-=over 4
+=back
-=item * AnnoCPAN: Annotated CPAN documentation
+=head1 AUTHOR
-L<http://annocpan.org/dist/Test-WWW-Selenium-Catalyst>
+Jonathan Rockway, C<< <jrockway at cpan.org> >>
+
+=head1 BUGS
-=item * CPAN Ratings
+Please report any bugs or feature requests to
+C<bug-test-www-selenium-catalyst at rt.cpan.org>, or through the web interface at
+L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-WWW-Selenium-Catalyst>.
+I will be notified, and then you'll automatically be notified of progress on
+your bug as I make changes.
-L<http://cpanratings.perl.org/d/Test-WWW-Selenium-Catalyst>
+=head1 PATCHES
-=item * RT: CPAN's request tracker
+Send me unified diffs against the git HEAD at:
-L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Test-WWW-Selenium-Catalyst>
+ git://git.jrock.us/Test-WWW-Selenium-Catalyst
-=item * Search CPAN
+You can view the repository online at
-L<http://search.cpan.org/dist/Test-WWW-Selenium-Catalyst>
+ http://git.jrock.us/?p=Test-WWW-Selenium-Catalyst.git;a=summary
-=back
+Thanks in advance for your contributions!
=head1 ACKNOWLEDGEMENTS
+Thanks for mst for getting on my case to actually write this thing :)
+
=head1 COPYRIGHT & LICENSE
Copyright 2006 Jonathan Rockway, all rights reserved.