File Coverage

blib/lib/Test/WWW/Mechanize/PSGI.pm
Criterion Covered Total %
statement 49 49 100.0
branch 10 12 83.3
condition 1 3 33.3
subroutine 10 10 100.0
pod 2 2 100.0
total 72 76 94.7


line stmt bran cond sub pod time code
1             package Test::WWW::Mechanize::PSGI;
2              
3 3     3   168119 use strict;
  3         25  
  3         72  
4 3     3   14 use warnings;
  3         11  
  3         154  
5              
6             our $VERSION = '0.39';
7              
8 3     3   17 use Carp qw( confess );
  3         6  
  3         280  
9 3     3   1087 use HTTP::Message::PSGI (); # adds from_psgi() to HTTP::Response
  3         56616  
  3         73  
10 3     3   18 use Try::Tiny qw( catch try );
  3         6  
  3         132  
11              
12 3     3   15 use base 'Test::WWW::Mechanize';
  3         5  
  3         1587  
13              
14             my $Test = Test::Builder->new();
15              
16             sub new {
17 7     7 1 18109 my $class = shift;
18 7         25 my %args = @_;
19              
20             # Dont let LWP complain about options for our attributes
21 7         14 my $app = $args{app};
22 7         15 delete $args{app};
23 7         14 my $env = delete $args{env};
24 7 50       21 confess('Missing argument app') unless $app;
25 7 50 33     45 confess('Argument app should be a code reference')
26             unless ref($app) && ref($app) eq 'CODE';
27              
28 7         48 my $self = $class->SUPER::new(%args);
29 7         36306 $self->{app} = $app;
30 7 100       24 $self->{env} = $env if $env;
31 7         21 return $self;
32             }
33              
34             sub simple_request {
35 8     8 1 31108 my ( $self, $request ) = @_;
36              
37 8         30 my $uri = $request->uri;
38 8 100       87 $uri->scheme('http') unless defined $uri->scheme;
39 8 100       7996 $uri->host('localhost') unless defined $uri->host;
40              
41             my $env = $self->prepare_request($request)
42 8 100       737 ->to_psgi( %{ $self->{env} || {} } );
  8         4110  
43 8         6666 $self->run_handlers( 'request_send', $request );
44 8         157 my $response;
45             try {
46 8     8   344 $response = HTTP::Response->from_psgi( $self->{app}->($env) );
47             }
48             catch {
49 1     1   24 $Test->diag("PSGI error: $_");
50 1         328 $response = HTTP::Response->new(500);
51 1         42 $response->content($_);
52 1         23 $response->content_type(q{});
53 8         89 };
54 8         1678 $response->request($request);
55              
56             # Trigger set_my_handler call in LWP::UserAgent::parse_head()
57 8         85 $self->run_handlers( 'response_header', $response );
58              
59             # Running the reponse_data handlers via run_handlers() doesn't pass all of
60             # the args that are required, so we'll run each handler explicitly. We do
61             # this here so that we can fire off the handler which gets added in
62             # LWP::UserAgent::parse_head(). Without this handler firing,
63             # LWP::UserAgent::base() will not know about any URL which may have been
64             # set in a tag. If we don't know about this tag then we
65             # may end up with an incorrect base URL and, by extension, we could end up
66             # at the wrong location when trying to follow relative URLs.
67              
68 8         3333 for my $handler ( $self->handlers( 'response_data', $response ) ) {
69             $handler->{callback}
70 4         105 ->( $response, $self, $handler, $response->content );
71             }
72              
73 8         873 $self->run_handlers( 'response_done', $response );
74 8         1891 return $response;
75             }
76              
77             1;
78              
79             =pod
80              
81             =encoding UTF-8
82              
83             =head1 NAME
84              
85             Test::WWW::Mechanize::PSGI - Test PSGI programs using WWW::Mechanize
86              
87             =head1 VERSION
88              
89             version 0.39
90              
91             =head1 SYNOPSIS
92              
93             # We're in a t/*.t test script...
94             use Test::WWW::Mechanize::PSGI;
95              
96             my $mech = Test::WWW::Mechanize::PSGI->new(
97             app => sub {
98             my $env = shift;
99             return [
100             200,
101             [ 'Content-Type' => 'text/html' ],
102             [ 'HiHello World'
103             ]
104             ];
105             },
106             );
107             $mech->get_ok('/');
108             is( $mech->ct, 'text/html', 'Is text/html' );
109             $mech->title_is('Hi');
110             $mech->content_contains('Hello World');
111             # ... and all other Test::WWW::Mechanize methods
112              
113             =head1 DESCRIPTION
114              
115             L is a specification to decouple web server environments from
116             web application framework code. L is a subclass
117             of L that incorporates features for web application
118             testing. The L module meshes the two to
119             allow easy testing of L applications.
120              
121             Testing web applications has always been a bit tricky, normally
122             requiring starting a web server for your application and making real HTTP
123             requests to it. This module allows you to test L web
124             applications but does not require a server or issue HTTP
125             requests. Instead, it passes the HTTP request object directly to
126             L. Thus you do not need to use a real hostname:
127             "http://localhost/" will do. However, this is optional. The following
128             two lines of code do exactly the same thing:
129              
130             $mech->get_ok('/action');
131             $mech->get_ok('http://localhost/action');
132              
133             This makes testing fast and easy. L provides
134             functions for common web testing scenarios. For example:
135              
136             $mech->get_ok( $page );
137             $mech->title_is( "Invoice Status", "Make sure we're on the invoice page" );
138             $mech->content_contains( "Andy Lester", "My name somewhere" );
139             $mech->content_like( qr/(cpan|perl)\.org/, "Link to perl.org or CPAN" );
140              
141             An alternative to this module is L.
142              
143             =head1 CONSTRUCTOR
144              
145             =head2 new
146              
147             Behaves like, and calls, L's C method. You should pass
148             in your application:
149              
150             my $mech = Test::WWW::Mechanize::PSGI->new(
151             app => sub {
152             my $env = shift;
153             return [ 200, [ 'Content-Type' => 'text/plain' ], ['Hello World'] ],;
154             },
155             env => { REMOTE_USER => 'Foo Bar' },
156             );
157              
158             =head1 METHODS: HTTP VERBS
159              
160             =head2 $mech->get_ok($url, [ \%LWP_options ,] $desc)
161              
162             A wrapper around WWW::Mechanize's get(), with similar options, except
163             the second argument needs to be a hash reference, not a hash. Like
164             well-behaved C<*_ok()> functions, it returns true if the test passed,
165             or false if not.
166              
167             A default description of "GET $url" is used if none if provided.
168              
169             =head2 $mech->head_ok($url, [ \%LWP_options ,] $desc)
170              
171             A wrapper around WWW::Mechanize's head(), with similar options, except
172             the second argument needs to be a hash reference, not a hash. Like
173             well-behaved C<*_ok()> functions, it returns true if the test passed,
174             or false if not.
175              
176             A default description of "HEAD $url" is used if none if provided.
177              
178             =head2 $mech->post_ok( $url, [ \%LWP_options ,] $desc )
179              
180             A wrapper around WWW::Mechanize's post(), with similar options, except
181             the second argument needs to be a hash reference, not a hash. Like
182             well-behaved C<*_ok()> functions, it returns true if the test passed,
183             or false if not.
184              
185             A default description of "POST to $url" is used if none if provided.
186              
187             =head2 $mech->put_ok( $url, [ \%LWP_options ,] $desc )
188              
189             A wrapper around WWW::Mechanize's put(), with similar options, except
190             the second argument needs to be a hash reference, not a hash. Like
191             well-behaved C<*_ok()> functions, it returns true if the test passed,
192             or false if not.
193              
194             A default description of "PUT to $url" is used if none if provided.
195              
196             =head2 $mech->submit_form_ok( \%params [, $desc] )
197              
198             Makes a C call and executes tests on the results.
199             The form must be found, and then submitted successfully. Otherwise,
200             this test fails.
201              
202             I<%params> is a hashref containing the params to pass to C.
203             Note that the params to C are a hash whereas the params to
204             this function are a hashref. You have to call this function like:
205              
206             $agent->submit_form_ok({
207             form_number => 3,
208             fields => {
209             username => 'mungo',
210             password => 'lost-and-alone',
211             }
212             }, "looking for 3rd form" );
213              
214             As with other test functions, C<$desc> is optional. If it is supplied
215             then it will display when running the test harness in verbose mode.
216              
217             Returns true value if the specified link was found and followed
218             successfully. The L object returned by submit_form()
219             is not available.
220              
221             =head2 $mech->follow_link_ok( \%params [, $desc] )
222              
223             Makes a C call and executes tests on the results.
224             The link must be found, and then followed successfully. Otherwise,
225             this test fails.
226              
227             I<%params> is a hashref containing the params to pass to C.
228             Note that the params to C are a hash whereas the params to
229             this function are a hashref. You have to call this function like:
230              
231             $mech->follow_link_ok( {n=>3}, "looking for 3rd link" );
232              
233             As with other test functions, C<$desc> is optional. If it is supplied
234             then it will display when running the test harness in verbose mode.
235              
236             Returns a true value if the specified link was found and followed
237             successfully. The L object returned by follow_link()
238             is not available.
239              
240             =head2 click_ok( $button[, $desc] )
241              
242             Clicks the button named by C<$button>. An optional C<$desc> can
243             be given for the test.
244              
245             =head1 METHODS: CONTENT CHECKING
246              
247             =head2 $mech->html_lint_ok( [$desc] )
248              
249             Checks the validity of the HTML on the current page. If the page is not
250             HTML, then it fails. The URI is automatically appended to the I<$desc>.
251              
252             Note that HTML::Lint must be installed for this to work. Otherwise,
253             it will blow up.
254              
255             =head2 $mech->title_is( $str [, $desc ] )
256              
257             Tells if the title of the page is the given string.
258              
259             $mech->title_is( "Invoice Summary" );
260              
261             =head2 $mech->title_like( $regex [, $desc ] )
262              
263             Tells if the title of the page matches the given regex.
264              
265             $mech->title_like( qr/Invoices for (.+)/
266              
267             =head2 $mech->title_unlike( $regex [, $desc ] )
268              
269             Tells if the title of the page matches the given regex.
270              
271             $mech->title_unlike( qr/Invoices for (.+)/
272              
273             =head2 $mech->base_is( $str [, $desc ] )
274              
275             Tells if the base of the page is the given string.
276              
277             $mech->base_is( "http://example.com/" );
278              
279             =head2 $mech->base_like( $regex [, $desc ] )
280              
281             Tells if the base of the page matches the given regex.
282              
283             $mech->base_like( qr{http://example.com/index.php?PHPSESSID=(.+)});
284              
285             =head2 $mech->base_unlike( $regex [, $desc ] )
286              
287             Tells if the base of the page matches the given regex.
288              
289             $mech->base_unlike( qr{http://example.com/index.php?PHPSESSID=(.+)});
290              
291             =head2 $mech->content_is( $str [, $desc ] )
292              
293             Tells if the content of the page matches the given string
294              
295             =head2 $mech->content_contains( $str [, $desc ] )
296              
297             Tells if the content of the page contains I<$str>.
298              
299             =head2 $mech->content_lacks( $str [, $desc ] )
300              
301             Tells if the content of the page lacks I<$str>.
302              
303             =head2 $mech->content_like( $regex [, $desc ] )
304              
305             Tells if the content of the page matches I<$regex>.
306              
307             =head2 $mech->content_unlike( $regex [, $desc ] )
308              
309             Tells if the content of the page does NOT match I<$regex>.
310              
311             =head2 $mech->has_tag( $tag, $text [, $desc ] )
312              
313             Tells if the page has a C<$tag> tag with the given content in its text.
314              
315             =head2 $mech->has_tag_like( $tag, $regex [, $desc ] )
316              
317             Tells if the page has a C<$tag> tag with the given content in its text.
318              
319             =head2 $mech->followable_links()
320              
321             Returns a list of links that L can follow. This is only http
322             and https links.
323              
324             =head2 $mech->page_links_ok( [ $desc ] )
325              
326             Follow all links on the current page and test for HTTP status 200
327              
328             $mech->page_links_ok('Check all links');
329              
330             =head2 $mech->page_links_content_like( $regex [, $desc ] )
331              
332             Follow all links on the current page and test their contents for I<$regex>.
333              
334             $mech->page_links_content_like( qr/foo/,
335             'Check all links contain "foo"' );
336              
337             =head2 $mech->links_ok( $links [, $desc ] )
338              
339             Follow specified links on the current page and test for HTTP status
340             200. The links may be specified as a reference to an array containing
341             L objects, an array of URLs, or a scalar URL
342             name.
343              
344             my @links = $mech->find_all_links( url_regex => qr/cnn\.com$/ );
345             $mech->links_ok( \@links, 'Check all links for cnn.com' );
346              
347             my @links = qw( index.html search.html about.html );
348             $mech->links_ok( \@links, 'Check main links' );
349              
350             $mech->links_ok( 'index.html', 'Check link to index' );
351              
352             =head2 $mech->link_status_is( $links, $status [, $desc ] )
353              
354             Follow specified links on the current page and test for HTTP status
355             passed. The links may be specified as a reference to an array
356             containing L objects, an array of URLs, or a
357             scalar URL name.
358              
359             my @links = $mech->followable_links();
360             $mech->link_status_is( \@links, 403,
361             'Check all links are restricted' );
362              
363             =head2 $mech->link_status_isnt( $links, $status [, $desc ] )
364              
365             Follow specified links on the current page and test for HTTP status
366             passed. The links may be specified as a reference to an array
367             containing L objects, an array of URLs, or a
368             scalar URL name.
369              
370             my @links = $mech->followable_links();
371             $mech->link_status_isnt( \@links, 404,
372             'Check all links are not 404' );
373              
374             =head2 $mech->link_content_like( $links, $regex [, $desc ] )
375              
376             Follow specified links on the current page and test the resulting
377             content of each against I<$regex>. The links may be specified as a
378             reference to an array containing L objects, an
379             array of URLs, or a scalar URL name.
380              
381             my @links = $mech->followable_links();
382             $mech->link_content_like( \@links, qr/Restricted/,
383             'Check all links are restricted' );
384              
385             =head2 $mech->link_content_unlike( $links, $regex [, $desc ] )
386              
387             Follow specified links on the current page and test that the resulting
388             content of each does not match I<$regex>. The links may be specified as a
389             reference to an array containing L objects, an array
390             of URLs, or a scalar URL name.
391              
392             my @links = $mech->followable_links();
393             $mech->link_content_unlike( \@links, qr/Restricted/,
394             'No restricted links' );
395              
396             =head2 $mech->stuff_inputs( [\%options] )
397              
398             Finds all free-text input fields (text, textarea, and password) in the
399             current form and fills them to their maximum length in hopes of finding
400             application code that can't handle it. Fields with no maximum length
401             and all textarea fields are set to 66000 bytes, which will often be
402             enough to overflow the data's eventual receptacle.
403              
404             There is no return value.
405              
406             If there is no current form then nothing is done.
407              
408             The hashref $options can contain the following keys:
409              
410             =over
411              
412             =item * ignore
413              
414             hash value is arrayref of field names to not touch, e.g.:
415              
416             $mech->stuff_inputs( {
417             ignore => [qw( specialfield1 specialfield2 )],
418             } );
419              
420             =item * fill
421              
422             hash value is default string to use when stuffing fields. Copies
423             of the string are repeated up to the max length of each field. E.g.:
424              
425             $mech->stuff_inputs( {
426             fill => '@' # stuff all fields with something easy to recognize
427             } );
428              
429             =item * specs
430              
431             hash value is arrayref of hashrefs with which you can pass detailed
432             instructions about how to stuff a given field. E.g.:
433              
434             $mech->stuff_inputs( {
435             specs=>{
436             # Some fields are datatype-constrained. It's most common to
437             # want the field stuffed with valid data.
438             widget_quantity => { fill=>'9' },
439             notes => { maxlength=>2000 },
440             }
441             } );
442              
443             The specs allowed are I (use this fill for the field rather than
444             the default) and I (use this as the field's maxlength instead
445             of any maxlength specified in the HTML).
446              
447             =back
448              
449             =head1 AUTHOR
450              
451             Leon Brocard
452              
453             =head1 COPYRIGHT AND LICENSE
454              
455             This software is copyright (c) 2009 by Leon Brocard.
456              
457             This is free software; you can redistribute it and/or modify it under
458             the same terms as the Perl 5 programming language system itself.
459              
460             =cut
461              
462             __END__