File Coverage

blib/lib/Test/WWW/Mechanize/PSGI.pm
Criterion Covered Total %
statement 46 46 100.0
branch 6 8 75.0
condition 1 3 33.3
subroutine 10 10 100.0
pod 2 2 100.0
total 65 69 94.2


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