update version to 0.02
[catagits/Test-WWW-Selenium-Catalyst.git] / lib / Test / WWW / Selenium / Catalyst.pm
1 package Test::WWW::Selenium::Catalyst;
2
3 use warnings;
4 use strict;
5 use Carp;
6 use Alien::SeleniumRC;
7 use Test::WWW::Selenium;
8 use Test::More;
9 use Catalyst::Utils;
10
11 BEGIN { $ENV{CATALYST_ENGINE} ||= 'HTTP'; }
12
13 local $SIG{CHLD} = 'IGNORE';
14
15 my $DEBUG = $ENV{CATALYST_DEBUG};
16 my $app; # app name (MyApp)
17 my $sel_pid; # pid of selenium server
18 my $app_pid; # pid of myapp server
19 my $www_selenium;
20
21 =head1 NAME
22
23 Test::WWW::Selenium::Catalyst - Test your Catalyst application with Selenium
24
25 =cut
26
27 our $VERSION = '0.02';
28
29 =head1 DEVELOPERISH RELEASE
30
31 This is still a test release.  It's working for me in production, but
32 it depends on a Java application (SeleniumRC), which can be
33 unreliable.  On my Debian system, I had to put C<firefox-bin> in my
34 path, and add C</usr/lib/firefox> to C<LD_LIBRARY_PATH>.  Every distro
35 and OS is different, so I'd like some feedback on how this works on
36 your system.  I would like to find a clean solution that lets this
37 module "Just Work" for everyone, but I have a feeling that it's going
38 to look more like C<if(gentoo){ ... } elsif (debian) { ... }> and so
39 on.  I can live with that, but I need your help to get to that stage!
40
41 Please report any problems to RT, the Catalyst mailing list, or the
42 #catalyst IRC channel on L<irc.perl.org>.  Thanks!
43
44 =head1 SYNOPSIS
45
46     use Test::WWW::Selenium::Catalyst 'MyApp';
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
53 This module starts the SeleniumRC server and your Catalyst app so that
54 you can test it with SeleniumRC.  Once you've called
55 C<< Test::WWW::Selenium::Catalyst->start >>, everything is just like
56 L<Test::WWW::Selenium|Test::WWW:Selenium>.
57
58 =head1 METHODS
59
60 =head2 start
61
62 Starts the Selenium and Catalyst servers, and returns a
63 pre-initialized, ready-to-use Test::WWW::Selenium object.
64
65 [NOTE] The selenium server is actually started when you C<use> this
66 module, and it's killed when your test exits.
67
68 =head2 sel_pid
69
70 Returns the process ID of the Selenium Server.
71
72 =head2 app_pid
73
74 Returns the process ID of the Catalyst server.
75
76 =cut
77
78
79 sub _start_server {
80     # fork off a selenium server
81     my $pid;
82     if(0 == ($pid = fork())){
83         local $SIG{TERM} = sub {
84             diag("Selenium server $$ going down (TERM)") if $DEBUG;
85             exit 0;
86         };
87         
88         chdir '/';
89         
90         if(!$DEBUG){
91             close *STDERR;
92             close *STDOUT;
93             #close *STDIN;
94         }
95         
96         diag("Selenium running in $$") if $DEBUG;
97         Alien::SeleniumRC::start()
98             or croak "Can't start Selenium server";
99         diag("Selenium server $$ going down") if $DEBUG;
100         exit 1;
101     }
102     $sel_pid = $pid;
103 }
104
105 sub sel_pid {
106     return $sel_pid;
107 }
108
109 sub app_pid {
110     return $app_pid;
111 }
112
113 sub import {
114     my ($class, $appname) = @_;
115     croak q{Specify your app's name} if !$appname;
116     $app = $appname;
117     
118     my $d = $ENV{Catalyst::Utils::class2env($appname). "_DEBUG"}; # MYAPP_DEBUG 
119     if(defined $d && $d){
120         $DEBUG = 1;
121     }
122     elsif(defined $d && $d == 0){
123         $DEBUG = 0;
124     }
125     # if it's something else, leave the CATALYST_DEBUG setting in tact
126     
127     _start_server() or croak "Couldn't start selenium server";
128     return 1;
129 }
130
131 sub start {
132     my $class = shift;
133     my $args  = shift || {};
134     
135     # start a Catalyst MyApp server
136     eval("use $app");
137     croak "Couldn't load $app: $@" if $@;
138     
139     my $pid;
140     if(0 == ($pid = fork())){
141         local $SIG{TERM} = sub {
142             diag("Catalyst server $$ going down (TERM)") if $DEBUG;
143             exit 0;
144         };
145         diag("Catalyst server running in $$") if $DEBUG;
146         $app->run('3000', 'localhost');
147         exit 1;
148     }
149     $app_pid = $pid;
150     
151     my $tries = 5;
152     my $error;
153     my $sel;
154     while(!$sel && $tries--){ 
155         sleep 1;
156         diag("Waiting for selenium server to start")
157           if $DEBUG;
158         
159         eval {
160             $sel = Test::WWW::Selenium->
161               new(host => 'localhost',
162                   port => 4444,
163                   browser => $args->{browser} || '*firefox',
164                   browser_url => 'http://localhost:3000/',
165                   auto_stop => 0,
166                  );
167         };
168         $error = $@;
169     }
170     croak "Can't start selenium: $error" if $error;
171     
172     return $www_selenium = $sel;
173 }
174
175 END {
176     if($www_selenium){
177         diag("Shutting down Selenium Server $sel_pid") if $DEBUG;
178         $www_selenium->do_command('shutDown');
179         undef $www_selenium;
180     }
181     if($sel_pid){
182         diag("Killing Selenium Server $sel_pid") if $DEBUG;
183         kill 15, $sel_pid or diag "Killing Selenium: $!";
184         undef $sel_pid;
185     }
186     if($app_pid){
187         diag("Killing catalyst server $app_pid") if $DEBUG;
188         kill 15, $app_pid or diag "Killing MyApp: $!";
189         undef $app_pid;
190     }
191     diag("Waiting for children to die") if $DEBUG;
192     waitpid $sel_pid, 0 if $sel_pid;
193     waitpid $app_pid, 0 if $app_pid;
194 }
195
196
197 =head1 ENVIRONMENT
198
199 Debugging messages are shown if C<CATALYST_DEBUG> or C<MYAPP_DEBUG>
200 are set.  C<MYAPP> is the name of your application, uppercased.  (This
201 is the same syntax as Catalyst itself.)
202
203 =head1 DIAGNOSTICS
204
205 =head2 Specify your app's name
206
207 You need to pass your Catalyst app's name as the argument to the use
208 statement:
209
210     use Test::WWW::Selenium::Catalyst 'MyApp'
211
212 C<MyApp> is the name of your Catalyst app.
213
214 =head1 SEE ALSO
215
216 =over 4 
217
218 =item * 
219
220 Selenium website: L<http://www.openqa.org/>
221
222 =item * 
223
224 Description of what you can do with the C<$sel> object: L<Test::WWW::Selenium>
225
226 =item * 
227
228 If you don't need a real web browser: L<Test::WWW::Mechanize::Catalyst>
229
230 =back
231
232 =head1 AUTHOR
233
234 Jonathan Rockway, C<< <jrockway at cpan.org> >>
235
236 =head1 BUGS
237
238 Please report any bugs or feature requests to
239 C<bug-test-www-selenium-catalyst at rt.cpan.org>, or through the web interface at
240 L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-WWW-Selenium-Catalyst>.
241 I will be notified, and then you'll automatically be notified of progress on
242 your bug as I make changes.
243
244 =head1 PATCHES
245
246 Send me unified diffs against the git HEAD at:
247
248     git://git.jrock.us/Test-WWW-Selenium-Catalyst
249
250 You can view the repository online at 
251
252     http://git.jrock.us/?p=Test-WWW-Selenium-Catalyst.git;a=summary
253
254 Thanks in advance for your contributions!
255
256 =head1 ACKNOWLEDGEMENTS
257
258 Thanks for mst for getting on my case to actually write this thing :)
259
260 =head1 COPYRIGHT & LICENSE
261
262 Copyright 2006 Jonathan Rockway, all rights reserved.
263
264 This program is free software; you can redistribute it and/or modify it
265 under the same terms as Perl itself.
266
267 =cut
268
269 1; # End of Test::WWW::Selenium::Catalyst