package Test::WWW::Selenium::Catalyst; use warnings; use strict; use Carp; use Alien::SeleniumRC; use Test::WWW::Selenium; use Test::More; use Catalyst::Utils; 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 my $www_selenium; =head1 NAME Test::WWW::Selenium::Catalyst - Test your Catalyst application with Selenium =cut our $VERSION = '0.02'; =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 in my path, and add C to C. 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 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. Thanks! =head1 SYNOPSIS use Test::WWW::Selenium::Catalyst 'MyApp'; use Test::More tests => 2; my $sel = Test::WWW::Selenium::Catalyst->start; $sel->open_ok('/'); $sel->is_text_present_ok('Welcome to MyApp'); 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. =head1 METHODS =head2 start 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 this module, and it's killed when your test exits. =head2 sel_pid Returns the process ID of the Selenium Server. =head2 app_pid Returns the process ID of the Catalyst server. =cut sub _start_server { # fork off a selenium server my $pid; if(0 == ($pid = fork())){ 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; Alien::SeleniumRC::start() or croak "Can't start Selenium server"; diag("Selenium server $$ going down") if $DEBUG; exit 1; } $sel_pid = $pid; } sub sel_pid { return $sel_pid; } sub app_pid { return $app_pid; } sub import { my ($class, $appname) = @_; 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; } elsif(defined $d && $d == 0){ $DEBUG = 0; } # 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; } $app_pid = $pid; my $tries = 5; my $error; 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/', auto_stop => 0, ); }; $error = $@; } croak "Can't start selenium: $error" if $error; return $www_selenium = $sel; } END { if($www_selenium){ diag("Shutting down Selenium Server $sel_pid") if $DEBUG; $www_selenium->do_command('shutDown'); undef $www_selenium; } if($sel_pid){ 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("Waiting for children to die") if $DEBUG; waitpid $sel_pid, 0 if $sel_pid; waitpid $app_pid, 0 if $app_pid; } =head1 ENVIRONMENT Debugging messages are shown if C or C are set. C is the name of your application, uppercased. (This is the same syntax as Catalyst itself.) =head1 DIAGNOSTICS =head2 Specify your app's name You need to pass your Catalyst app's name as the argument to the use statement: use Test::WWW::Selenium::Catalyst 'MyApp' C is the name of your Catalyst app. =head1 SEE ALSO =over 4 =item * Selenium website: L =item * Description of what you can do with the C<$sel> object: L =item * If you don't need a real web browser: L =back =head1 AUTHOR Jonathan Rockway, C<< >> =head1 BUGS Please report any bugs or feature requests to C, or through the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. =head1 PATCHES Send me unified diffs against the git HEAD at: git://git.jrock.us/Test-WWW-Selenium-Catalyst You can view the repository online at http://git.jrock.us/?p=Test-WWW-Selenium-Catalyst.git;a=summary 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. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; # End of Test::WWW::Selenium::Catalyst