| 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 |