File Coverage

blib/lib/Test/WWW/Mechanize.pm
Criterion Covered Total %
statement 597 790 75.5
branch 165 266 62.0
condition 44 88 50.0
subroutine 71 90 78.8
pod 61 61 100.0
total 938 1295 72.4


line stmt bran cond sub pod time code
1             package Test::WWW::Mechanize;
2              
3 32     32   2314506 use strict;
  32         254  
  32         814  
4 32     32   160 use warnings;
  32         57  
  32         726  
5 32     32   584 use 5.010;
  32         95  
6              
7             =head1 NAME
8              
9             Test::WWW::Mechanize - Testing-specific WWW::Mechanize subclass
10              
11             =head1 VERSION
12              
13             Version 1.56
14              
15             =cut
16              
17             our $VERSION = '1.56';
18              
19             =head1 SYNOPSIS
20              
21             Test::WWW::Mechanize is a subclass of L that incorporates
22             features for web application testing. For example:
23              
24             use Test::More tests => 5;
25             use Test::WWW::Mechanize;
26              
27             my $mech = Test::WWW::Mechanize->new;
28             $mech->get_ok( $page );
29             $mech->base_is( 'http://petdance.com/', 'Proper ' );
30             $mech->title_is( 'Invoice Status', "Make sure we're on the invoice page" );
31             $mech->text_contains( 'Andy Lester', 'My name somewhere' );
32             $mech->content_like( qr/(cpan|perl)\.org/, 'Link to perl.org or CPAN' );
33              
34             This is equivalent to:
35              
36             use Test::More tests => 5;
37             use WWW::Mechanize;
38              
39             my $mech = WWW::Mechanize->new;
40             $mech->get( $page );
41             ok( $mech->success );
42             is( $mech->base, 'http://petdance.com', 'Proper ' );
43             is( $mech->title, 'Invoice Status', "Make sure we're on the invoice page" );
44             ok( index( $mech->content( format => 'text' ), 'Andy Lester' ) >= 0, 'My name somewhere' );
45             like( $mech->content, qr/(cpan|perl)\.org/, 'Link to perl.org or CPAN' );
46              
47             but has nicer diagnostics if they fail.
48              
49             Default descriptions will be supplied for most methods if you omit them. e.g.
50              
51             my $mech = Test::WWW::Mechanize->new;
52             $mech->get_ok( 'http://petdance.com/' );
53             $mech->base_is( 'http://petdance.com/' );
54             $mech->title_is( 'Invoice Status' );
55             $mech->content_contains( 'Andy Lester' );
56             $mech->content_like( qr/(cpan|perl)\.org/ );
57              
58             results in
59              
60             ok - Got 'http://petdance.com/' ok
61             ok - Base is 'http://petdance.com/'
62             ok - Title is 'Invoice Status'
63             ok - Text contains 'Andy Lester'
64             ok - Content is like '(?-xism:(cpan|perl)\.org)'
65              
66             =cut
67              
68 32     32   13364 use HTML::TokeParser ();
  32         271569  
  32         723  
69 32     32   20401 use WWW::Mechanize ();
  32         3808934  
  32         1058  
70 32         196 use Test::LongString qw(
71             contains_string
72             is_string
73             lacks_string
74             like_string
75             unlike_string
76 32     32   14945 );
  32         62473  
77 32     32   2399 use Test::Builder ();
  32         75  
  32         414  
78 32     32   143 use Carp ();
  32         68  
  32         734  
79 32         2764 use Carp::Assert::More qw(
80             assert_arrayref
81             assert_in
82             assert_is
83             assert_isa
84             assert_nonblank
85 32     32   15073 );
  32         103412  
86              
87 32     32   248 use parent 'WWW::Mechanize';
  32         70  
  32         251  
88              
89             my $TB = Test::Builder->new();
90              
91              
92             =head1 CONSTRUCTOR
93              
94             =head2 new( %args )
95              
96             Behaves like, and calls, L's C method. Any parms
97             passed in get passed to WWW::Mechanize's constructor.
98              
99             You can pass in C<< autolint => 1 >> to make Test::WWW::Mechanize
100             automatically run HTML::Lint after any of the following methods are
101             called. You can also pass in an HTML::Lint object like this:
102              
103             my $lint = HTML::Lint->new( only_types => HTML::Lint::Error::STRUCTURE );
104             my $mech = Test::WWW::Mechanize->new( autolint => $lint );
105              
106             The same is also possible with C<< autotidy => 1 >> to use HTML::Tidy5.
107              
108             =over
109              
110             =item * get_ok()
111              
112             =item * post_ok()
113              
114             =item * submit_form_ok()
115              
116             =item * follow_link_ok()
117              
118             =item * click_ok()
119              
120             =back
121              
122             This means you no longer have to do the following:
123              
124             my $mech = Test::WWW::Mechanize->new();
125             $mech->get_ok( $url, 'Fetch the intro page' );
126             $mech->html_lint_ok( 'Intro page looks OK' );
127              
128             and can simply do
129              
130             my $mech = Test::WWW::Mechanize->new( autolint => 1 );
131             $mech->get_ok( $url, 'Fetch the intro page' );
132              
133             The C<< $mech->get_ok() >> only counts as one test in the test count. Both the
134             main IO operation and the linting must pass for the entire test to pass.
135              
136             You can control autolint and autotidy on the fly with the C
137             and C methods.
138              
139             =cut
140              
141             sub new {
142 37     37 1 5046411 my $class = shift;
143              
144 37         326 my %args = (
145             agent => "Test-WWW-Mechanize/$VERSION",
146             @_
147             );
148              
149 37         143 my $autolint = delete $args{autolint};
150 37         132 my $autotidy = delete $args{autotidy};
151              
152 37         1152 my $self = $class->SUPER::new( %args );
153              
154 37         446 $self->autolint( $autolint );
155 37         125 $self->autotidy( $autotidy );
156              
157 37         113 return $self;
158             }
159              
160              
161             # Override WWW::Mechanize->_reset_page() to handle Test::WWW::Mechanize-specific data.
162             sub _reset_page {
163 426     426   2011595 my $self = shift;
164              
165             # Parent object stuff
166 426         1623 $self->SUPER::_reset_page( @_ );
167              
168 426         5295 $self->{ids} = undef;
169              
170 426         737 return;
171             }
172              
173              
174             =head1 METHODS: HTTP VERBS
175              
176             =head2 $mech->get_ok($url, [ \%LWP_options ,] $desc)
177              
178             A wrapper around WWW::Mechanize's get(), with similar options, except
179             the second argument needs to be a hash reference, not a hash. Like
180             well-behaved C<*_ok()> functions, it returns true if the test passed,
181             or false if not.
182              
183             A default description of "GET $url" is used if none if provided.
184              
185             =cut
186              
187             sub get_ok {
188 44     44 1 212015 my $self = shift;
189              
190 44         516 my ($url,$desc,%opts) = $self->_unpack_args( 'GET', @_ );
191              
192 44         1032 $self->get( $url, %opts );
193 44         927 my $ok = $self->success;
194              
195 44         1163 $ok = $self->_post_load_validation( $ok, $desc );
196              
197 44         169 return $ok;
198             }
199              
200             sub _post_load_validation {
201 53     53   129 my $self = shift;
202 53         104 my $ok = shift;
203 53         113 my $desc = shift;
204              
205 53         294 local $Test::Builder::Level = $Test::Builder::Level + 1;
206              
207 53 100       205 if ( $ok ) {
208 51         104 my $emitted_ok = 0;
209 51 100       165 if ( $self->is_html ) {
210 44 50 33     811 if ( $self->autolint && $self->autotidy ) {
211 0         0 my $msg = 'autolint & autotidy';
212 0 0       0 $msg .= ": $desc" if defined $desc;
213             $TB->subtest(
214             $desc,
215             sub {
216 0     0   0 $self->_lint_content_ok();
217 0         0 $self->_tidy_content_ok();
218             }
219 0         0 );
220 0         0 ++$emitted_ok;
221             }
222             else {
223 44 50       140 if ( $self->autolint ) {
    50          
224 0         0 $ok = $self->_lint_content_ok( $desc );
225 0         0 ++$emitted_ok;
226             }
227             elsif ( $self->autotidy ) {
228 0         0 $ok = $self->_tidy_content_ok( $desc );
229 0         0 ++$emitted_ok;
230             }
231             }
232             }
233              
234 51 50       222 if ( !$emitted_ok ) {
235 51         548 $TB->ok( $ok, $desc );
236             }
237             }
238             else {
239 2         34 $TB->ok( $ok, $desc );
240 2         2375 $TB->diag( $self->status );
241 2 50       524 $TB->diag( $self->response->message ) if $self->response;
242             }
243              
244 53         30452 return $ok;
245             }
246              
247             =head2 $mech->head_ok($url, [ \%LWP_options ,] $desc)
248              
249             A wrapper around WWW::Mechanize's head(), with similar options, except
250             the second argument needs to be a hash reference, not a hash. Like
251             well-behaved C<*_ok()> functions, it returns true if the test passed,
252             or false if not.
253              
254             A default description of "HEAD $url" is used if none if provided.
255              
256             =cut
257              
258             sub head_ok {
259 10     10 1 11989 my $self = shift;
260              
261 10         52 my ($url,$desc,%opts) = $self->_unpack_args( 'HEAD', @_ );
262              
263 10         50 $self->head( $url, %opts );
264 10         217 my $ok = $self->success;
265              
266 10         109 $TB->ok( $ok, $desc );
267 10 100       4578 if ( !$ok ) {
268 1         37 $TB->diag( $self->status );
269 1 50       241 $TB->diag( $self->response->message ) if $self->response;
270             }
271              
272 10         294 return $ok;
273             }
274              
275              
276             =head2 $mech->post_ok( $url, [ \%LWP_options ,] $desc )
277              
278             A wrapper around WWW::Mechanize's post(), with similar options, except
279             the second argument needs to be a hash reference, not a hash. Like
280             well-behaved C<*_ok()> functions, it returns true if the test passed,
281             or false if not.
282              
283             B Due to compatibility reasons it is not possible to pass
284             additional LWP_options beyond form data via this method (such as
285             Content or Content-Type). It is recommend that you use WWW::Mechanize's
286             post() directly for instances where more granular control of the post
287             is needed.
288              
289             A default description of "POST to $url" is used if none if provided.
290              
291             =cut
292              
293             sub post_ok {
294 0     0 1 0 my $self = shift;
295              
296 0         0 my ($url,$desc,%opts) = $self->_unpack_args( 'POST', @_ );
297              
298 0         0 $self->post( $url, \%opts );
299 0         0 my $ok = $self->success;
300 0         0 $ok = $self->_post_load_validation( $ok, $desc );
301              
302 0         0 return $ok;
303             }
304              
305             =head2 $mech->put_ok( $url, [ \%LWP_options ,] $desc )
306              
307             A wrapper around WWW::Mechanize's put(), with similar options, except
308             the second argument needs to be a hash reference, not a hash. Like
309             well-behaved C<*_ok()> functions, it returns true if the test passed,
310             or false if not.
311              
312             A default description of "PUT to $url" is used if none if provided.
313              
314             =cut
315              
316             sub put_ok {
317 3     3 1 6323 my $self = shift;
318              
319 3         20 my ($url,$desc,%opts) = $self->_unpack_args( 'PUT', @_ );
320 3 100       15 $opts{content} = '' if !exists $opts{content};
321 3         44 $self->put( $url, %opts );
322              
323 3         51 my $ok = $self->success;
324 3         123 $TB->ok( $ok, $desc );
325 3 50       1149 if ( !$ok ) {
326 0         0 $TB->diag( $self->status );
327 0 0       0 $TB->diag( $self->response->message ) if $self->response;
328             }
329              
330 3         10 return $ok;
331             }
332              
333             =head2 $mech->delete_ok( $url, [ \%LWP_options ,] $desc )
334              
335             A wrapper around WWW::Mechanize's delete(), with similar options, except
336             the second argument needs to be a hash reference, not a hash. Like
337             well-behaved C<*_ok()> functions, it returns true if the test passed,
338             or false if not.
339              
340             A default description of "DELETE to $url" is used if none if provided.
341              
342             =cut
343              
344             sub delete_ok {
345 4     4 1 7309 my $self = shift;
346              
347 4         24 my ($url,$desc,%opts) = $self->_unpack_args( 'DELETE', @_ );
348              
349 4 100       44 if ($self->can('delete')) {
350 3         25 $self->delete( $url, %opts );
351             }
352             else {
353             # When version of LWP::UserAgent is older than 6.04.
354 1         6 $self->_delete( $url, %opts );
355             }
356 4         54 my $ok = $self->success;
357              
358 4         69 $ok = $self->_post_load_validation( $ok, $desc );
359              
360 4         15 return $ok;
361             }
362              
363             sub _delete {
364 1     1   8 require URI;
365 1         4 require HTTP::Request::Common;
366 1         3 my $self = shift;
367 1         2 my $uri = shift;
368              
369 1 50       4 $uri = $uri->url if ref($uri) eq 'WWW::Mechanize::Link';
370 1 50       13 $uri = $self->base
371             ? URI->new_abs( $uri, $self->base )
372             : URI->new($uri);
373              
374 1         117 my @parameters = ( $uri->as_string, @_ );
375 1         7 my @suff = $self->_process_colonic_headers( \@parameters, 1 );
376 1         10 return $self->request( HTTP::Request::Common::DELETE(@parameters), @suff );
377             }
378              
379             =head2 $mech->submit_form_ok( \%parms [, $desc] )
380              
381             Makes a C call and executes tests on the results.
382             The form must be found, and then submitted successfully. Otherwise,
383             this test fails.
384              
385             I<%parms> is a hashref containing the parms to pass to C.
386             Note that the parms to C are a hash whereas the parms to
387             this function are a hashref. You have to call this function like:
388              
389             $mech->submit_form_ok( {
390             form_number => 3,
391             fields => {
392             answer => 42
393             },
394             }, 'now we just need the question'
395             );
396              
397             As with other test functions, C<$desc> is optional. If it is supplied
398             then it will display when running the test harness in verbose mode.
399              
400             Returns true value if the specified link was found and followed
401             successfully. The L object returned by submit_form()
402             is not available.
403              
404             =cut
405              
406             sub submit_form_ok {
407 1     1 1 17 my $self = shift;
408 1   50     5 my $parms = shift || {};
409 1         2 my $desc = shift;
410              
411 1 50       5 if ( ref $parms ne 'HASH' ) {
412 0         0 Carp::croak 'FATAL: parameters must be given as a hashref';
413             }
414              
415             # return from submit_form() is an HTTP::Response or undef
416 1         2 my $response = $self->submit_form( %{$parms} );
  1         31  
417              
418 1   33     70 my $ok = $response && $response->is_success;
419 1         12 $ok = $self->_post_load_validation( $ok, $desc );
420              
421 1         56 return $ok;
422             }
423              
424              
425             =head2 $mech->follow_link_ok( \%parms [, $desc] )
426              
427             Makes a C call and executes tests on the results.
428             The link must be found, and then followed successfully. Otherwise,
429             this test fails.
430              
431             I<%parms> is a hashref containing the parms to pass to C.
432             Note that the parms to C are a hash whereas the parms to
433             this function are a hashref. You have to call this function like:
434              
435             $mech->follow_link_ok( {n=>3}, 'looking for 3rd link' );
436              
437             As with other test functions, C<$desc> is optional. If it is supplied
438             then it will display when running the test harness in verbose mode.
439              
440             Returns a true value if the specified link was found and followed
441             successfully. The L object returned by follow_link()
442             is not available.
443              
444             =cut
445              
446             sub follow_link_ok {
447 2     2 1 3382 my $self = shift;
448 2   50     10 my $parms = shift || {};
449 2         11 my $desc = shift;
450              
451 2 50       9 if (!defined($desc)) {
452 0         0 my $parms_str = join(', ', map { join('=', $_, $parms->{$_}) } keys(%{$parms}));
  0         0  
  0         0  
453 0 0       0 $desc = qq{Followed link with "$parms_str"} if !defined($desc);
454             }
455              
456 2 50       7 if ( ref $parms ne 'HASH' ) {
457 0         0 Carp::croak 'FATAL: parameters must be given as a hashref';
458             }
459              
460             # return from follow_link() is an HTTP::Response or undef
461 2         4 my $response = $self->follow_link( %{$parms} );
  2         31  
462              
463 2   66     147 my $ok = $response && $response->is_success;
464 2         34 $ok = $self->_post_load_validation( $ok, $desc );
465              
466 2         7 return $ok;
467             }
468              
469              
470             =head2 $mech->click_ok( $button[, $desc] )
471              
472             =head2 $mech->click_ok( \@button-and-coordinates [, $desc ] )
473              
474             Clicks the button named by C<$button>. An optional C<$desc> can be
475             given for the test.
476              
477             $mech->click_ok( 'continue', 'Clicking the "Continue" button' );
478              
479             Alternatively the first argument can be an arrayref with three elements:
480             The name of the button and the X and Y coordinates of the button.
481              
482             $mech->click_ok( [ 'continue', 12, 47 ], 'Clicking the "Continue" button' );
483              
484             =cut
485              
486             sub click_ok {
487 2     2 1 40 my $self = shift;
488 2         4 my $button = shift;
489 2         2 my $desc = shift;
490              
491 2         3 my $response;
492 2 100       8 if ( ref($button) eq 'ARRAY' ) {
493 1         7 $response = $self->click( $button->[0], $button->[1], $button->[2] );
494             }
495             else {
496 1         46 $response = $self->click( $button );
497             }
498              
499 2 50       27 if ( !$response ) {
500 0         0 return $TB->ok( 0, $desc );
501             }
502              
503 2         10 my $ok = $response->is_success;
504              
505 2         27 $ok = $self->_post_load_validation( $ok, $desc );
506              
507 2         122 return $ok;
508             }
509              
510              
511             sub _unpack_args {
512 61     61   221 my $self = shift;
513 61         293 my $method = shift;
514 61         189 my $url = shift;
515              
516 61         203 my $desc;
517             my %opts;
518              
519 61 100       416 if ( @_ ) {
520 21         57 my $flex = shift; # The flexible argument
521              
522 21 100       108 if ( !defined( $flex ) ) {
    100          
    100          
523 2         6 $desc = shift;
524             }
525             elsif ( ref $flex eq 'HASH' ) {
526 5         9 %opts = %{$flex};
  5         19  
527 5         9 $desc = shift;
528             }
529             elsif ( ref $flex eq 'ARRAY' ) {
530 4         7 %opts = @{$flex};
  4         12  
531 4         7 $desc = shift;
532             }
533             else {
534 10         31 $desc = $flex;
535             }
536             } # parms left
537              
538 61 100       336 if ( not defined $desc ) {
539 45 50       277 $url = $url->url if ref($url) eq 'WWW::Mechanize::Link';
540 45         247 $desc = "$method $url";
541             }
542              
543 61         362 return ($url, $desc, %opts);
544             }
545              
546              
547             =head1 METHODS: HEADER CHECKING
548              
549             =head2 $mech->header_exists_ok( $header [, $desc ] )
550              
551             Assures that a given response header exists. The actual value of the
552             response header is not checked, only that the header exists.
553              
554             =cut
555              
556             sub header_exists_ok {
557 3     3 1 7655 my $self = shift;
558 3         15 my $header = shift;
559 3   66     28 my $desc = shift || qq{Response has $header header};
560              
561 3         15 return $TB->ok( defined($self->response->header($header)), $desc );
562             }
563              
564              
565             =head2 $mech->lacks_header_ok( $header [, $desc ] )
566              
567             Assures that a given response header does NOT exist.
568              
569             =cut
570              
571             sub lacks_header_ok {
572 3     3 1 10299 my $self = shift;
573 3         24 my $header = shift;
574 3   66     17 my $desc = shift || qq{Response lacks $header header};
575              
576 3         11 return $TB->ok( !defined($self->response->header($header)), $desc );
577             }
578              
579              
580             =head2 $mech->header_is( $header, $value [, $desc ] )
581              
582             Assures that a given response header exists and has the given value.
583              
584             =cut
585              
586             sub header_is {
587 4     4 1 12931 my $self = shift;
588 4         40 my $header = shift;
589 4         11 my $value = shift;
590 4   66     17 my $desc = shift || qq{Response has $header header with value "$value"};
591              
592             # Force scalar context.
593 4         13 my $actual_value = $self->response->header($header);
594              
595 4         188 my $ok;
596 4 100       10 if ( defined( $actual_value ) ) {
597 3         8 $ok = $TB->is_eq( $actual_value, $value, $desc );
598             }
599             else {
600 1         4 $ok = $TB->ok( 0, $desc );
601 1         872 $TB->diag( "Header $header does not exist" );
602             }
603              
604 4         2566 return $ok;
605             }
606              
607              
608             =head2 $mech->header_like( $header, $value [, $desc ] )
609              
610             Assures that a given response header exists and has the given value.
611              
612             =cut
613              
614             sub header_like {
615 2     2 1 5571 my $self = shift;
616 2         11 my $header = shift;
617 2         6 my $regex = shift;
618 2   33     19 my $desc = shift || qq{Response has $header header that matches regex $regex};
619              
620             # Force scalar context.
621 2         11 my $actual_value = $self->response->header($header);
622 2         97 return $TB->like( $self->response->header($header), $regex, $desc );
623             }
624              
625              
626             =head1 METHODS: CONTENT CHECKING
627              
628             =head2 $mech->html_lint_ok( [$desc] )
629              
630             Checks the validity of the HTML on the current page using the HTML::Lint
631             module. If the page is not HTML, then it fails. The URI is automatically
632             appended to the I<$desc>.
633              
634             Note that HTML::Lint must be installed for this to work. Otherwise,
635             it will blow up.
636              
637             =cut
638              
639             sub html_lint_ok {
640 0     0 1 0 my $self = shift;
641 0         0 my $desc = shift;
642              
643 0         0 my $uri = $self->uri;
644 0 0       0 $desc = $desc ? "$desc ($uri)" : $uri;
645              
646 0         0 my $ok;
647              
648 0 0       0 if ( $self->is_html ) {
649 0         0 $ok = $self->_lint_content_ok( $desc );
650             }
651             else {
652 0         0 $ok = $TB->ok( 0, $desc );
653 0         0 $TB->diag( q{This page doesn't appear to be HTML, or didn't get the proper text/html content type returned.} );
654             }
655              
656 0         0 return $ok;
657             }
658              
659              
660             sub _lint_content_ok {
661 0     0   0 local $Test::Builder::Level = $Test::Builder::Level + 1;
662              
663 0         0 my $self = shift;
664 0         0 my $desc = shift;
665              
666 0         0 my $module = "HTML::Lint 2.20";
667 0 0       0 if ( not ( eval "use $module; 1;" ) ) {
668 0         0 die "Test::WWW::Mechanize can't do linting without $module: $@";
669             }
670              
671 0         0 my $lint = $self->{autolint};
672 0 0 0     0 if ( ref $lint && $lint->isa('HTML::Lint') ) {
673 0         0 $lint->newfile;
674 0         0 $lint->clear_errors;
675             }
676             else {
677 0         0 $lint = HTML::Lint->new();
678             }
679              
680 0         0 $lint->parse( $self->content );
681 0         0 $lint->eof();
682              
683 0         0 my @errors = $lint->errors;
684 0         0 my $nerrors = @errors;
685 0         0 my $ok;
686 0 0       0 if ( $nerrors ) {
687 0         0 $ok = $TB->ok( 0, $desc );
688 0         0 $TB->diag( 'HTML::Lint errors for ' . $self->uri );
689 0         0 $TB->diag( $_->as_string ) for @errors;
690 0 0       0 my $s = $nerrors == 1 ? '' : 's';
691 0         0 $TB->diag( "$nerrors error$s on the page" );
692             }
693             else {
694 0         0 $ok = $TB->ok( 1, $desc );
695             }
696              
697 0         0 return $ok;
698             }
699              
700              
701             =head2 $mech->html_tidy_ok( [$desc] )
702              
703             Checks the validity of the HTML on the current page using the HTML::Tidy
704             module. If the page is not HTML, then it fails. The URI is automatically
705             appended to the I<$desc>.
706              
707             Note that HTML::tidy must be installed for this to work. Otherwise,
708             it will blow up.
709              
710             =cut
711              
712             sub html_tidy_ok {
713 0     0 1 0 my $self = shift;
714 0         0 my $desc = shift;
715              
716 0         0 my $uri = $self->uri;
717 0 0       0 $desc = $desc ? "$desc ($uri)" : $uri;
718              
719 0         0 my $ok;
720              
721 0 0       0 if ( $self->is_html ) {
722 0         0 $ok = $self->_tidy_content_ok( $desc );
723             }
724             else {
725 0         0 $ok = $TB->ok( 0, $desc );
726 0         0 $TB->diag( q{This page doesn't appear to be HTML, or didn't get the proper text/html content type returned.} );
727             }
728              
729 0         0 return $ok;
730             }
731              
732              
733             sub _tidy_content_ok {
734 0     0   0 local $Test::Builder::Level = $Test::Builder::Level + 1;
735              
736 0         0 my $self = shift;
737 0         0 my $desc = shift;
738              
739 0         0 my $module = 'HTML::Tidy5 1.00';
740              
741 0 0       0 if ( not ( eval "use $module; 1;" ) ) {
742 0         0 die "Test::WWW::Mechanize can't do tidying without $module: $@";
743             }
744              
745 0         0 my $tidy = $self->{autotidy};
746 0 0 0     0 if ( ref $tidy && $tidy->isa('HTML::Tidy5') ) {
747 0         0 $tidy->clear_messages();
748             }
749             else {
750 0         0 $tidy = HTML::Tidy5->new();
751             }
752              
753 0         0 $tidy->parse( '', $self->content_for_tidy );
754              
755 0         0 my @messages = $tidy->messages;
756 0         0 my $nmessages = @messages;
757 0         0 my $ok;
758 0 0       0 if ( $nmessages ) {
759 0         0 $ok = $TB->ok( 0, $desc );
760 0         0 $TB->diag( 'HTML::Tidy5 messages for ' . $self->uri );
761 0         0 $TB->diag( $_->as_string ) for @messages;
762 0 0       0 my $s = $nmessages == 1 ? '' : 's';
763 0         0 $TB->diag( "$nmessages message$s on the page" );
764             }
765             else {
766 0         0 $ok = $TB->ok( 1, $desc );
767             }
768              
769 0         0 return $ok;
770             }
771              
772              
773             =head2 $mech->content_for_tidy()
774              
775             This method is called by C to get the content that should
776             be validated by HTML::Tidy5. By default, this is just C,
777             but subclasses can override it to modify the content before validation.
778              
779             This method should not change any state in the Mech object. Specifically,
780             it should not actually modify any of the actual content.
781              
782             =cut
783              
784             sub content_for_tidy {
785 0     0 1 0 my $self = shift;
786              
787 0         0 return $self->content;
788             }
789              
790              
791             =head2 $mech->title_is( $str [, $desc ] )
792              
793             Tells if the title of the page is the given string.
794              
795             $mech->title_is( 'Invoice Summary' );
796              
797             =cut
798              
799             sub title_is {
800 2     2 1 2342 my $self = shift;
801 2         7 my $str = shift;
802 2         8 my $desc = shift;
803 2 100       7 $desc = qq{Title is "$str"} if !defined($desc);
804              
805 2         5 local $Test::Builder::Level = $Test::Builder::Level + 1;
806 2         8 return is_string( $self->title, $str, $desc );
807             }
808              
809             =head2 $mech->title_like( $regex [, $desc ] )
810              
811             Tells if the title of the page matches the given regex.
812              
813             $mech->title_like( qr/Invoices for (.+)/ );
814              
815             =cut
816              
817             sub title_like {
818 1     1 1 2387 my $self = shift;
819 1         3 my $regex = shift;
820 1         5 my $desc = shift;
821 1 50       7 $desc = qq{Title is like "$regex"} if !defined($desc);
822              
823 1         5 local $Test::Builder::Level = $Test::Builder::Level + 1;
824 1         4 return like_string( $self->title, $regex, $desc );
825             }
826              
827             =head2 $mech->title_unlike( $regex [, $desc ] )
828              
829             Tells if the title of the page matches the given regex.
830              
831             $mech->title_unlike( qr/Invoices for (.+)/ );
832              
833             =cut
834              
835             sub title_unlike {
836 0     0 1 0 my $self = shift;
837 0         0 my $regex = shift;
838 0         0 my $desc = shift;
839 0 0       0 $desc = qq{Title is unlike "$regex"} if !defined($desc);
840              
841 0         0 local $Test::Builder::Level = $Test::Builder::Level + 1;
842 0         0 return unlike_string( $self->title, $regex, $desc );
843             }
844              
845             =head2 $mech->base_is( $str [, $desc ] )
846              
847             Tells if the base of the page is the given string.
848              
849             $mech->base_is( 'http://example.com/' );
850              
851             =cut
852              
853             sub base_is {
854 0     0 1 0 my $self = shift;
855 0         0 my $str = shift;
856 0         0 my $desc = shift;
857 0 0       0 $desc = qq{Base is "$str"} if !defined($desc);
858              
859 0         0 local $Test::Builder::Level = $Test::Builder::Level + 1;
860 0         0 return is_string( $self->base, $str, $desc );
861             }
862              
863             =head2 $mech->base_like( $regex [, $desc ] )
864              
865             Tells if the base of the page matches the given regex.
866              
867             $mech->base_like( qr{http://example.com/index.php?PHPSESSID=(.+)});
868              
869             =cut
870              
871             sub base_like {
872 0     0 1 0 my $self = shift;
873 0         0 my $regex = shift;
874 0         0 my $desc = shift;
875 0 0       0 $desc = qq{Base is like "$regex"} if !defined($desc);
876              
877 0         0 local $Test::Builder::Level = $Test::Builder::Level + 1;
878 0         0 return like_string( $self->base, $regex, $desc );
879             }
880              
881             =head2 $mech->base_unlike( $regex [, $desc ] )
882              
883             Tells if the base of the page matches the given regex.
884              
885             $mech->base_unlike( qr{http://example.com/index.php?PHPSESSID=(.+)});
886              
887             =cut
888              
889             sub base_unlike {
890 0     0 1 0 my $self = shift;
891 0         0 my $regex = shift;
892 0         0 my $desc = shift;
893 0 0       0 $desc = qq{Base is unlike "$regex"} if !defined($desc);
894              
895 0         0 local $Test::Builder::Level = $Test::Builder::Level + 1;
896 0         0 return unlike_string( $self->base, $regex, $desc );
897             }
898              
899             =head2 $mech->content_is( $str [, $desc ] )
900              
901             Tells if the content of the page matches the given string
902              
903             =cut
904              
905             sub content_is {
906 0     0 1 0 my $self = shift;
907 0         0 my $str = shift;
908 0         0 my $desc = shift;
909              
910 0         0 local $Test::Builder::Level = $Test::Builder::Level + 1;
911 0 0       0 $desc = qq{Content is "$str"} if !defined($desc);
912              
913 0         0 return is_string( $self->content, $str, $desc );
914             }
915              
916             =head2 $mech->content_contains( $str [, $desc ] )
917              
918             Tells if the content of the page contains I<$str>.
919              
920             =cut
921              
922             sub content_contains {
923 7     7 1 19570 my $self = shift;
924 7         16 my $str = shift;
925 7         20 my $desc = shift;
926              
927 7         17 local $Test::Builder::Level = $Test::Builder::Level + 1;
928              
929 7 100       26 if ( ref($str) ) {
930 4         16 return $TB->ok( 0, 'Test::WWW::Mechanize->content_contains called incorrectly. It requires a scalar, not a reference.' );
931             }
932 3 100       9 $desc = qq{Content contains "$str"} if !defined($desc);
933              
934 3         23 return contains_string( $self->content, $str, $desc );
935             }
936              
937             =head2 $mech->content_lacks( $str [, $desc ] )
938              
939             Tells if the content of the page lacks I<$str>.
940              
941             =cut
942              
943             sub content_lacks {
944 7     7 1 18990 my $self = shift;
945 7         17 my $str = shift;
946 7         12 my $desc = shift;
947              
948 7         20 local $Test::Builder::Level = $Test::Builder::Level + 1;
949 7 100       27 if ( ref($str) ) {
950 4         25 return $TB->ok( 0, 'Test::WWW::Mechanize->content_lacks called incorrectly. It requires a scalar, not a reference.' );
951             }
952 3 100       8 $desc = qq{Content lacks "$str"} if !defined($desc);
953              
954 3         15 return lacks_string( $self->content, $str, $desc );
955             }
956              
957             =head2 $mech->content_like( $regex [, $desc ] )
958              
959             Tells if the content of the page matches I<$regex>.
960              
961             =cut
962              
963             sub content_like {
964 0     0 1 0 my $self = shift;
965 0         0 my $regex = shift;
966 0         0 my $desc = shift;
967 0 0       0 $desc = qq{Content is like "$regex"} if !defined($desc);
968              
969 0         0 local $Test::Builder::Level = $Test::Builder::Level + 1;
970 0         0 return like_string( $self->content, $regex, $desc );
971             }
972              
973             =head2 $mech->content_unlike( $regex [, $desc ] )
974              
975             Tells if the content of the page does NOT match I<$regex>.
976              
977             =cut
978              
979             sub content_unlike {
980 0     0 1 0 my $self = shift;
981 0         0 my $regex = shift;
982 0   0     0 my $desc = shift || qq{Content is unlike "$regex"};
983              
984 0         0 local $Test::Builder::Level = $Test::Builder::Level + 1;
985 0         0 return unlike_string( $self->content, $regex, $desc );
986             }
987              
988             =head2 $mech->text_contains( $str [, $desc ] )
989              
990             Tells if the text form of the page's content contains I<$str>.
991              
992             When your page contains HTML which is difficult, unimportant, or
993             unlikely to match over time as designers alter markup, use
994             C instead of C.
995              
996             # Hi, User!
997             $mech->content_contains('Hi, User'); # Fails.
998             $mech->text_contains('Hi, User'); # Passes.
999              
1000             Text is determined by calling C<< $mech->text() >>.
1001             See L.
1002              
1003             =cut
1004              
1005             sub text_contains {
1006 7     7 1 41968 my $self = shift;
1007 7         12 my $str = shift;
1008 7   66     28 my $desc = shift || qq{Text contains "$str"};
1009              
1010 7         18 local $Test::Builder::Level = $Test::Builder::Level + 1;
1011 7 100       17 if ( ref($str) ) {
1012 4         24 return $TB->ok( 0, 'Test::WWW::Mechanize->text_contains called incorrectly. It requires a scalar, not a reference.' );
1013             }
1014              
1015 3         19 return contains_string( $self->text, $str, $desc );
1016             }
1017              
1018             =head2 $mech->text_lacks( $str [, $desc ] )
1019              
1020             Tells if the text of the page lacks I<$str>.
1021              
1022             =cut
1023              
1024             sub text_lacks {
1025 4     4 1 12264 my $self = shift;
1026 4         7 my $str = shift;
1027 4         7 my $desc = shift;
1028              
1029 4         8 local $Test::Builder::Level = $Test::Builder::Level + 1;
1030 4 50       12 if ( ref($str) ) {
1031 4         15 return $TB->ok( 0, 'Test::WWW::Mechanize->text_lacks called incorrectly. It requires a scalar, not a reference.' );
1032             }
1033 0 0       0 $desc = qq{Text lacks "$str"} if !defined($desc);
1034              
1035 0         0 return lacks_string( $self->text, $str, $desc );
1036             }
1037              
1038             =head2 $mech->text_like( $regex [, $desc ] )
1039              
1040             Tells if the text form of the page's content matches I<$regex>.
1041              
1042             =cut
1043              
1044             sub text_like {
1045 0     0 1 0 my $self = shift;
1046 0         0 my $regex = shift;
1047 0   0     0 my $desc = shift || qq{Text is like "$regex"};
1048              
1049 0         0 local $Test::Builder::Level = $Test::Builder::Level + 1;
1050 0         0 return like_string( $self->text, $regex, $desc );
1051             }
1052              
1053             =head2 $mech->text_unlike( $regex [, $desc ] )
1054              
1055             Tells if the text format of the page's content does NOT match I<$regex>.
1056              
1057             =cut
1058              
1059             sub text_unlike {
1060 0     0 1 0 my $self = shift;
1061 0         0 my $regex = shift;
1062 0   0     0 my $desc = shift || qq{Text is unlike "$regex"};
1063              
1064 0         0 local $Test::Builder::Level = $Test::Builder::Level + 1;
1065 0         0 return unlike_string( $self->text, $regex, $desc );
1066             }
1067              
1068             =head2 $mech->has_tag( $tag, $text [, $desc ] )
1069              
1070             Tells if the page has a C<$tag> tag with the given content in its text.
1071              
1072             =cut
1073              
1074             sub has_tag {
1075 10     10 1 28610 my $self = shift;
1076 10         66 my $tag = shift;
1077 10         56 my $text = shift;
1078 10   66     116 my $desc = shift || qq{Page has $tag tag with "$text"};
1079              
1080 10     18   130 my $found = $self->_tag_walk( $tag, sub { $text eq $_[0] } );
  18         205  
1081              
1082 10         110 return $TB->ok( $found, $desc );
1083             }
1084              
1085              
1086             =head2 $mech->has_tag_like( $tag, $regex [, $desc ] )
1087              
1088             Tells if the page has a C<$tag> tag with the given content in its text.
1089              
1090             =cut
1091              
1092             sub has_tag_like {
1093 2     2 1 6989 my $self = shift;
1094 2         7 my $tag = shift;
1095 2         4 my $regex = shift;
1096 2         10 my $desc = shift;
1097 2 50       13 $desc = qq{Page has $tag tag like "$regex"} if !defined($desc);
1098              
1099 2     6   33 my $found = $self->_tag_walk( $tag, sub { $_[0] =~ $regex } );
  6         59  
1100              
1101 2         22 return $TB->ok( $found, $desc );
1102             }
1103              
1104              
1105             sub _tag_walk {
1106 12     12   34 my $self = shift;
1107 12         33 my $tag = shift;
1108 12         19 my $match = shift;
1109              
1110 12         119 my $p = HTML::TokeParser->new( \($self->content) );
1111              
1112 12         3024 while ( my $token = $p->get_tag( $tag ) ) {
1113 24         6933 my $tagtext = $p->get_trimmed_text();
1114 24 100       2001 return 1 if $match->( $tagtext );
1115             }
1116 2         326 return;
1117             }
1118              
1119             =head2 $mech->page_links_ok( [ $desc ] )
1120              
1121             Follow all links on the current page and test for HTTP status 200
1122              
1123             $mech->page_links_ok('Check all links');
1124              
1125             =cut
1126              
1127             sub page_links_ok {
1128 3     3 1 5536 my $self = shift;
1129 3         6 my $desc = shift;
1130              
1131 3 100       15 $desc = 'All links ok' unless defined $desc;
1132              
1133 3         20 my @links = $self->followable_links();
1134 3         10788 my @urls = _format_links(\@links);
1135              
1136 3         10 my @failures = $self->_check_links_status( \@urls );
1137 3         14 my $ok = (@failures==0);
1138              
1139 3         13 $TB->ok( $ok, $desc );
1140 3         1684 $TB->diag( $_ ) for @failures;
1141              
1142 3         655 return $ok;
1143             }
1144              
1145             =head2 $mech->page_links_content_like( $regex [, $desc ] )
1146              
1147             Follow all links on the current page and test their contents for I<$regex>.
1148              
1149             $mech->page_links_content_like( qr/foo/,
1150             'Check all links contain "foo"' );
1151              
1152             =cut
1153              
1154             sub page_links_content_like {
1155 4     4 1 9179 my $self = shift;
1156 4         10 my $regex = shift;
1157 4         14 my $desc = shift;
1158              
1159 4 100       17 $desc = qq{All links are like "$regex"} unless defined $desc;
1160              
1161 4         17 my $usable_regex=$TB->maybe_regex( $regex );
1162              
1163 4 100       79 if ( !defined( $usable_regex ) ) {
1164 1         3 my $ok = $TB->ok( 0, 'page_links_content_like' );
1165 1         1165 $TB->diag(qq{ "$regex" doesn't look much like a regex to me.});
1166 1         203 return $ok;
1167             }
1168              
1169 3         19 my @links = $self->followable_links();
1170 3         9394 my @urls = _format_links(\@links);
1171              
1172 3         12 my @failures = $self->_check_links_content( \@urls, $regex );
1173 3         12 my $ok = (@failures==0);
1174              
1175 3         33 $TB->ok( $ok, $desc );
1176 3         1822 $TB->diag( $_ ) for @failures;
1177              
1178 3         626 return $ok;
1179             }
1180              
1181             =head2 $mech->page_links_content_unlike( $regex [, $desc ] )
1182              
1183             Follow all links on the current page and test their contents do not
1184             contain the specified regex.
1185              
1186             $mech->page_links_content_unlike(qr/Restricted/,
1187             'Check all links do not contain Restricted');
1188              
1189             =cut
1190              
1191             sub page_links_content_unlike {
1192 3     3 1 7594 my $self = shift;
1193 3         7 my $regex = shift;
1194 3         9 my $desc = shift;
1195 3 50       11 $desc = qq{All links are unlike "$regex"} unless defined($desc);
1196              
1197 3         13 my $usable_regex=$TB->maybe_regex( $regex );
1198              
1199 3 100       64 if ( !defined( $usable_regex ) ) {
1200 1         4 my $ok = $TB->ok( 0, 'page_links_content_unlike' );
1201 1         834 $TB->diag(qq{ "$regex" doesn't look much like a regex to me.});
1202 1         200 return $ok;
1203             }
1204              
1205 2         13 my @links = $self->followable_links();
1206 2         2862 my @urls = _format_links(\@links);
1207              
1208 2         11 my @failures = $self->_check_links_content( \@urls, $regex, 'unlike' );
1209 2         8 my $ok = (@failures==0);
1210              
1211 2         42 $TB->ok( $ok, $desc );
1212 2         1548 $TB->diag( $_ ) for @failures;
1213              
1214 2         623 return $ok;
1215             }
1216              
1217             =head2 $mech->links_ok( $links [, $desc ] )
1218              
1219             Follow specified links on the current page and test for HTTP status
1220             200. The links may be specified as a reference to an array containing
1221             L objects, an array of URLs, or a scalar URL
1222             name.
1223              
1224             my @links = $mech->find_all_links( url_regex => qr/cnn\.com$/ );
1225             $mech->links_ok( \@links, 'Check all links for cnn.com' );
1226              
1227             my @links = qw( index.html search.html about.html );
1228             $mech->links_ok( \@links, 'Check main links' );
1229              
1230             $mech->links_ok( 'index.html', 'Check link to index' );
1231              
1232             =cut
1233              
1234             sub links_ok {
1235 6     6 1 13450 my $self = shift;
1236 6         24 my $links = shift;
1237 6         26 my $desc = shift;
1238              
1239 6         38 my @urls = _format_links( $links );
1240 6 50       25 $desc = _default_links_desc(\@urls, 'are ok') unless defined $desc;
1241 6         27 my @failures = $self->_check_links_status( \@urls );
1242 6         16 my $ok = (@failures == 0);
1243              
1244 6         35 $TB->ok( $ok, $desc );
1245 6         3914 $TB->diag( $_ ) for @failures;
1246              
1247 6         1115 return $ok;
1248             }
1249              
1250             =head2 $mech->link_status_is( $links, $status [, $desc ] )
1251              
1252             Follow specified links on the current page and test for HTTP status
1253             passed. The links may be specified as a reference to an array
1254             containing L objects, an array of URLs, or a
1255             scalar URL name.
1256              
1257             my @links = $mech->followable_links();
1258             $mech->link_status_is( \@links, 403,
1259             'Check all links are restricted' );
1260              
1261             =cut
1262              
1263             sub link_status_is {
1264 3     3 1 9443 my $self = shift;
1265 3         6 my $links = shift;
1266 3         6 my $status = shift;
1267 3         11 my $desc = shift;
1268              
1269 3         22 my @urls = _format_links( $links );
1270 3 100       15 $desc = _default_links_desc(\@urls, "have status $status") if !defined($desc);
1271 3         19 my @failures = $self->_check_links_status( \@urls, $status );
1272 3         10 my $ok = (@failures == 0);
1273              
1274 3         18 $TB->ok( $ok, $desc );
1275 3         1919 $TB->diag( $_ ) for @failures;
1276              
1277 3         231 return $ok;
1278             }
1279              
1280             =head2 $mech->link_status_isnt( $links, $status [, $desc ] )
1281              
1282             Follow specified links on the current page and test for HTTP status
1283             passed. The links may be specified as a reference to an array
1284             containing L objects, an array of URLs, or a
1285             scalar URL name.
1286              
1287             my @links = $mech->followable_links();
1288             $mech->link_status_isnt( \@links, 404,
1289             'Check all links are not 404' );
1290              
1291             =cut
1292              
1293             sub link_status_isnt {
1294 2     2 1 3275 my $self = shift;
1295 2         5 my $links = shift;
1296 2         4 my $status = shift;
1297 2         8 my $desc = shift;
1298              
1299 2         8 my @urls = _format_links( $links );
1300 2 50       7 $desc = _default_links_desc(\@urls, "do not have status $status") if !defined($desc);
1301 2         7 my @failures = $self->_check_links_status( \@urls, $status, 'isnt' );
1302 2         7 my $ok = (@failures == 0);
1303              
1304 2         12 $TB->ok( $ok, $desc );
1305 2         1334 $TB->diag( $_ ) for @failures;
1306              
1307 2         229 return $ok;
1308             }
1309              
1310              
1311             =head2 $mech->link_content_like( $links, $regex [, $desc ] )
1312              
1313             Follow specified links on the current page and test the resulting
1314             content of each against I<$regex>. The links may be specified as a
1315             reference to an array containing L objects, an
1316             array of URLs, or a scalar URL name.
1317              
1318             my @links = $mech->followable_links();
1319             $mech->link_content_like( \@links, qr/Restricted/,
1320             'Check all links are restricted' );
1321              
1322             =cut
1323              
1324             sub link_content_like {
1325 4     4 1 12028 my $self = shift;
1326 4         7 my $links = shift;
1327 4         8 my $regex = shift;
1328 4         18 my $desc = shift;
1329              
1330 4         16 my $usable_regex=$TB->maybe_regex( $regex );
1331              
1332 4 100       78 if ( !defined( $usable_regex ) ) {
1333 1         5 my $ok = $TB->ok( 0, 'link_content_like' );
1334 1         1167 $TB->diag(qq{ "$regex" doesn't look much like a regex to me.});
1335 1         264 return $ok;
1336             }
1337              
1338 3         15 my @urls = _format_links( $links );
1339 3 100       12 $desc = _default_links_desc( \@urls, qq{are like "$regex"} ) if !defined($desc);
1340 3         10 my @failures = $self->_check_links_content( \@urls, $regex );
1341 3         10 my $ok = (@failures == 0);
1342              
1343 3         14 $TB->ok( $ok, $desc );
1344 3         1543 $TB->diag( $_ ) for @failures;
1345              
1346 3         646 return $ok;
1347             }
1348              
1349             =head2 $mech->link_content_unlike( $links, $regex [, $desc ] )
1350              
1351             Follow specified links on the current page and test that the resulting
1352             content of each does not match I<$regex>. The links may be specified as a
1353             reference to an array containing L objects, an array
1354             of URLs, or a scalar URL name.
1355              
1356             my @links = $mech->followable_links();
1357             $mech->link_content_unlike( \@links, qr/Restricted/,
1358             'No restricted links' );
1359              
1360             =cut
1361              
1362             sub link_content_unlike {
1363 4     4 1 9441 my $self = shift;
1364 4         7 my $links = shift;
1365 4         12 my $regex = shift;
1366 4         11 my $desc = shift;
1367              
1368 4         14 my $usable_regex=$TB->maybe_regex( $regex );
1369              
1370 4 100       65 if ( !defined( $usable_regex ) ) {
1371 1         4 my $ok = $TB->ok( 0, 'link_content_unlike' );
1372 1         951 $TB->diag(qq{ "$regex" doesn't look much like a regex to me.});
1373 1         214 return $ok;
1374             }
1375              
1376 3         8 my @urls = _format_links( $links );
1377 3 100       15 $desc = _default_links_desc( \@urls, qq{are not like "$regex"} ) if !defined($desc);
1378 3         23 my @failures = $self->_check_links_content( \@urls, $regex, 'unlike' );
1379 3         11 my $ok = (@failures == 0);
1380              
1381 3         13 $TB->ok( $ok, $desc );
1382 3         1402 $TB->diag( $_ ) for @failures;
1383              
1384 3         628 return $ok;
1385             }
1386              
1387             # Create a default description for the link_* methods, including the link count.
1388             sub _default_links_desc {
1389 3     3   10 my ($urls, $desc_suffix) = @_;
1390 3         8 my $url_count = scalar(@{$urls});
  3         6  
1391 3 50       21 return sprintf( '%d link%s %s', $url_count, $url_count == 1 ? '' : 's', $desc_suffix );
1392             }
1393              
1394             # This actually performs the status check of each URL.
1395             sub _check_links_status {
1396 14     14   27 my $self = shift;
1397 14         21 my $urls = shift;
1398 14   100     70 my $status = shift || 200;
1399 14   100     73 my $test = shift || 'is';
1400              
1401             # Create a clone of the $mech used during the test as to not disrupt
1402             # the original.
1403 14         76 my $mech = $self->clone();
1404              
1405 14         6281 my @failures;
1406              
1407 14         25 for my $url ( @{$urls} ) {
  14         51  
1408 39 100       508 if ( $mech->follow_link( url => $url ) ) {
1409 37 100       1703 if ( $test eq 'is' ) {
1410 30 100       78 push( @failures, $url ) unless $mech->status() == $status;
1411             }
1412             else {
1413 7 100       18 push( @failures, $url ) if $mech->status() == $status;
1414             }
1415 37         297 $mech->back();
1416             }
1417             else {
1418 2         229 push( @failures, $url );
1419             }
1420             } # for
1421              
1422 14         466 return @failures;
1423             }
1424              
1425             # This actually performs the content check of each URL.
1426             sub _check_links_content {
1427 11     11   21 my $self = shift;
1428 11         17 my $urls = shift;
1429 11   33     34 my $regex = shift || qr//;
1430 11   100     50 my $test = shift || 'like';
1431              
1432             # Create a clone of the $mech used during the test as to not disrupt
1433             # the original.
1434 11         76 my $mech = $self->clone();
1435              
1436 11         4731 my @failures;
1437 11         17 for my $url ( @{$urls} ) {
  11         37  
1438 33 50       506 if ( $mech->follow_link( url => $url ) ) {
1439 33         478 my $content=$mech->content();
1440 33 100       986 if ( $test eq 'like' ) {
1441 18 100       120 push( @failures, $url ) unless $content =~ /$regex/;
1442             }
1443             else {
1444 15 100       123 push( @failures, $url ) if $content =~ /$regex/;
1445             }
1446 33         121 $mech->back();
1447             }
1448             else {
1449 0         0 push( @failures, $url );
1450             }
1451             } # for
1452              
1453 11         433 return @failures;
1454             }
1455              
1456             # Return a list of URLs to match for Mech to follow.
1457             sub _format_links {
1458 25     25   74 my $links = shift;
1459              
1460 25         62 my @urls;
1461 25 100       86 if (ref($links) eq 'ARRAY') {
1462 22         48 my $link = $links->[0];
1463 22 50       57 if ( defined($link) ) {
1464 22 100       64 if ( ref($link) eq 'WWW::Mechanize::Link' ) {
1465 21         38 @urls = map { $_->url() } @{$links};
  67         247  
  21         45  
1466             }
1467             else {
1468 1         2 @urls = @{$links};
  1         3  
1469             }
1470             }
1471             }
1472             else {
1473 3         16 push(@urls,$links);
1474             }
1475 25         201 return @urls;
1476             }
1477              
1478             =head1 METHODS: SCRAPING
1479              
1480             =head2 $mech->scrape_text_by_attr( $attr, $attr_value [, $html ] )
1481              
1482             =head2 $mech->scrape_text_by_attr( $attr, $attr_regex [, $html ] )
1483              
1484             Returns a list of strings, each string the text surrounded by an
1485             element with attribute I<$attr> of value I<$value>. You can also pass in
1486             a regular expression. If nothing is found the return is an empty list.
1487             In scalar context the return is the first string found.
1488              
1489             If passed, I<$html> is scraped instead of the current page's content.
1490              
1491             =cut
1492              
1493             sub scrape_text_by_attr {
1494 0     0 1 0 my $self = shift;
1495 0         0 my $attr = shift;
1496 0         0 my $value = shift;
1497              
1498 0         0 my $html = $self->_get_optional_html( @_ );
1499              
1500 0         0 my @results;
1501              
1502 0 0       0 if ( defined $html ) {
1503 0         0 my $parser = HTML::TokeParser->new(\$html);
1504              
1505 0         0 while ( my $token = $parser->get_tag() ) {
1506 0 0       0 if ( ref $token->[1] eq 'HASH' ) {
1507 0 0       0 if ( exists $token->[1]->{$attr} ) {
1508             my $matched =
1509             (ref $value eq 'Regexp')
1510             ? $token->[1]->{$attr} =~ $value
1511 0 0       0 : $token->[1]->{$attr} eq $value;
1512 0 0       0 if ( $matched ) {
1513 0         0 my $tag = $token->[ 0 ];
1514 0         0 push @results, $parser->get_trimmed_text( "/$tag" );
1515 0 0       0 if ( !wantarray ) {
1516 0         0 last;
1517             }
1518             }
1519             }
1520             }
1521             }
1522             }
1523              
1524 0 0       0 return $results[0] if !wantarray;
1525 0         0 return @results;
1526             }
1527              
1528              
1529             =head2 $mech->scrape_text_by_id( $id [, $html ] )
1530              
1531             Finds all elements with the given ID attribute and pulls out the text that that element encloses.
1532              
1533             In list context, returns a list of all strings found. In scalar context, returns the first one found.
1534              
1535             If C<$html> is not provided then the current content is used.
1536              
1537             =cut
1538              
1539             sub scrape_text_by_id {
1540 28     28 1 98 my $self = shift;
1541 28         57 my $id = shift;
1542              
1543 28         62 my $html = $self->_get_optional_html( @_ );
1544              
1545 28         43 my @results;
1546              
1547 28 50       58 if ( defined $html ) {
1548             # If the ID doesn't appear anywhere in the text, then there's no point in parsing.
1549 28         58 my $found = index( $html, $id );
1550 28 100       52 if ( $found >= 0 ) {
1551 24         98 my $parser = HTML::TokeParser->new( \$html );
1552              
1553 24         2999 while ( my $token = $parser->get_tag() ) {
1554 190 100       4936 if ( ref $token->[1] eq 'HASH' ) {
1555 125         164 my $actual_id = $token->[1]->{id};
1556 125 100       217 $actual_id = '' unless defined $actual_id;
1557 125 100       327 if ( $actual_id eq $id ) {
1558 25         30 my $tag = $token->[ 0 ];
1559 25         81 push @results, $parser->get_trimmed_text( "/$tag" );
1560 25 100       1711 if ( !wantarray ) {
1561 19         107 last;
1562             }
1563             }
1564             }
1565             }
1566             }
1567             }
1568              
1569 28 100       192 return $results[0] if !wantarray;
1570 6         70 return @results;
1571             }
1572              
1573              
1574             sub _get_optional_html {
1575 28     28   37 my $self = shift;
1576              
1577 28         39 my $html;
1578 28 50       52 if ( @_ ) {
1579 0         0 $html = shift;
1580 0         0 assert_nonblank( $html, '$html passed in is a populated scalar' );
1581             }
1582             else {
1583 28 50       68 if ( $self->is_html ) {
1584 28         336 $html = $self->content();
1585             }
1586             }
1587              
1588 28         752 return $html;
1589             }
1590              
1591              
1592             =head2 $mech->scraped_id_is( $id, $expected [, $msg] )
1593              
1594             Scrapes the current page for given ID and tests that it matches the expected value.
1595              
1596             =cut
1597              
1598             sub scraped_id_is {
1599 11     11 1 2840 my $self = shift;
1600 11         29 my $id = shift;
1601 11         14 my $expected = shift;
1602 11         20 my $msg = shift;
1603              
1604 11         19 my $ok;
1605 11         33 my $got = $self->scrape_text_by_id( $id );
1606 11 100       34 if ( defined( $got ) ) {
1607 10         44 $ok = $TB->is_eq( $got, $expected, $msg );
1608             }
1609             else {
1610 1         9 $ok = $TB->ok( 0, $msg );
1611 1         1250 $TB->diag( qq{Can't find ID "$id" to compare to "$expected"} );
1612             }
1613              
1614 11         6186 return $ok;
1615             }
1616              
1617              
1618             =head2 $mech->scraped_id_like( $id, $expected_regex [, $msg] )
1619              
1620             Scrapes the current page for given id and tests that it matches the expected regex.
1621              
1622             =cut
1623              
1624             sub scraped_id_like {
1625 5     5 1 2561 my $self = shift;
1626 5         13 my $id = shift;
1627 5         7 my $expected = shift;
1628 5         10 my $msg = shift;
1629              
1630 5         11 my $ok;
1631 5         22 my $got = $self->scrape_text_by_id( $id );
1632 5 100       18 if ( defined($got) ) {
1633 4         20 $ok = $TB->like( $got, $expected, $msg );
1634             }
1635             else {
1636 1         13 $ok = $TB->ok( 0, $msg );
1637 1         1056 $TB->diag( qq{Can't find ID "$id" to match against $expected} );
1638             }
1639              
1640 5         2564 return $ok;
1641             }
1642              
1643              
1644             =head2 id_exists( $id )
1645              
1646             Returns TRUE/FALSE if the given ID exists in the given HTML, or if none
1647             is provided, then the current page.
1648              
1649             The Mech object caches the IDs so that it doesn't bother reparsing every
1650             time it's asked about an ID.
1651              
1652             =cut
1653              
1654             sub id_exists {
1655 28     28 1 5257 my $self = shift;
1656 28         58 my $id = shift;
1657              
1658 28         70 assert_is( $self->ct, 'text/html', 'Can only call id_exists on HTML pages' );
1659              
1660 28 100       400 if ( !$self->{ids} ) {
1661 2         5 my $ids = $self->{ids} = {};
1662             my $p = HTML::Parser->new(
1663             handlers => {
1664             start => [
1665             sub {
1666 10     10   100 my $attr = shift;
1667              
1668 10 100       37 if ( my $id = $attr->{id} ) {
1669 4         21 $ids->{$id} = 1;
1670             }
1671             },
1672 2         24 'attr'
1673             ],
1674             },
1675             );
1676 2         85 $p->parse( $self->content );
1677 2         17 $p->eof;
1678             }
1679              
1680 28         105 return $self->{ids}->{$id};
1681             }
1682              
1683              
1684             =head2 $agent->id_exists_ok( $id [, $msg] )
1685              
1686             Verifies there is an HTML element with ID C<$id> in the page.
1687              
1688             =cut
1689              
1690             sub id_exists_ok {
1691 11     11 1 2764 local $Test::Builder::Level = $Test::Builder::Level + 1;
1692              
1693 11         14 my $self = shift;
1694 11         12 my $id = shift;
1695 11   33     56 my $msg = shift || ('ID "' . ($id || '') . '" should exist');
1696              
1697 11         23 my $exists = $self->id_exists( $id );
1698              
1699 11         35 return $TB->ok( $exists, $msg );
1700             }
1701              
1702              
1703             =head2 $agent->ids_exist_ok( \@ids [, $msg] )
1704              
1705             Verifies an HTML element exists with each ID in C<\@ids>.
1706              
1707             =cut
1708              
1709             sub ids_exist_ok {
1710 4     4 1 1202 local $Test::Builder::Level = $Test::Builder::Level + 1;
1711              
1712 4         6 my $self = shift;
1713 4         5 my $ids = shift;
1714 4         10 my $msg = shift;
1715              
1716 4         19 assert_arrayref( $ids );
1717              
1718 4         30 my $subtest_name = 'ids_exist_ok( [' . join( ', ', @{$ids} ) . ']';
  4         12  
1719 4 100       12 $subtest_name .= ", $msg" if defined $msg;
1720 4         6 $subtest_name .= ' )';
1721              
1722             return $TB->subtest(
1723             $subtest_name,
1724             sub {
1725 4     4   3484 $TB->plan( tests => scalar @{$ids} );
  4         25  
1726              
1727 4         2382 foreach my $id ( @$ids ) {
1728 6         693 $self->id_exists_ok( $id );
1729             }
1730             }
1731 4         34 );
1732             }
1733              
1734             =head2 $agent->lacks_id_ok( $id [, $msg] )
1735              
1736             Verifies there is NOT an HTML element with ID C<$id> in the page.
1737              
1738             =cut
1739              
1740             sub lacks_id_ok {
1741 12     12 1 3220 local $Test::Builder::Level = $Test::Builder::Level + 1;
1742              
1743 12         25 my $self = shift;
1744 12         15 my $id = shift;
1745 12   66     35 my $msg = shift || ('ID "' . ($id || '') . '" should not exist');
1746              
1747 12         35 assert_nonblank( $id );
1748              
1749 12         88 my $exists = $self->id_exists( $id );
1750              
1751 12         35 return $TB->ok( !$exists, $msg );
1752             }
1753              
1754              
1755             =head2 $agent->lacks_ids_ok( \@ids [, $msg] )
1756              
1757             Verifies there are no HTML elements with any of the ids given in C<\@ids>.
1758              
1759             =cut
1760              
1761             sub lacks_ids_ok {
1762 4     4 1 1550 local $Test::Builder::Level = $Test::Builder::Level + 1;
1763              
1764 4         6 my $self = shift;
1765 4         6 my $ids = shift;
1766 4         12 my $msg = shift;
1767              
1768 4         11 assert_arrayref( $ids );
1769              
1770 4         28 my $subtest_name = 'lacks_ids_ok( [' . join( ', ', @{$ids} ) . ']';
  4         11  
1771 4 100       12 $subtest_name .= ", $msg" if defined $msg;
1772 4         18 $subtest_name .= ' )';
1773              
1774             return $TB->subtest(
1775             $subtest_name,
1776             sub {
1777 4     4   2672 $TB->plan( tests => scalar @{$ids} );
  4         13  
1778              
1779 4         2164 foreach my $id ( @$ids ) {
1780 9 50       1752 my $id_disp = defined($id) ? $id : '';
1781 9         31 $self->lacks_id_ok( $id, "ID '$id_disp' should not exist" );
1782             }
1783             }
1784 4         41 );
1785             }
1786              
1787              
1788             =head2 $mech->button_exists( $button )
1789              
1790             Returns a boolean saying whether the submit C<$button> exists. Does not
1791             do a test. For that you want C or C.
1792              
1793             =cut
1794              
1795             sub button_exists {
1796 4     4 1 16 my $self = shift;
1797 4         15 my $button = shift;
1798              
1799 4         72 my $input = $self->grep_inputs( {
1800             type => qr/^submit$/,
1801             name => qr/^$button$/
1802             } );
1803              
1804 4         33 return !!$input;
1805             }
1806              
1807              
1808             =head2 $mech->button_exists_ok( $button [, $msg] )
1809              
1810             Asserts that the button exists on the page.
1811              
1812             =cut
1813              
1814             sub button_exists_ok {
1815 1     1 1 3 local $Test::Builder::Level = $Test::Builder::Level + 1;
1816              
1817 1         2 my $self = shift;
1818 1         5 my $button = shift;
1819 1         3 my $msg = shift;
1820              
1821 1         4 return $TB->ok( $self->button_exists( $button ), $msg );
1822             }
1823              
1824              
1825             =head2 $mech->lacks_button_ok( $button [, $msg] )
1826              
1827             Asserts that the button exists on the page.
1828              
1829             =cut
1830              
1831             sub lacks_button_ok {
1832 1     1 1 3 local $Test::Builder::Level = $Test::Builder::Level + 1;
1833              
1834 1         2 my $self = shift;
1835 1         2 my $button = shift;
1836 1         2 my $msg = shift;
1837              
1838 1         4 return $TB->ok( !$self->button_exists( $button ), $msg );
1839             }
1840              
1841              
1842             =head1 METHODS: MISCELLANEOUS
1843              
1844             =head2 $mech->autolint( [$status] )
1845              
1846             Without an argument, this method returns a true or false value indicating
1847             whether autolint is active.
1848              
1849             When passed an argument, autolint is turned on or off depending on whether
1850             the argument is true or false, and the previous autolint status is returned.
1851             As with the autolint option of C<< new >>, C<< $status >> can be an
1852             L<< HTML::Lint >> object.
1853              
1854             If autolint is currently using an L<< HTML::Lint >> object you provided,
1855             the return is that object, so you can change and exactly restore
1856             autolint status:
1857              
1858             my $old_status = $mech->autolint( 0 );
1859             ... operations that should not be linted ...
1860             $mech->autolint( $old_status );
1861              
1862             =cut
1863              
1864             sub autolint {
1865 125     125 1 244 my $self = shift;
1866              
1867 125         258 my $ret = $self->{autolint};
1868 125 100       329 if ( @_ ) {
1869 37         98 $self->{autolint} = shift;
1870             }
1871              
1872 125         547 return $ret;
1873             }
1874              
1875              
1876             =head2 $mech->autotidy( [$status] )
1877              
1878             Without an argument, this method returns a true or false value indicating
1879             whether autotidy is active.
1880              
1881             When passed an argument, autotidy is turned on or off depending on whether
1882             the argument is true or false, and the previous autotidy status is returned.
1883             As with the autotidy option of C<< new >>, C<< $status >> can be an
1884             L<< HTML::Tidy5 >> object.
1885              
1886             If autotidy is currently using an L<< HTML::Tidy5 >> object you provided,
1887             the return is that object, so you can change and exactly restore
1888             autotidy status:
1889              
1890             my $old_status = $mech->autotidy( 0 );
1891             ... operations that should not be tidied ...
1892             $mech->autotidy( $old_status );
1893              
1894             =cut
1895              
1896             sub autotidy {
1897 81     81 1 166 my $self = shift;
1898              
1899 81         143 my $ret = $self->{autotidy};
1900 81 100       236 if ( @_ ) {
1901 37         95 $self->{autotidy} = shift;
1902             }
1903              
1904 81         201 return $ret;
1905             }
1906              
1907              
1908             =head2 $mech->grep_inputs( \%properties )
1909              
1910             Returns a list of all the input controls in the
1911             current form whose properties match all of the regexes in C<$properties>.
1912             The controls returned are all descended from HTML::Form::Input.
1913              
1914             If C<$properties> is undef or empty then all inputs will be
1915             returned.
1916              
1917             If there is no current page, there is no form on the current
1918             page, or there are no submit controls in the current form
1919             then the return will be an empty list.
1920              
1921             # Get all text controls whose names begin with "customer".
1922             my @customer_text_inputs =
1923             $mech->grep_inputs( {
1924             type => qr/^(text|textarea)$/,
1925             name => qr/^customer/
1926             }
1927             );
1928              
1929             =cut
1930              
1931             sub grep_inputs {
1932 6     6 1 10 my $self = shift;
1933 6         9 my $properties = shift;
1934              
1935 6         13 my @found;
1936              
1937 6         59 my $form = $self->current_form();
1938 6 50       6057 if ( $form ) {
1939 6         17 my @inputs = $form->inputs();
1940 6         54 @found = _grep_hashes( \@inputs, $properties );
1941             }
1942              
1943 6         31 return @found;
1944             }
1945              
1946              
1947             =head2 $mech->grep_submits( \%properties )
1948              
1949             grep_submits() does the same thing as grep_inputs() except that
1950             it only returns controls that are submit controls, ignoring
1951             other types of input controls like text and checkboxes.
1952              
1953             =cut
1954              
1955             sub grep_submits {
1956 0     0 1 0 my $self = shift;
1957 0   0     0 my $properties = shift || {};
1958              
1959 0         0 $properties->{type} = qr/^(?:submit|image)$/; # submits only
1960 0         0 my @found = $self->grep_inputs( $properties );
1961              
1962 0         0 return @found;
1963             }
1964              
1965             # Search an array of hashrefs, returning a list of the incoming
1966             # hashrefs that match *all* the pattern in $patterns.
1967             sub _grep_hashes {
1968 6     6   13 my $hashes = shift;
1969 6   50     12 my $patterns = shift || {};
1970              
1971 6         9 my @found;
1972              
1973 6 50       8 if ( ! %{$patterns} ) {
  6         16  
1974             # Nothing to match on, so return them all.
1975 0         0 @found = @{$hashes};
  0         0  
1976             }
1977             else {
1978 6         8 foreach my $hash ( @{$hashes} ) {
  6         23  
1979              
1980             # Check every pattern for a match on the current hash.
1981 23         32 my $matches_everything = 1;
1982 23         26 foreach my $pattern_key ( keys %{$patterns} ) {
  23         48  
1983 27 100 66     173 $matches_everything = 0 unless exists $hash->{$pattern_key} && $hash->{$pattern_key} =~ $patterns->{$pattern_key};
1984 27 100       56 last if !$matches_everything;
1985             }
1986              
1987 23 100       80 push @found, $hash if $matches_everything;
1988             }
1989             }
1990              
1991 6         35 return @found;
1992             }
1993              
1994              
1995             =head2 $mech->stuff_inputs( [\%options] )
1996              
1997             Finds all free-text input fields (text, textarea, and password) in the
1998             current form and fills them to their maximum length in hopes of finding
1999             application code that can't handle it. Fields with no maximum length
2000             and all textarea fields are set to 66000 bytes, which will often be
2001             enough to overflow the data's eventual receptacle.
2002              
2003             There is no return value.
2004              
2005             If there is no current form then nothing is done.
2006              
2007             The hashref $options can contain the following keys:
2008              
2009             =over
2010              
2011             =item * ignore
2012              
2013             hash value is arrayref of field names to not touch, e.g.:
2014              
2015             $mech->stuff_inputs( {
2016             ignore => [qw( specialfield1 specialfield2 )],
2017             } );
2018              
2019             =item * fill
2020              
2021             hash value is default string to use when stuffing fields. Copies
2022             of the string are repeated up to the max length of each field. E.g.:
2023              
2024             $mech->stuff_inputs( {
2025             fill => '@' # stuff all fields with something easy to recognize
2026             } );
2027              
2028             =item * specs
2029              
2030             hash value is arrayref of hashrefs with which you can pass detailed
2031             instructions about how to stuff a given field. E.g.:
2032              
2033             $mech->stuff_inputs( {
2034             specs=>{
2035             # Some fields are datatype-constrained. It's most common to
2036             # want the field stuffed with valid data.
2037             widget_quantity => { fill=>'9' },
2038             notes => { maxlength=>2000 },
2039             }
2040             } );
2041              
2042             The specs allowed are I (use this fill for the field rather than
2043             the default) and I (use this as the field's maxlength instead
2044             of any maxlength specified in the HTML).
2045              
2046             =back
2047              
2048             =cut
2049              
2050             sub stuff_inputs {
2051 7     7 1 6233 my $self = shift;
2052              
2053 7   100     28 my $options = shift || {};
2054 7         36 assert_isa( $options, 'HASH' );
2055 7         70 assert_in( $_, ['ignore', 'fill', 'specs'] ) foreach ( keys %{$options} );
  7         37  
2056              
2057             # set up the fill we'll use unless a field overrides it
2058 7         174 my $default_fill = '@';
2059 7 50 66     35 if ( exists $options->{fill} && defined $options->{fill} && length($options->{fill}) > 0 ) {
      66        
2060 3         7 $default_fill = $options->{fill};
2061             }
2062              
2063             # fields in the form to not stuff
2064 7         25 my $ignore = {};
2065 7 100       22 if ( exists $options->{ignore} ) {
2066 1         4 assert_isa( $options->{ignore}, 'ARRAY' );
2067 1         17 $ignore = { map {($_, 1)} @{$options->{ignore}} };
  1         8  
  1         3  
2068             }
2069              
2070 7         14 my $specs = {};
2071 7 100       15 if ( exists $options->{specs} ) {
2072 2         7 assert_isa( $options->{specs}, 'HASH' );
2073 2         16 $specs = $options->{specs};
2074 2         3 foreach my $field_name ( keys %{$specs} ) {
  2         6  
2075 4         51 assert_isa( $specs->{$field_name}, 'HASH' );
2076 4         25 assert_in( $_, ['fill', 'maxlength'] ) foreach ( keys %{$specs->{$field_name}} );
  4         15  
2077             }
2078             }
2079              
2080 7         134 my @inputs = $self->find_all_inputs( type_regex => qr/^(text|textarea|password)$/ );
2081              
2082 7         748 foreach my $field ( @inputs ) {
2083 42 50       274 next if $field->readonly();
2084 42 50       258 next if $field->disabled(); # TODO: HTML::Form::TextInput allows setting disabled--allow it here?
2085              
2086 42         239 my $name = $field->name();
2087              
2088             # skip if it's one of the fields to ignore
2089 42 100       244 next if exists $ignore->{ $name };
2090              
2091             # fields with no maxlength will get this many characters
2092 41         51 my $maxlength = 66000;
2093              
2094             # maxlength from the HTML
2095 41 100       60 if ( $field->type ne 'textarea' ) {
2096 34 100       129 if ( exists $field->{maxlength} ) {
2097 27         37 $maxlength = $field->{maxlength};
2098             # TODO: what to do about maxlength==0 ? non-numeric? less than 0 ?
2099             }
2100             }
2101              
2102 41         64 my $fill = $default_fill;
2103              
2104 41 100       68 if ( exists $specs->{$name} ) {
2105             # process the per-field info
2106              
2107 4 50 66     34 if ( exists $specs->{$name}->{fill} && defined $specs->{$name}->{fill} && length($specs->{$name}->{fill}) > 0 ) {
      66        
2108 3         6 $fill = $specs->{$name}->{fill};
2109             }
2110              
2111             # maxlength override from specs
2112 4 100 66     21 if ( exists $specs->{$name}->{maxlength} && defined $specs->{$name}->{maxlength} ) {
2113 2         5 $maxlength = $specs->{$name}->{maxlength};
2114             # TODO: what to do about maxlength==0 ? non-numeric? less than 0?
2115             }
2116             }
2117              
2118             # stuff it
2119 41 100       71 if ( ($maxlength % length($fill)) == 0 ) {
2120             # the simple case
2121 38         403 $field->value( $fill x ($maxlength/length($fill)) );
2122             }
2123             else {
2124             # can be improved later
2125 3         24 $field->value( substr( $fill x int(($maxlength + length($fill) - 1)/length($fill)), 0, $maxlength ) );
2126             }
2127             } # for @inputs
2128              
2129 7         66 return;
2130             }
2131              
2132              
2133             =head2 $mech->followable_links()
2134              
2135             Returns a list of links that Mech can follow. This is only http and
2136             https links.
2137              
2138             =cut
2139              
2140             sub followable_links {
2141 9     9 1 30 my $self = shift;
2142              
2143 9         121 return $self->find_all_links( url_abs_regex => qr{^(?:https?|file)://} );
2144             }
2145              
2146              
2147             =head2 $mech->lacks_uncapped_inputs( [$comment] )
2148              
2149             Executes a test to make sure that the current form content has no
2150             text input fields that lack the C attribute, and that each
2151             C value is a positive integer. The test fails if the current
2152             form has such a field, and succeeds otherwise.
2153              
2154             Checks that all text input fields in the current form specify a maximum
2155             input length. Fields for which the concept of input length is irrelevant,
2156             and controls that HTML does not allow to be capped (e.g. textarea)
2157             are ignored.
2158              
2159             The return is true if the test succeeded, false otherwise.
2160              
2161             =cut
2162              
2163             sub lacks_uncapped_inputs {
2164 2     2 1 3315 my $self = shift;
2165 2         11 my $comment = shift;
2166              
2167 2 50       8 $comment = 'All text inputs should have maxlength attributes' unless defined($comment);
2168              
2169 2         8 local $Test::Builder::Level = $Test::Builder::Level + 1;
2170              
2171 2         4 my @uncapped;
2172              
2173 2         19 my @inputs = $self->grep_inputs( { type => qr/^(?:text|password)$/ } );
2174 2         13 foreach my $field ( @inputs ) {
2175 9 50       71 next if $field->readonly();
2176 9 100       65 next if $field->disabled();
2177              
2178 7 100       51 if ( not defined($field->{maxlength}) ) {
2179 1         23 push( @uncapped, $field->name . ' has no maxlength attribute' );
2180 1         16 next;
2181             }
2182              
2183 6         13 my $val = $field->{maxlength};
2184 6 100 66     45 if ( ($val !~ /^\s*\d+\s*$/) || ($val+0 <= 0) ) {
2185 4         18 push( @uncapped, $field->name . qq{ has an invalid maxlength attribute of "$val"} );
2186             }
2187             }
2188              
2189 2         21 my $ok = $TB->ok( @uncapped == 0, $comment );
2190 2         1345 $TB->diag( $_ ) for @uncapped;
2191              
2192 2         1047 return $ok;
2193             }
2194              
2195             =head2 $mech->check_all_images_ok( [%criterium ], [$comment] )
2196              
2197             Executes a test to make sure all images in the page can be downloaded. It
2198             does this by running C requests on them. The current page content stays the same.
2199              
2200             The test fails if any image cannot be found, but reports all of the ones that were not found.
2201              
2202             For a definition of I, see L<< Cin WWW::Mechanize|WWW::Mechanize/$mech->images >>.
2203              
2204             The optional C<%criterium> argument can be passed in before the C<$comment> and will be used to define
2205             which images should be considered. This is useful to filter out specific paths.
2206              
2207             $mech->check_all_images_ok( url_regex => qr{^/}, 'All absolute images should exist');
2208             $mech->check_all_images_ok( url_regex => qr{\.(?:gif|jpg)$}, 'All gif and jpg images should exist');
2209             $mech->check_all_images_ok(
2210             url_regex => qr{^((?!\Qhttps://googleads.g.doubleclick.net/\E).)*$},
2211             'All images should exist, but Ignore the ones from Doubleclick'
2212             );
2213              
2214             For a full list of possible arguments see L<< Cin WWW::Mechanize|WWW::Mechanize/$mech->find_all_images >>.
2215              
2216             The return is true if the test succeeded, false otherwise.
2217              
2218             =cut
2219              
2220             sub check_all_images_ok {
2221 0     0 1   my $self = shift;
2222 0           my @args = @_;
2223              
2224 0           my $comment;
2225 0 0         if ( @args % 2 ) {
2226 0           $comment = pop @args;
2227             }
2228              
2229 0 0         $comment = 'All images in the page should exist' unless defined($comment);
2230              
2231 0           require HTTP::Request::Common;
2232              
2233 0           my @not_ok;
2234 0           foreach my $img ( map { $_->URI } $self->find_all_images(@args) ) {
  0            
2235 0           my $abs = $img->abs;
2236              
2237 0           state $head_cache; # Cache images we've already checked between calls.
2238 0 0         if ( !$head_cache->{$abs}++ ) {
2239             # WWW::Mechanize->_make_request makes a raw LWP::UserAgent request that does
2240             # not show up in our history and does not mess with our current content.
2241 0           my $res = $self->_make_request( HTTP::Request::Common::HEAD($abs) );
2242 0 0         if ( not $res->is_success ) {
2243 0           push( @not_ok, $img . ' returned code ' . $res->code );
2244             }
2245             }
2246             }
2247              
2248 0           my $ok = $TB->ok( @not_ok == 0, $comment );
2249 0           $TB->diag($_) for @not_ok;
2250              
2251 0           return $ok;
2252             }
2253              
2254             =head1 TODO
2255              
2256             Add HTML::Tidy capabilities.
2257              
2258             Other ideas for features are at https://github.com/petdance/test-www-mechanize
2259              
2260             =head1 AUTHOR
2261              
2262             Andy Lester, C<< >>
2263              
2264             =head1 BUGS
2265              
2266             Please report any bugs or feature requests to
2267             .
2268              
2269             =head1 SUPPORT
2270              
2271             You can find documentation for this module with the perldoc command.
2272              
2273             perldoc Test::WWW::Mechanize
2274              
2275             You can also look for information at:
2276              
2277             =over 4
2278              
2279             =item * Bug tracker
2280              
2281             L
2282              
2283             =item * CPAN Ratings
2284              
2285             L
2286              
2287             =item * Search CPAN
2288              
2289             L
2290              
2291             =back
2292              
2293             =head1 ACKNOWLEDGEMENTS
2294              
2295             Thanks to
2296             Julien Fiegehenn,
2297             @marderh,
2298             Eric A. Zarko,
2299             @moznion,
2300             Robert Stone,
2301             @tynovsky,
2302             Jerry Gay,
2303             Jonathan "Duke" Leto,
2304             Philip G. Potter,
2305             Niko Tyni,
2306             Greg Sheard,
2307             Michael Schwern,
2308             Mark Blackman,
2309             Mike O'Regan,
2310             Shawn Sorichetti,
2311             Chris Dolan,
2312             Matt Trout,
2313             MATSUNO Tokuhiro,
2314             and Pete Krawczyk for patches.
2315              
2316             =head1 COPYRIGHT & LICENSE
2317              
2318             Copyright 2004-2022 Andy Lester.
2319              
2320             This library is free software; you can redistribute it and/or modify it
2321             under the terms of the Artistic License version 2.0.
2322              
2323             =cut
2324              
2325             1; # End of Test::WWW::Mechanize