Skip tests when java isn't installed/runable (IPC::Cmd)
[catagits/Test-WWW-Selenium-Catalyst.git] / lib / Test / WWW / Selenium / Catalyst.pm
index 075fdb5..014694f 100644 (file)
@@ -12,10 +12,11 @@ BEGIN { $ENV{CATALYST_ENGINE} ||= 'HTTP'; }
 
 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
 
@@ -23,11 +24,11 @@ Test::WWW::Selenium::Catalyst - Test your Catalyst application with Selenium
 
 =cut
 
-our $VERSION = '0.00_01';
+our $VERSION = '0.05';
 
-=head1 DEVELOPER RELEASE
+=head1 DEVELOPERISH RELEASE
 
-This is a developer release.  It's working for me in production, but
+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
@@ -42,7 +43,7 @@ Please report any problems to RT, the Catalyst mailing list, or the
 
 =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; 
@@ -51,18 +52,64 @@ Please report any problems to RT, the Catalyst mailing list, or the
 
 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
+C<< Test::WWW::Selenium::Catalyst->start >>, everything is just like
 L<Test::WWW::Selenium|Test::WWW:Selenium>.
 
-=head1 FUNCTIONS
+=head1 METHODS
 
-=head2 start
+=head2 start(\%args)
 
-Starts the Selenium and Catalyst servers, and returns a
-pre-initialized, ready-to-use Test::WWW::Selenium object.
+Starts the Selenium and Catalyst servers, and returns a pre-initialized,
+ready-to-use Test::WWW::Selenium object.
 
-[NOTE] The selenium server is actually started when you C<use> this
-module, and it's killed when your test exits.
+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
+
+B<Default>: 3000
+
+Port on which to run the catalyst application server. The C<MYAPP_PORT>
+environment variable is also respected.
+
+
+=item selenium_class
+
+B<Default>: Test::WWW::Selenium
+
+Classname of Selenium object to create. Use this if you want to subclass
+selenium to add custom logic.
+
+=item selenium_host
+
+=item selenium_port
+
+Location of externally running selenium server if you do not wish this module
+to control one. See also 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
 
@@ -76,31 +123,39 @@ Returns the process ID of the Catalyst server.
 
 
 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;
 }
@@ -110,78 +165,105 @@ sub app_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_selenium_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->stop();
+            $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;
+
+    } elsif ($www_selenium) {
+        diag("Stopping Selenium Session $sel_pid") if $DEBUG;
+        $www_selenium->stop();
+        undef $www_selenium;
     }
+
     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;
@@ -195,6 +277,12 @@ Debugging messages are shown if C<CATALYST_DEBUG> or C<MYAPP_DEBUG>
 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
@@ -212,11 +300,12 @@ C<MyApp> is the name of your Catalyst app.
 
 =item * 
 
-Selenium website: L<http://www.openqa.org/>
+Selenium website: L<http://seleniumhq.org/>
 
 =item * 
 
 Description of what you can do with the C<$sel> object: L<Test::WWW::Selenium>
+and L<WWW::Selenium>
 
 =item * 
 
@@ -226,6 +315,8 @@ If you don't need a real web browser: L<Test::WWW::Mechanize::Catalyst>
 
 =head1 AUTHOR
 
+Ash Berlin C<< <ash@cpan.org> >>
+
 Jonathan Rockway, C<< <jrockway at cpan.org> >>
 
 =head1 BUGS
@@ -236,33 +327,17 @@ 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.
 
-=head1 SUPPORT
-
-You can find documentation for this module with the perldoc command.
-
-    perldoc Test::WWW::Selenium::Catalyst
-
-You can also look for information at:
-
-=over 4
-
-=item * AnnoCPAN: Annotated CPAN documentation
+=head1 PATCHES
 
-L<http://annocpan.org/dist/Test-WWW-Selenium-Catalyst>
+Send me unified diffs against the git HEAD at:
 
-=item * CPAN Ratings
+    git://github.com/jrockway/test-www-selenium-catalyst.git
 
-L<http://cpanratings.perl.org/d/Test-WWW-Selenium-Catalyst>
+You can view the repository online at 
 
-=item * RT: CPAN's request tracker
+    http://github.com/jrockway/test-www-selenium-catalyst/tree/master
 
-L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Test-WWW-Selenium-Catalyst>
-
-=item * Search CPAN
-
-L<http://search.cpan.org/dist/Test-WWW-Selenium-Catalyst>
-
-=back
+Thanks in advance for your contributions!
 
 =head1 ACKNOWLEDGEMENTS
 
@@ -270,6 +345,8 @@ Thanks for mst for getting on my case to actually write this thing :)
 
 =head1 COPYRIGHT & LICENSE
 
+Copyright 2009 Ash Berlin, all rights reserved.
+
 Copyright 2006 Jonathan Rockway, all rights reserved.
 
 This program is free software; you can redistribute it and/or modify it