(very) minor doco change
[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
7278917e 27our $VERSION = '0.06';
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
8f552b3b 76B<Default>: 3000
77
374611f4 78Port on which to run the catalyst application server. The C<MYAPP_PORT>
79environment variable is also respected.
80
81
82=item selenium_class
83
84B<Default>: Test::WWW::Selenium
85
86Classname of Selenium object to create. Use this if you want to subclass
87selenium to add custom logic.
88
89=item selenium_host
90
91=item selenium_port
92
93Location of externally running selenium server if you do not wish this module
8f552b3b 94to control one. See also for details.
374611f4 95
96=back
97
98All other options passed verbatim to the selenium constructor.
99
8f552b3b 100B<NOTE>: By default a selenium server is started when you C<use> this module,
101and it's killed when your test exits. If wish to manage a selenium server
102yourself, (for instance you wish to start up a server once and run a number of
103tests against it) pass C<-no_selenium_server> to import:
374611f4 104
3a859d5c 105 use Test::WWW::Selenium 'MyApp',
8f552b3b 106 -no_selenium_server => 1
374611f4 107
108Along a similar vein you can also pass command line arguments to the selenium
109server via C<-selenium_args>:
110
3a859d5c 111 use Test::WWW::Selenium 'MyApp',
8f552b3b 112 -selenium_args => "-singleWindow -port 4445"
4f6d213e 113
114=head2 sel_pid
115
116Returns the process ID of the Selenium Server.
117
118=head2 app_pid
119
120Returns the process ID of the Catalyst server.
121
122=cut
123
124
125sub _start_server {
374611f4 126 my ($class, $args) = @_;
4f6d213e 127 # fork off a selenium server
128 my $pid;
129 if(0 == ($pid = fork())){
374611f4 130 local $SIG{TERM} = sub {
131 diag("Selenium server $$ going down (TERM)") if $DEBUG;
132 exit 0;
133 };
134
135 chdir '/';
136
137 if(!$DEBUG){
138 close *STDERR;
139 close *STDOUT;
140 #close *STDIN;
141 }
142
143 diag("Selenium running in $$") if $DEBUG;
144 $class->_start_selenium($args);
145 diag("Selenium server $$ going down") if $DEBUG;
146 exit 1;
4f6d213e 147 }
148 $sel_pid = $pid;
149}
150
374611f4 151# Moved out to be subclassable seperately to the fork logic
152sub _start_selenium {
153 my ($class, $arg) = @_;
154 $arg = '' unless defined $arg;
155 Alien::SeleniumRC::start($arg)
156 or croak "Can't start Selenium server";
157}
158
4f6d213e 159sub sel_pid {
160 return $sel_pid;
161}
162
163sub app_pid {
164 return $app_pid;
165}
166
167sub import {
374611f4 168 my ($class, $appname, %args) = @_;
169
4f6d213e 170 croak q{Specify your app's name} if !$appname;
171 $app = $appname;
172
173 my $d = $ENV{Catalyst::Utils::class2env($appname). "_DEBUG"}; # MYAPP_DEBUG
374611f4 174 if(defined $d){
175 $DEBUG = $d;
4f6d213e 176 }
28900118 177
178 $args{-selenium_args} ||= '-singleWindow';
179
180 if ($ENV{SELENIUM_SERVER}) {
181 $args{-no_selenium_server} = 1;
182 }
183 elsif ($ENV{SELENIUM_PORT}) {
184 $args{-selenium_args} .= " -port " . $ENV{SELENIUM_PORT};
185 }
374611f4 186
0744bdf6 187 unless ($args{-no_selenium_server}) {
374611f4 188 $class->_start_server($args{-selenium_args}) or croak "Couldn't start selenium server";
4f6d213e 189 }
4f6d213e 190 return 1;
191}
192
193sub start {
194 my $class = shift;
195 my $args = shift || {};
374611f4 196
197 my $port = delete $args->{port};
198 $port ||= $ENV{Catalyst::Utils::class2env($app). "_PORT"} # MYAPP_PORT
199 || 3000;
200
201 my $uri;
202
203 # Check for CATALYST_SERVER env var like TWMC does.
204 if ( $ENV{CATALYST_SERVER} ) {
205 $uri = $ENV{CATALYST_SERVER};
206 } elsif ( $args->{app_uri} ) {
207 $uri = delete $args->{app_uri}
208 } else {
209 # start a Catalyst MyApp server
210 eval("use $app");
211 croak "Couldn't load $app: $@" if $@;
212
213 my $pid;
214 if(0 == ($pid = fork())){
215 local $SIG{TERM} = sub {
216 diag("Catalyst server $$ going down (TERM)") if $DEBUG;
217 exit 0;
218 };
219 diag("Catalyst server running in $$") if $DEBUG;
220 $app->run($port, 'localhost');
221 exit 1;
222 }
223 $uri = 'http://localhost:' . $port;
224 $app_pid = $pid;
4f6d213e 225 }
4f6d213e 226
227 my $tries = 5;
228 my $error;
374611f4 229 my $sel_class = delete $args->{selenium_class} || 'Test::WWW::Selenium';
4f6d213e 230 my $sel;
374611f4 231
28900118 232 if ($ENV{SELENIUM_SERVER}) {
233 my $uri = $ENV{SELENIUM_SERVER};
234 $uri =~ s!^(?:http://)?!http://!;
235 $uri = new URI($uri);
236 $args->{selenium_host} = $uri->host;
237 $args->{selenium_port} = $uri->port;
238 }
239 elsif ($ENV{SELENIUM_PORT}) {
240 $args->{selenium_port} = $ENV{SELENIUM_PORT};
241 }
242
243 my $sel_host = delete $args->{selenium_host} || 'localhost';
244 my $sel_port = delete $args->{selenium_port} || 4444;
4f6d213e 245 while(!$sel && $tries--){
374611f4 246 sleep 1;
247 diag("Waiting for selenium server to start")
248 if $DEBUG;
249
250 eval {
251 $sel = $sel_class->new(
28900118 252 host => $sel_host,
253 port => $sel_port,
374611f4 254 browser => '*firefox',
255 browser_url => $uri,
256 auto_stop => 0,
257 %$args
258 );
259 };
260 $error = $@;
4f6d213e 261 }
38cd3a5c 262 croak "Can't start selenium: $error" if $error;
4f6d213e 263
38cd3a5c 264 return $www_selenium = $sel;
4f6d213e 265}
266
267END {
268 if($sel_pid){
374611f4 269 if($www_selenium){
270 diag("Shutting down Selenium Server $sel_pid") if $DEBUG;
0744bdf6 271 $www_selenium->stop();
a9714b27 272 # This can fail if a page hasn't been requested yet.
a6c14dc6 273 eval { $www_selenium->do_command('shutDownSeleniumServer') };
374611f4 274 undef $www_selenium;
275 }
276 diag("Killing Selenium Server $sel_pid") if $DEBUG;
277 kill 15, $sel_pid or diag "Killing Selenium: $!";
278 undef $sel_pid;
8f552b3b 279
280 } elsif ($www_selenium) {
281 diag("Stopping Selenium Session $sel_pid") if $DEBUG;
282 $www_selenium->stop();
283 undef $www_selenium;
4f6d213e 284 }
8f552b3b 285
4f6d213e 286 if($app_pid){
374611f4 287 diag("Killing catalyst server $app_pid") if $DEBUG;
288 kill 15, $app_pid or diag "Killing MyApp: $!";
289 undef $app_pid;
4f6d213e 290 }
291 diag("Waiting for children to die") if $DEBUG;
292 waitpid $sel_pid, 0 if $sel_pid;
293 waitpid $app_pid, 0 if $app_pid;
294}
295
296
297=head1 ENVIRONMENT
298
299Debugging messages are shown if C<CATALYST_DEBUG> or C<MYAPP_DEBUG>
300are set. C<MYAPP> is the name of your application, uppercased. (This
301is the same syntax as Catalyst itself.)
302
374611f4 303C<CATALYST_SERVER> can be set to test against an externally running server,
304in a similar manner to how L<Test::WWW::Mechanize::Catalyst> behaves.
305
306The port that the application sever runs on can be affected by C<MYAPP_PORT>
307in addition to being specifiable in the arguments passed to start.
308
4f6d213e 309=head1 DIAGNOSTICS
310
311=head2 Specify your app's name
312
313You need to pass your Catalyst app's name as the argument to the use
314statement:
315
316 use Test::WWW::Selenium::Catalyst 'MyApp'
317
318C<MyApp> is the name of your Catalyst app.
319
1cdeb21f 320=head1 SEE ALSO
321
322=over 4
323
324=item *
325
8f552b3b 326Selenium website: L<http://seleniumhq.org/>
1cdeb21f 327
328=item *
329
330Description of what you can do with the C<$sel> object: L<Test::WWW::Selenium>
8f552b3b 331and L<WWW::Selenium>
1cdeb21f 332
333=item *
334
335If you don't need a real web browser: L<Test::WWW::Mechanize::Catalyst>
336
337=back
338
4f6d213e 339=head1 AUTHOR
340
8f552b3b 341Ash Berlin C<< <ash@cpan.org> >>
342
4f6d213e 343Jonathan Rockway, C<< <jrockway at cpan.org> >>
344
345=head1 BUGS
346
347Please report any bugs or feature requests to
348C<bug-test-www-selenium-catalyst at rt.cpan.org>, or through the web interface at
349L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-WWW-Selenium-Catalyst>.
350I will be notified, and then you'll automatically be notified of progress on
351your bug as I make changes.
352
cba20015 353=head1 PATCHES
4f6d213e 354
cba20015 355Send me unified diffs against the git HEAD at:
4f6d213e 356
8f552b3b 357 git://github.com/jrockway/test-www-selenium-catalyst.git
4f6d213e 358
cba20015 359You can view the repository online at
4f6d213e 360
8f552b3b 361 http://github.com/jrockway/test-www-selenium-catalyst/tree/master
4f6d213e 362
cba20015 363Thanks in advance for your contributions!
4f6d213e 364
365=head1 ACKNOWLEDGEMENTS
366
7278917e 367Thanks for mst for getting on my (jrockway's) case to actually write this thing
368:)
1cdeb21f 369
4f6d213e 370=head1 COPYRIGHT & LICENSE
371
8f552b3b 372Copyright 2009 Ash Berlin, all rights reserved.
373
4f6d213e 374Copyright 2006 Jonathan Rockway, all rights reserved.
375
376This program is free software; you can redistribute it and/or modify it
377under the same terms as Perl itself.
378
379=cut
380
3811; # End of Test::WWW::Selenium::Catalyst