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