Make selenium and MYAPP invocation more flexible
[catagits/Test-WWW-Selenium-Catalyst.git] / lib / Test / WWW / Selenium / Catalyst.pm
CommitLineData
4f6d213e 1package Test::WWW::Selenium::Catalyst;
2
3use warnings;
4use strict;
5use Carp;
6use Alien::SeleniumRC;
7use Test::WWW::Selenium;
8use Test::More;
9use Catalyst::Utils;
10
11BEGIN { $ENV{CATALYST_ENGINE} ||= 'HTTP'; }
12
13local $SIG{CHLD} = 'IGNORE';
14
374611f4 15our $DEBUG = $ENV{CATALYST_DEBUG};
16our $app; # app name (MyApp)
17our $sel_pid; # pid of selenium server
18our $app_pid; # pid of myapp server
19our $www_selenium;
4f6d213e 20
21=head1 NAME
22
1cdeb21f 23Test::WWW::Selenium::Catalyst - Test your Catalyst application with Selenium
4f6d213e 24
25=cut
26
374611f4 27our $VERSION = '0.03';
4f6d213e 28
de35f86c 29=head1 DEVELOPERISH RELEASE
1cdeb21f 30
de35f86c 31This is still a test release. It's working for me in production, but
1cdeb21f 32it depends on a Java application (SeleniumRC), which can be
33unreliable. On my Debian system, I had to put C<firefox-bin> in my
34path, and add C</usr/lib/firefox> to C<LD_LIBRARY_PATH>. Every distro
35and OS is different, so I'd like some feedback on how this works on
36your system. I would like to find a clean solution that lets this
37module "Just Work" for everyone, but I have a feeling that it's going
38to look more like C<if(gentoo){ ... } elsif (debian) { ... }> and so
39on. I can live with that, but I need your help to get to that stage!
40
41Please report any problems to RT, the Catalyst mailing list, or the
42#catalyst IRC channel on L<irc.perl.org>. Thanks!
43
4f6d213e 44=head1 SYNOPSIS
45
374611f4 46 use Test::WWW::Selenium::Catalyst 'MyApp', 'command line to selenium';
4f6d213e 47 use Test::More tests => 2;
48
49 my $sel = Test::WWW::Selenium::Catalyst->start;
50 $sel->open_ok('/');
51 $sel->is_text_present_ok('Welcome to MyApp');
52
1cdeb21f 53This module starts the SeleniumRC server and your Catalyst app so that
54you can test it with SeleniumRC. Once you've called
6fd21f2e 55C<< Test::WWW::Selenium::Catalyst->start >>, everything is just like
1cdeb21f 56L<Test::WWW::Selenium|Test::WWW:Selenium>.
57
cba20015 58=head1 METHODS
4f6d213e 59
374611f4 60=head2 start(\%args)
4f6d213e 61
374611f4 62Starts the Selenium and Catalyst servers, and returns a pre-initialized,
63ready-to-use Test::WWW::Selenium object.
4f6d213e 64
374611f4 65Arguments:
66
67=over
68
69=item app_uri
70
71URI at which the application can be reached. If this is specified then no
72application server will be started.
73
74=item port
75
76Port on which to run the catalyst application server. The C<MYAPP_PORT>
77environment variable is also respected.
78
79
80=item selenium_class
81
82B<Default>: Test::WWW::Selenium
83
84Classname of Selenium object to create. Use this if you want to subclass
85selenium to add custom logic.
86
87=item selenium_host
88
89=item selenium_port
90
91Location of externally running selenium server if you do not wish this module
92to control one. See also below for details.
93
94=back
95
96All other options passed verbatim to the selenium constructor.
97
98B<NOTE>: By default a selenium server is started when you C<use> this module, and
99it's killed when your test exits. If wish to manage a selenium server yourself,
100(for instance you wish to start up a server once and run a number of tests
101against it) pass C<-no_selenium_server> to import:
102
103 use Test::WWW::Selenium 'MyApp'
104 -no_selenium_server = 1
105
106Along a similar vein you can also pass command line arguments to the selenium
107server via C<-selenium_args>:
108
109 use Test::WWW::Selenium 'MyApp'
110 -selenium_args = "-singleWindow -port 4445"
4f6d213e 111
112=head2 sel_pid
113
114Returns the process ID of the Selenium Server.
115
116=head2 app_pid
117
118Returns the process ID of the Catalyst server.
119
120=cut
121
122
123sub _start_server {
374611f4 124 my ($class, $args) = @_;
4f6d213e 125 # fork off a selenium server
126 my $pid;
127 if(0 == ($pid = fork())){
374611f4 128 local $SIG{TERM} = sub {
129 diag("Selenium server $$ going down (TERM)") if $DEBUG;
130 exit 0;
131 };
132
133 chdir '/';
134
135 if(!$DEBUG){
136 close *STDERR;
137 close *STDOUT;
138 #close *STDIN;
139 }
140
141 diag("Selenium running in $$") if $DEBUG;
142 $class->_start_selenium($args);
143 diag("Selenium server $$ going down") if $DEBUG;
144 exit 1;
4f6d213e 145 }
146 $sel_pid = $pid;
147}
148
374611f4 149# Moved out to be subclassable seperately to the fork logic
150sub _start_selenium {
151 my ($class, $arg) = @_;
152 $arg = '' unless defined $arg;
153 Alien::SeleniumRC::start($arg)
154 or croak "Can't start Selenium server";
155}
156
4f6d213e 157sub sel_pid {
158 return $sel_pid;
159}
160
161sub app_pid {
162 return $app_pid;
163}
164
165sub import {
374611f4 166 my ($class, $appname, %args) = @_;
167
4f6d213e 168 croak q{Specify your app's name} if !$appname;
169 $app = $appname;
170
171 my $d = $ENV{Catalyst::Utils::class2env($appname). "_DEBUG"}; # MYAPP_DEBUG
374611f4 172 if(defined $d){
173 $DEBUG = $d;
4f6d213e 174 }
374611f4 175
176 unless ($args{-no_seleniun_server}) {
177 $class->_start_server($args{-selenium_args}) or croak "Couldn't start selenium server";
4f6d213e 178 }
4f6d213e 179 return 1;
180}
181
182sub start {
183 my $class = shift;
184 my $args = shift || {};
374611f4 185
186 my $port = delete $args->{port};
187 $port ||= $ENV{Catalyst::Utils::class2env($app). "_PORT"} # MYAPP_PORT
188 || 3000;
189
190 my $uri;
191
192 # Check for CATALYST_SERVER env var like TWMC does.
193 if ( $ENV{CATALYST_SERVER} ) {
194 $uri = $ENV{CATALYST_SERVER};
195 } elsif ( $args->{app_uri} ) {
196 $uri = delete $args->{app_uri}
197 } else {
198 # start a Catalyst MyApp server
199 eval("use $app");
200 croak "Couldn't load $app: $@" if $@;
201
202 my $pid;
203 if(0 == ($pid = fork())){
204 local $SIG{TERM} = sub {
205 diag("Catalyst server $$ going down (TERM)") if $DEBUG;
206 exit 0;
207 };
208 diag("Catalyst server running in $$") if $DEBUG;
209 $app->run($port, 'localhost');
210 exit 1;
211 }
212 $uri = 'http://localhost:' . $port;
213 $app_pid = $pid;
4f6d213e 214 }
4f6d213e 215
216 my $tries = 5;
217 my $error;
374611f4 218 my $sel_class = delete $args->{selenium_class} || 'Test::WWW::Selenium';
4f6d213e 219 my $sel;
374611f4 220
4f6d213e 221 while(!$sel && $tries--){
374611f4 222 sleep 1;
223 diag("Waiting for selenium server to start")
224 if $DEBUG;
225
226 eval {
227 $sel = $sel_class->new(
228 host => delete $args->{selenium_host} || 'localhost',
229 port => delete $args->{selenium_port} || 4444,
230 browser => '*firefox',
231 browser_url => $uri,
232 auto_stop => 0,
233 %$args
234 );
235 };
236 $error = $@;
4f6d213e 237 }
38cd3a5c 238 croak "Can't start selenium: $error" if $error;
4f6d213e 239
38cd3a5c 240 return $www_selenium = $sel;
4f6d213e 241}
242
243END {
244 if($sel_pid){
374611f4 245 if($www_selenium){
246 diag("Shutting down Selenium Server $sel_pid") if $DEBUG;
247 $www_selenium->do_command('shutDown');
248 undef $www_selenium;
249 }
250 diag("Killing Selenium Server $sel_pid") if $DEBUG;
251 kill 15, $sel_pid or diag "Killing Selenium: $!";
252 undef $sel_pid;
4f6d213e 253 }
254 if($app_pid){
374611f4 255 diag("Killing catalyst server $app_pid") if $DEBUG;
256 kill 15, $app_pid or diag "Killing MyApp: $!";
257 undef $app_pid;
4f6d213e 258 }
259 diag("Waiting for children to die") if $DEBUG;
260 waitpid $sel_pid, 0 if $sel_pid;
261 waitpid $app_pid, 0 if $app_pid;
262}
263
264
265=head1 ENVIRONMENT
266
267Debugging messages are shown if C<CATALYST_DEBUG> or C<MYAPP_DEBUG>
268are set. C<MYAPP> is the name of your application, uppercased. (This
269is the same syntax as Catalyst itself.)
270
374611f4 271C<CATALYST_SERVER> can be set to test against an externally running server,
272in a similar manner to how L<Test::WWW::Mechanize::Catalyst> behaves.
273
274The port that the application sever runs on can be affected by C<MYAPP_PORT>
275in addition to being specifiable in the arguments passed to start.
276
4f6d213e 277=head1 DIAGNOSTICS
278
279=head2 Specify your app's name
280
281You need to pass your Catalyst app's name as the argument to the use
282statement:
283
284 use Test::WWW::Selenium::Catalyst 'MyApp'
285
286C<MyApp> is the name of your Catalyst app.
287
1cdeb21f 288=head1 SEE ALSO
289
290=over 4
291
292=item *
293
294Selenium website: L<http://www.openqa.org/>
295
296=item *
297
298Description of what you can do with the C<$sel> object: L<Test::WWW::Selenium>
299
300=item *
301
302If you don't need a real web browser: L<Test::WWW::Mechanize::Catalyst>
303
304=back
305
4f6d213e 306=head1 AUTHOR
307
308Jonathan Rockway, C<< <jrockway at cpan.org> >>
309
310=head1 BUGS
311
312Please report any bugs or feature requests to
313C<bug-test-www-selenium-catalyst at rt.cpan.org>, or through the web interface at
314L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-WWW-Selenium-Catalyst>.
315I will be notified, and then you'll automatically be notified of progress on
316your bug as I make changes.
317
cba20015 318=head1 PATCHES
4f6d213e 319
cba20015 320Send me unified diffs against the git HEAD at:
4f6d213e 321
cba20015 322 git://git.jrock.us/Test-WWW-Selenium-Catalyst
4f6d213e 323
cba20015 324You can view the repository online at
4f6d213e 325
cba20015 326 http://git.jrock.us/?p=Test-WWW-Selenium-Catalyst.git;a=summary
4f6d213e 327
cba20015 328Thanks in advance for your contributions!
4f6d213e 329
330=head1 ACKNOWLEDGEMENTS
331
1cdeb21f 332Thanks for mst for getting on my case to actually write this thing :)
333
4f6d213e 334=head1 COPYRIGHT & LICENSE
335
336Copyright 2006 Jonathan Rockway, all rights reserved.
337
338This program is free software; you can redistribute it and/or modify it
339under the same terms as Perl itself.
340
341=cut
342
3431; # End of Test::WWW::Selenium::Catalyst