use
5.010;
our
$VERSION
=
'1.60'
;
assert_arrayref
assert_in
assert_is
assert_isa
assert_nonblank
)
;
my
$TB
= Test::Builder->new();
sub
new {
my
$class
=
shift
;
my
%args
= (
agent
=>
"Test-WWW-Mechanize/$VERSION"
,
@_
);
my
$autolint
=
delete
$args
{autolint};
my
$autotidy
=
delete
$args
{autotidy};
my
$self
=
$class
->SUPER::new(
%args
);
$self
->autolint(
$autolint
);
$self
->autotidy(
$autotidy
);
return
$self
;
}
sub
_reset_page {
my
$self
=
shift
;
$self
->SUPER::_reset_page(
@_
);
$self
->{ids} =
undef
;
return
;
}
sub
get_ok {
my
$self
=
shift
;
my
(
$url
,
$desc
,
%opts
) =
$self
->_unpack_args(
'GET'
,
@_
);
if
( !
defined
(
$url
) ) {
my
$ok
=
$TB
->ok( 0,
$desc
);
$TB
->diag(
'URL cannot be undef.'
);
return
$ok
;
}
$self
->get(
$url
,
%opts
);
my
$ok
=
$self
->success;
$ok
=
$self
->_post_load_validation(
$ok
,
$desc
);
return
$ok
;
}
sub
_post_load_validation {
my
$self
=
shift
;
my
$ok
=
shift
;
my
$desc
=
shift
;
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
if
(
$ok
) {
my
$emitted_ok
= 0;
if
(
$self
->is_html ) {
if
(
$self
->autolint &&
$self
->autotidy ) {
my
$msg
=
'autolint & autotidy'
;
$msg
.=
": $desc"
if
defined
$desc
;
$TB
->subtest(
$desc
,
sub
{
$self
->_lint_content_ok();
$self
->_tidy_content_ok();
}
);
++
$emitted_ok
;
}
else
{
if
(
$self
->autolint ) {
$ok
=
$self
->_lint_content_ok(
$desc
);
++
$emitted_ok
;
}
elsif
(
$self
->autotidy ) {
$ok
=
$self
->_tidy_content_ok(
$desc
);
++
$emitted_ok
;
}
}
}
if
( !
$emitted_ok
) {
$TB
->ok(
$ok
,
$desc
);
if
( !
$ok
) {
my
$url
=
$self
->_diag_url();
$TB
->diag(
$url
)
if
$url
;
}
}
}
else
{
$TB
->ok(
$ok
,
$desc
);
my
$url
=
$self
->_diag_url();
$TB
->diag(
$url
)
if
$url
;
$TB
->diag(
$self
->status );
$TB
->diag(
$self
->response->message )
if
$self
->response;
}
return
$ok
;
}
sub
head_ok {
my
$self
=
shift
;
my
(
$url
,
$desc
,
%opts
) =
$self
->_unpack_args(
'HEAD'
,
@_
);
if
( !
defined
(
$url
) ) {
my
$ok
=
$TB
->ok( 0,
$desc
);
$TB
->diag(
'URL cannot be undef.'
);
return
$ok
;
}
$self
->head(
$url
,
%opts
);
my
$ok
=
$self
->success;
$TB
->ok(
$ok
,
$desc
);
if
( !
$ok
) {
my
$url
=
$self
->_diag_url();
$TB
->diag(
$url
)
if
$url
;
$TB
->diag(
$self
->status );
$TB
->diag(
$self
->response->message )
if
$self
->response;
}
return
$ok
;
}
sub
post_ok {
my
$self
=
shift
;
my
(
$url
,
$desc
,
%opts
) =
$self
->_unpack_args(
'POST'
,
@_
);
if
( !
defined
(
$url
) ) {
my
$ok
=
$TB
->ok( 0,
$desc
);
$TB
->diag(
'URL cannot be undef.'
);
return
$ok
;
}
$self
->post(
$url
, \
%opts
);
my
$ok
=
$self
->success;
$ok
=
$self
->_post_load_validation(
$ok
,
$desc
);
return
$ok
;
}
sub
put_ok {
my
$self
=
shift
;
my
(
$url
,
$desc
,
%opts
) =
$self
->_unpack_args(
'PUT'
,
@_
);
if
( !
defined
(
$url
) ) {
my
$ok
=
$TB
->ok( 0,
$desc
);
$TB
->diag(
'URL cannot be undef.'
);
return
$ok
;
}
$opts
{content} =
''
if
!
exists
$opts
{content};
$self
->put(
$url
,
%opts
);
my
$ok
=
$self
->success;
$TB
->ok(
$ok
,
$desc
);
if
( !
$ok
) {
my
$url
=
$self
->_diag_url();
$TB
->diag(
$url
)
if
$url
;
$TB
->diag(
$self
->status );
$TB
->diag(
$self
->response->message )
if
$self
->response;
}
return
$ok
;
}
sub
delete_ok {
my
$self
=
shift
;
my
(
$url
,
$desc
,
%opts
) =
$self
->_unpack_args(
'DELETE'
,
@_
);
if
( !
defined
(
$url
) ) {
my
$ok
=
$TB
->ok( 0,
$desc
);
$TB
->diag(
'URL cannot be undef.'
);
return
$ok
;
}
if
(
$self
->can(
'delete'
)) {
$self
->
delete
(
$url
,
%opts
);
}
else
{
$self
->_delete(
$url
,
%opts
);
}
my
$ok
=
$self
->success;
$ok
=
$self
->_post_load_validation(
$ok
,
$desc
);
return
$ok
;
}
sub
_delete {
my
$self
=
shift
;
my
$uri
=
shift
;
$uri
=
$uri
->url
if
ref
(
$uri
) eq
'WWW::Mechanize::Link'
;
$uri
=
$self
->base
? URI->new_abs(
$uri
,
$self
->base )
: URI->new(
$uri
);
my
@parameters
= (
$uri
->as_string,
@_
);
my
@suff
=
$self
->_process_colonic_headers( \
@parameters
, 1 );
return
$self
->request( HTTP::Request::Common::DELETE(
@parameters
),
@suff
);
}
sub
submit_form_ok {
my
$self
=
shift
;
my
$parms
=
shift
|| {};
my
$desc
=
shift
;
if
(
ref
$parms
ne
'HASH'
) {
Carp::croak
'FATAL: parameters must be given as a hashref'
;
}
my
$response
=
$self
->submit_form( %{
$parms
} );
my
$ok
=
$response
&&
$response
->is_success;
$ok
=
$self
->_post_load_validation(
$ok
,
$desc
);
return
$ok
;
}
sub
follow_link_ok {
my
$self
=
shift
;
my
$parms
=
shift
|| {};
my
$desc
=
shift
;
if
(!
defined
(
$desc
)) {
my
$parms_str
=
join
(
', '
,
map
{
join
(
'='
,
$_
,
$parms
->{
$_
}) }
keys
(%{
$parms
}));
$desc
=
qq{Followed link with "$parms_str"}
if
!
defined
(
$desc
);
}
if
(
ref
$parms
ne
'HASH'
) {
Carp::croak
'FATAL: parameters must be given as a hashref'
;
}
my
$response
=
$self
->follow_link( %{
$parms
} );
my
$ok
=
$response
&&
$response
->is_success;
$ok
=
$self
->_post_load_validation(
$ok
,
$desc
);
return
$ok
;
}
sub
click_ok {
my
$self
=
shift
;
my
$button
=
shift
;
my
$desc
=
shift
;
my
$response
;
if
(
ref
(
$button
) eq
'ARRAY'
) {
$response
=
$self
->click(
$button
->[0],
$button
->[1],
$button
->[2] );
}
else
{
$response
=
$self
->click(
$button
);
}
if
( !
$response
) {
return
$TB
->ok( 0,
$desc
);
}
my
$ok
=
$response
->is_success;
$ok
=
$self
->_post_load_validation(
$ok
,
$desc
);
return
$ok
;
}
sub
_unpack_args {
my
$self
=
shift
;
my
$method
=
shift
;
my
$url
=
shift
;
my
$desc
;
my
%opts
;
if
(
@_
) {
my
$flex
=
shift
;
if
( !
defined
(
$flex
) ) {
$desc
=
shift
;
}
elsif
(
ref
$flex
eq
'HASH'
) {
%opts
= %{
$flex
};
$desc
=
shift
;
}
elsif
(
ref
$flex
eq
'ARRAY'
) {
%opts
= @{
$flex
};
$desc
=
shift
;
}
else
{
$desc
=
$flex
;
}
}
if
( not
defined
$desc
) {
$url
=
$url
->url
if
ref
(
$url
) eq
'WWW::Mechanize::Link'
;
$desc
=
"$method $url"
;
}
return
(
$url
,
$desc
,
%opts
);
}
sub
header_exists_ok {
my
$self
=
shift
;
my
$header
=
shift
;
my
$desc
=
shift
||
qq{Response has $header header}
;
return
$TB
->ok(
defined
(
$self
->response->header(
$header
)),
$desc
);
}
sub
lacks_header_ok {
my
$self
=
shift
;
my
$header
=
shift
;
my
$desc
=
shift
||
qq{Response lacks $header header}
;
return
$TB
->ok( !
defined
(
$self
->response->header(
$header
)),
$desc
);
}
sub
header_is {
my
$self
=
shift
;
my
$header
=
shift
;
my
$value
=
shift
;
my
$desc
=
shift
||
qq{Response has $header header with value "$value"}
;
my
$actual_value
=
$self
->response->header(
$header
);
my
$ok
;
if
(
defined
(
$actual_value
) ) {
$ok
=
$TB
->is_eq(
$actual_value
,
$value
,
$desc
);
}
else
{
$ok
=
$TB
->ok( 0,
$desc
);
$TB
->diag(
"Header $header does not exist"
);
}
return
$ok
;
}
sub
header_like {
my
$self
=
shift
;
my
$header
=
shift
;
my
$regex
=
shift
;
my
$desc
=
shift
||
qq{Response has $header header that matches regex $regex}
;
my
$actual_value
=
$self
->response->header(
$header
);
return
$TB
->like(
$self
->response->header(
$header
),
$regex
,
$desc
);
}
sub
html_lint_ok {
my
$self
=
shift
;
my
$desc
=
shift
;
my
$uri
=
$self
->uri;
$desc
=
$desc
?
"$desc ($uri)"
:
$uri
;
my
$ok
;
if
(
$self
->is_html ) {
$ok
=
$self
->_lint_content_ok(
$desc
);
}
else
{
$ok
=
$TB
->ok( 0,
$desc
);
$TB
->diag(
q{This page doesn't appear to be HTML, or didn't get the proper text/html content type returned.}
);
}
return
$ok
;
}
sub
_lint_content_ok {
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
my
$self
=
shift
;
my
$desc
=
shift
;
my
$module
=
"HTML::Lint 2.20"
;
if
( not (
eval
"use $module; 1;"
) ) {
die
"Test::WWW::Mechanize can't do linting without $module: $@"
;
}
my
$lint
=
$self
->{autolint};
if
(
ref
$lint
&&
$lint
->isa(
'HTML::Lint'
) ) {
$lint
->newfile;
$lint
->clear_errors;
}
else
{
$lint
= HTML::Lint->new();
}
$lint
->parse(
$self
->content );
$lint
->
eof
();
my
@errors
=
$lint
->errors;
my
$nerrors
=
@errors
;
my
$ok
;
if
(
$nerrors
) {
$ok
=
$TB
->ok( 0,
$desc
);
$TB
->diag(
'HTML::Lint errors for '
.
$self
->uri );
$TB
->diag(
$_
->as_string )
for
@errors
;
my
$s
=
$nerrors
== 1 ?
''
:
's'
;
$TB
->diag(
"$nerrors error$s on the page"
);
}
else
{
$ok
=
$TB
->ok( 1,
$desc
);
}
return
$ok
;
}
sub
html_tidy_ok {
my
$self
=
shift
;
my
$desc
=
shift
;
my
$uri
=
$self
->uri;
$desc
=
$desc
?
"$desc ($uri)"
:
$uri
;
my
$ok
;
if
(
$self
->is_html ) {
$ok
=
$self
->_tidy_content_ok(
$desc
);
}
else
{
$ok
=
$TB
->ok( 0,
$desc
);
$TB
->diag(
q{This page doesn't appear to be HTML, or didn't get the proper text/html content type returned.}
);
}
return
$ok
;
}
sub
_tidy_content_ok {
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
my
$self
=
shift
;
my
$desc
=
shift
;
my
$module
=
'HTML::Tidy5 1.00'
;
if
( not (
eval
"use $module; 1;"
) ) {
die
"Test::WWW::Mechanize can't do tidying without $module: $@"
;
}
my
$tidy
=
$self
->{autotidy};
if
(
ref
$tidy
&&
$tidy
->isa(
'HTML::Tidy5'
) ) {
$tidy
->clear_messages();
}
else
{
$tidy
= HTML::Tidy5->new();
}
$tidy
->parse(
''
,
$self
->content_for_tidy );
my
@messages
=
$tidy
->messages;
my
$nmessages
=
@messages
;
my
$ok
;
if
(
$nmessages
) {
$ok
=
$TB
->ok( 0,
$desc
);
$TB
->diag(
'HTML::Tidy5 messages for '
.
$self
->uri );
$TB
->diag(
$_
->as_string )
for
@messages
;
my
$s
=
$nmessages
== 1 ?
''
:
's'
;
$TB
->diag(
"$nmessages message$s on the page"
);
}
else
{
$ok
=
$TB
->ok( 1,
$desc
);
}
return
$ok
;
}
sub
content_for_tidy {
my
$self
=
shift
;
return
$self
->content;
}
sub
title_is {
my
$self
=
shift
;
my
$str
=
shift
;
my
$desc
=
shift
;
$desc
=
qq{Title is "$str"}
if
!
defined
(
$desc
);
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
return
is_string(
$self
->title,
$str
,
$desc
);
}
sub
title_like {
my
$self
=
shift
;
my
$regex
=
shift
;
my
$desc
=
shift
;
$desc
=
qq{Title is like "$regex"}
if
!
defined
(
$desc
);
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
return
like_string(
$self
->title,
$regex
,
$desc
);
}
sub
title_unlike {
my
$self
=
shift
;
my
$regex
=
shift
;
my
$desc
=
shift
;
$desc
=
qq{Title is unlike "$regex"}
if
!
defined
(
$desc
);
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
return
unlike_string(
$self
->title,
$regex
,
$desc
);
}
sub
base_is {
my
$self
=
shift
;
my
$str
=
shift
;
my
$desc
=
shift
;
$desc
=
qq{Base is "$str"}
if
!
defined
(
$desc
);
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
return
is_string(
$self
->base,
$str
,
$desc
);
}
sub
base_like {
my
$self
=
shift
;
my
$regex
=
shift
;
my
$desc
=
shift
;
$desc
=
qq{Base is like "$regex"}
if
!
defined
(
$desc
);
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
return
like_string(
$self
->base,
$regex
,
$desc
);
}
sub
base_unlike {
my
$self
=
shift
;
my
$regex
=
shift
;
my
$desc
=
shift
;
$desc
=
qq{Base is unlike "$regex"}
if
!
defined
(
$desc
);
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
return
unlike_string(
$self
->base,
$regex
,
$desc
);
}
sub
content_is {
my
$self
=
shift
;
my
$str
=
shift
;
my
$desc
=
shift
;
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
$desc
=
qq{Content is "$str"}
if
!
defined
(
$desc
);
return
is_string(
$self
->content,
$str
,
$desc
);
}
sub
content_contains {
my
$self
=
shift
;
my
$str
=
shift
;
my
$desc
=
shift
;
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
if
(
ref
(
$str
) ) {
return
$TB
->ok( 0,
'Test::WWW::Mechanize->content_contains called incorrectly. It requires a scalar, not a reference.'
);
}
$desc
=
qq{Content contains "$str"}
if
!
defined
(
$desc
);
return
contains_string(
$self
->content,
$str
,
$desc
);
}
sub
content_lacks {
my
$self
=
shift
;
my
$str
=
shift
;
my
$desc
=
shift
;
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
if
(
ref
(
$str
) ) {
return
$TB
->ok( 0,
'Test::WWW::Mechanize->content_lacks called incorrectly. It requires a scalar, not a reference.'
);
}
$desc
=
qq{Content lacks "$str"}
if
!
defined
(
$desc
);
return
lacks_string(
$self
->content,
$str
,
$desc
);
}
sub
content_like {
my
$self
=
shift
;
my
$regex
=
shift
;
my
$desc
=
shift
;
$desc
=
qq{Content is like "$regex"}
if
!
defined
(
$desc
);
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
return
like_string(
$self
->content,
$regex
,
$desc
);
}
sub
content_unlike {
my
$self
=
shift
;
my
$regex
=
shift
;
my
$desc
=
shift
||
qq{Content is unlike "$regex"}
;
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
return
unlike_string(
$self
->content,
$regex
,
$desc
);
}
sub
text_contains {
my
$self
=
shift
;
my
$str
=
shift
;
my
$desc
=
shift
||
qq{Text contains "$str"}
;
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
if
(
ref
(
$str
) ) {
return
$TB
->ok( 0,
'Test::WWW::Mechanize->text_contains called incorrectly. It requires a scalar, not a reference.'
);
}
return
contains_string(
$self
->text,
$str
,
$desc
);
}
sub
text_lacks {
my
$self
=
shift
;
my
$str
=
shift
;
my
$desc
=
shift
;
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
if
(
ref
(
$str
) ) {
return
$TB
->ok( 0,
'Test::WWW::Mechanize->text_lacks called incorrectly. It requires a scalar, not a reference.'
);
}
$desc
=
qq{Text lacks "$str"}
if
!
defined
(
$desc
);
return
lacks_string(
$self
->text,
$str
,
$desc
);
}
sub
text_like {
my
$self
=
shift
;
my
$regex
=
shift
;
my
$desc
=
shift
||
qq{Text is like "$regex"}
;
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
return
like_string(
$self
->text,
$regex
,
$desc
);
}
sub
text_unlike {
my
$self
=
shift
;
my
$regex
=
shift
;
my
$desc
=
shift
||
qq{Text is unlike "$regex"}
;
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
return
unlike_string(
$self
->text,
$regex
,
$desc
);
}
sub
has_tag {
my
$self
=
shift
;
my
$tag
=
shift
;
my
$text
=
shift
;
my
$desc
=
shift
||
qq{Page has $tag tag with "$text"}
;
my
$found
=
$self
->_tag_walk(
$tag
,
sub
{
$text
eq
$_
[0] } );
return
$TB
->ok(
$found
,
$desc
);
}
sub
has_tag_like {
my
$self
=
shift
;
my
$tag
=
shift
;
my
$regex
=
shift
;
my
$desc
=
shift
;
$desc
=
qq{Page has $tag tag like "$regex"}
if
!
defined
(
$desc
);
my
$found
=
$self
->_tag_walk(
$tag
,
sub
{
$_
[0] =~
$regex
} );
return
$TB
->ok(
$found
,
$desc
);
}
sub
_tag_walk {
my
$self
=
shift
;
my
$tag
=
shift
;
my
$match
=
shift
;
my
$p
= HTML::TokeParser->new( \(
$self
->content) );
while
(
my
$token
=
$p
->get_tag(
$tag
) ) {
my
$tagtext
=
$p
->get_trimmed_text();
return
1
if
$match
->(
$tagtext
);
}
return
;
}
sub
page_links_ok {
my
$self
=
shift
;
my
$desc
=
shift
;
$desc
=
'All links ok'
unless
defined
$desc
;
my
@links
=
$self
->followable_links();
my
@urls
= _format_links(\
@links
);
my
@failures
=
$self
->_check_links_status( \
@urls
);
my
$ok
= (
@failures
==0);
$TB
->ok(
$ok
,
$desc
);
$TB
->diag(
$_
)
for
@failures
;
return
$ok
;
}
sub
page_links_content_like {
my
$self
=
shift
;
my
$regex
=
shift
;
my
$desc
=
shift
;
$desc
=
qq{All links are like "$regex"}
unless
defined
$desc
;
my
$usable_regex
=
$TB
->maybe_regex(
$regex
);
if
( !
defined
(
$usable_regex
) ) {
my
$ok
=
$TB
->ok( 0,
'page_links_content_like'
);
$TB
->diag(
qq{ "$regex" doesn't look much like a regex to me.}
);
return
$ok
;
}
my
@links
=
$self
->followable_links();
my
@urls
= _format_links(\
@links
);
my
@failures
=
$self
->_check_links_content( \
@urls
,
$regex
);
my
$ok
= (
@failures
==0);
$TB
->ok(
$ok
,
$desc
);
$TB
->diag(
$_
)
for
@failures
;
return
$ok
;
}
sub
page_links_content_unlike {
my
$self
=
shift
;
my
$regex
=
shift
;
my
$desc
=
shift
;
$desc
=
qq{All links are unlike "$regex"}
unless
defined
(
$desc
);
my
$usable_regex
=
$TB
->maybe_regex(
$regex
);
if
( !
defined
(
$usable_regex
) ) {
my
$ok
=
$TB
->ok( 0,
'page_links_content_unlike'
);
$TB
->diag(
qq{ "$regex" doesn't look much like a regex to me.}
);
return
$ok
;
}
my
@links
=
$self
->followable_links();
my
@urls
= _format_links(\
@links
);
my
@failures
=
$self
->_check_links_content( \
@urls
,
$regex
,
'unlike'
);
my
$ok
= (
@failures
==0);
$TB
->ok(
$ok
,
$desc
);
$TB
->diag(
$_
)
for
@failures
;
return
$ok
;
}
sub
links_ok {
my
$self
=
shift
;
my
$links
=
shift
;
my
$desc
=
shift
;
my
@urls
= _format_links(
$links
);
$desc
= _default_links_desc(\
@urls
,
'are ok'
)
unless
defined
$desc
;
my
@failures
=
$self
->_check_links_status( \
@urls
);
my
$ok
= (
@failures
== 0);
$TB
->ok(
$ok
,
$desc
);
$TB
->diag(
$_
)
for
@failures
;
return
$ok
;
}
sub
link_status_is {
my
$self
=
shift
;
my
$links
=
shift
;
my
$status
=
shift
;
my
$desc
=
shift
;
my
@urls
= _format_links(
$links
);
$desc
= _default_links_desc(\
@urls
,
"have status $status"
)
if
!
defined
(
$desc
);
my
@failures
=
$self
->_check_links_status( \
@urls
,
$status
);
my
$ok
= (
@failures
== 0);
$TB
->ok(
$ok
,
$desc
);
$TB
->diag(
$_
)
for
@failures
;
return
$ok
;
}
sub
link_status_isnt {
my
$self
=
shift
;
my
$links
=
shift
;
my
$status
=
shift
;
my
$desc
=
shift
;
my
@urls
= _format_links(
$links
);
$desc
= _default_links_desc(\
@urls
,
"do not have status $status"
)
if
!
defined
(
$desc
);
my
@failures
=
$self
->_check_links_status( \
@urls
,
$status
,
'isnt'
);
my
$ok
= (
@failures
== 0);
$TB
->ok(
$ok
,
$desc
);
$TB
->diag(
$_
)
for
@failures
;
return
$ok
;
}
sub
link_content_like {
my
$self
=
shift
;
my
$links
=
shift
;
my
$regex
=
shift
;
my
$desc
=
shift
;
my
$usable_regex
=
$TB
->maybe_regex(
$regex
);
if
( !
defined
(
$usable_regex
) ) {
my
$ok
=
$TB
->ok( 0,
'link_content_like'
);
$TB
->diag(
qq{ "$regex" doesn't look much like a regex to me.}
);
return
$ok
;
}
my
@urls
= _format_links(
$links
);
$desc
= _default_links_desc( \
@urls
,
qq{are like "$regex"}
)
if
!
defined
(
$desc
);
my
@failures
=
$self
->_check_links_content( \
@urls
,
$regex
);
my
$ok
= (
@failures
== 0);
$TB
->ok(
$ok
,
$desc
);
$TB
->diag(
$_
)
for
@failures
;
return
$ok
;
}
sub
link_content_unlike {
my
$self
=
shift
;
my
$links
=
shift
;
my
$regex
=
shift
;
my
$desc
=
shift
;
my
$usable_regex
=
$TB
->maybe_regex(
$regex
);
if
( !
defined
(
$usable_regex
) ) {
my
$ok
=
$TB
->ok( 0,
'link_content_unlike'
);
$TB
->diag(
qq{ "$regex" doesn't look much like a regex to me.}
);
return
$ok
;
}
my
@urls
= _format_links(
$links
);
$desc
= _default_links_desc( \
@urls
,
qq{are not like "$regex"}
)
if
!
defined
(
$desc
);
my
@failures
=
$self
->_check_links_content( \
@urls
,
$regex
,
'unlike'
);
my
$ok
= (
@failures
== 0);
$TB
->ok(
$ok
,
$desc
);
$TB
->diag(
$_
)
for
@failures
;
return
$ok
;
}
sub
_default_links_desc {
my
(
$urls
,
$desc_suffix
) =
@_
;
my
$url_count
=
scalar
(@{
$urls
});
return
sprintf
(
'%d link%s %s'
,
$url_count
,
$url_count
== 1 ?
''
:
's'
,
$desc_suffix
);
}
sub
_check_links_status {
my
$self
=
shift
;
my
$urls
=
shift
;
my
$status
=
shift
|| 200;
my
$test
=
shift
||
'is'
;
my
$mech
=
$self
->clone();
my
@failures
;
for
my
$url
( @{
$urls
} ) {
if
(
$mech
->follow_link(
url
=>
$url
) ) {
if
(
$test
eq
'is'
) {
push
(
@failures
,
$url
)
unless
$mech
->status() ==
$status
;
}
else
{
push
(
@failures
,
$url
)
if
$mech
->status() ==
$status
;
}
$mech
->back();
}
else
{
push
(
@failures
,
$url
);
}
}
return
@failures
;
}
sub
_check_links_content {
my
$self
=
shift
;
my
$urls
=
shift
;
my
$regex
=
shift
||
qr/<html>/
;
my
$test
=
shift
||
'like'
;
my
$mech
=
$self
->clone();
my
@failures
;
for
my
$url
( @{
$urls
} ) {
if
(
$mech
->follow_link(
url
=>
$url
) ) {
my
$content
=
$mech
->content();
if
(
$test
eq
'like'
) {
push
(
@failures
,
$url
)
unless
$content
=~ /
$regex
/;
}
else
{
push
(
@failures
,
$url
)
if
$content
=~ /
$regex
/;
}
$mech
->back();
}
else
{
push
(
@failures
,
$url
);
}
}
return
@failures
;
}
sub
_format_links {
my
$links
=
shift
;
my
@urls
;
if
(
ref
(
$links
) eq
'ARRAY'
) {
my
$link
=
$links
->[0];
if
(
defined
(
$link
) ) {
if
(
ref
(
$link
) eq
'WWW::Mechanize::Link'
) {
@urls
=
map
{
$_
->url() } @{
$links
};
}
else
{
@urls
= @{
$links
};
}
}
}
else
{
push
(
@urls
,
$links
);
}
return
@urls
;
}
sub
scrape_text_by_attr {
my
$self
=
shift
;
my
$attr
=
shift
;
my
$value
=
shift
;
my
$html
=
$self
->_get_optional_html(
@_
);
my
@results
;
if
(
defined
$html
) {
my
$parser
= HTML::TokeParser->new(\
$html
);
while
(
my
$token
=
$parser
->get_tag() ) {
if
(
ref
$token
->[1] eq
'HASH'
) {
if
(
exists
$token
->[1]->{
$attr
} ) {
my
$matched
=
(
ref
$value
eq
'Regexp'
)
?
$token
->[1]->{
$attr
} =~
$value
:
$token
->[1]->{
$attr
} eq
$value
;
if
(
$matched
) {
my
$tag
=
$token
->[ 0 ];
push
@results
,
$parser
->get_trimmed_text(
"/$tag"
);
if
( !
wantarray
) {
last
;
}
}
}
}
}
}
return
$results
[0]
if
!
wantarray
;
return
@results
;
}
sub
scrape_text_by_id {
my
$self
=
shift
;
my
$id
=
shift
;
my
$html
=
$self
->_get_optional_html(
@_
);
my
@results
;
if
(
defined
$html
) {
my
$found
=
index
(
$html
,
$id
);
if
(
$found
>= 0 ) {
my
$parser
= HTML::TokeParser->new( \
$html
);
while
(
my
$token
=
$parser
->get_tag() ) {
if
(
ref
$token
->[1] eq
'HASH'
) {
my
$actual_id
=
$token
->[1]->{id};
$actual_id
=
''
unless
defined
$actual_id
;
if
(
$actual_id
eq
$id
) {
my
$tag
=
$token
->[ 0 ];
push
@results
,
$parser
->get_trimmed_text(
"/$tag"
);
if
( !
wantarray
) {
last
;
}
}
}
}
}
}
return
$results
[0]
if
!
wantarray
;
return
@results
;
}
sub
_get_optional_html {
my
$self
=
shift
;
my
$html
;
if
(
@_
) {
$html
=
shift
;
assert_nonblank(
$html
,
'$html passed in is a populated scalar'
);
}
else
{
if
(
$self
->is_html ) {
$html
=
$self
->content();
}
}
return
$html
;
}
sub
scraped_id_is {
my
$self
=
shift
;
my
$id
=
shift
;
my
$expected
=
shift
;
my
$msg
=
shift
;
my
$ok
;
my
$got
=
$self
->scrape_text_by_id(
$id
);
if
(
defined
(
$got
) ) {
$ok
=
$TB
->is_eq(
$got
,
$expected
,
$msg
);
}
else
{
$ok
=
$TB
->ok( 0,
$msg
);
$TB
->diag(
qq{Can't find ID "$id" to compare to "$expected"}
);
}
return
$ok
;
}
sub
scraped_id_like {
my
$self
=
shift
;
my
$id
=
shift
;
my
$expected
=
shift
;
my
$msg
=
shift
;
my
$ok
;
my
$got
=
$self
->scrape_text_by_id(
$id
);
if
(
defined
(
$got
) ) {
$ok
=
$TB
->like(
$got
,
$expected
,
$msg
);
}
else
{
$ok
=
$TB
->ok( 0,
$msg
);
$TB
->diag(
qq{Can't find ID "$id" to match against $expected}
);
}
return
$ok
;
}
sub
id_exists {
my
$self
=
shift
;
my
$id
=
shift
;
assert_is(
$self
->ct,
'text/html'
,
'Can only call id_exists on HTML pages'
);
if
( !
$self
->{ids} ) {
my
$ids
=
$self
->{ids} = {};
my
$p
= HTML::Parser->new(
handlers
=> {
start
=> [
sub
{
my
$attr
=
shift
;
if
(
my
$id
=
$attr
->{id} ) {
$ids
->{
$id
} = 1;
}
},
'attr'
],
},
);
$p
->parse(
$self
->content );
$p
->
eof
;
}
return
$self
->{ids}->{
$id
};
}
sub
id_exists_ok {
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
my
$self
=
shift
;
my
$id
=
shift
;
my
$msg
=
shift
|| (
'ID "'
. (
$id
||
''
) .
'" should exist'
);
my
$exists
=
$self
->id_exists(
$id
);
return
$TB
->ok(
$exists
,
$msg
);
}
sub
ids_exist_ok {
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
my
$self
=
shift
;
my
$ids
=
shift
;
my
$msg
=
shift
;
assert_arrayref(
$ids
);
my
$subtest_name
=
'ids_exist_ok( ['
.
join
(
', '
, @{
$ids
} ) .
']'
;
$subtest_name
.=
", $msg"
if
defined
$msg
;
$subtest_name
.=
' )'
;
return
$TB
->subtest(
$subtest_name
,
sub
{
$TB
->plan(
tests
=>
scalar
@{
$ids
} );
foreach
my
$id
(
@$ids
) {
$self
->id_exists_ok(
$id
);
}
}
);
}
sub
lacks_id_ok {
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
my
$self
=
shift
;
my
$id
=
shift
;
my
$msg
=
shift
|| (
'ID "'
. (
$id
||
''
) .
'" should not exist'
);
assert_nonblank(
$id
);
my
$exists
=
$self
->id_exists(
$id
);
return
$TB
->ok( !
$exists
,
$msg
);
}
sub
lacks_ids_ok {
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
my
$self
=
shift
;
my
$ids
=
shift
;
my
$msg
=
shift
;
assert_arrayref(
$ids
);
my
$subtest_name
=
'lacks_ids_ok( ['
.
join
(
', '
, @{
$ids
} ) .
']'
;
$subtest_name
.=
", $msg"
if
defined
$msg
;
$subtest_name
.=
' )'
;
return
$TB
->subtest(
$subtest_name
,
sub
{
$TB
->plan(
tests
=>
scalar
@{
$ids
} );
foreach
my
$id
(
@$ids
) {
my
$id_disp
=
defined
(
$id
) ?
$id
:
'<undef>'
;
$self
->lacks_id_ok(
$id
,
"ID '$id_disp' should not exist"
);
}
}
);
}
sub
button_exists {
my
$self
=
shift
;
my
$button
=
shift
;
my
$input
=
$self
->grep_inputs( {
type
=>
qr/^submit$/
,
name
=>
qr/^$button$/
} );
return
!!
$input
;
}
sub
button_exists_ok {
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
my
$self
=
shift
;
my
$button
=
shift
;
my
$msg
=
shift
||
qq{Button named "$button" exists}
;
return
$TB
->ok(
$self
->button_exists(
$button
),
$msg
);
}
sub
lacks_button_ok {
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
my
$self
=
shift
;
my
$button
=
shift
;
my
$msg
=
shift
||
qq{No button named "$button" exists}
;
return
$TB
->ok( !
$self
->button_exists(
$button
),
$msg
);
}
sub
autolint {
my
$self
=
shift
;
my
$ret
=
$self
->{autolint};
if
(
@_
) {
$self
->{autolint} =
shift
;
}
return
$ret
;
}
sub
autotidy {
my
$self
=
shift
;
my
$ret
=
$self
->{autotidy};
if
(
@_
) {
$self
->{autotidy} =
shift
;
}
return
$ret
;
}
sub
grep_inputs {
my
$self
=
shift
;
my
$properties
=
shift
;
my
@found
;
my
$form
=
$self
->current_form();
if
(
$form
) {
my
@inputs
=
$form
->inputs();
@found
= _grep_hashes( \
@inputs
,
$properties
);
}
return
@found
;
}
sub
grep_submits {
my
$self
=
shift
;
my
$properties
=
shift
|| {};
$properties
->{type} =
qr/^(?:submit|image)$/
;
my
@found
=
$self
->grep_inputs(
$properties
);
return
@found
;
}
sub
_grep_hashes {
my
$hashes
=
shift
;
my
$patterns
=
shift
|| {};
my
@found
;
if
( ! %{
$patterns
} ) {
@found
= @{
$hashes
};
}
else
{
foreach
my
$hash
( @{
$hashes
} ) {
my
$matches_everything
= 1;
foreach
my
$pattern_key
(
keys
%{
$patterns
} ) {
$matches_everything
= 0
unless
exists
$hash
->{
$pattern_key
} &&
$hash
->{
$pattern_key
} =~
$patterns
->{
$pattern_key
};
last
if
!
$matches_everything
;
}
push
@found
,
$hash
if
$matches_everything
;
}
}
return
@found
;
}
sub
stuff_inputs {
my
$self
=
shift
;
my
$options
=
shift
|| {};
assert_isa(
$options
,
'HASH'
);
assert_in(
$_
, [
'ignore'
,
'fill'
,
'specs'
] )
foreach
(
keys
%{
$options
} );
my
$default_fill
=
'@'
;
if
(
defined
$options
->{fill} &&
length
(
$options
->{fill}) > 0 ) {
$default_fill
=
$options
->{fill};
}
my
$ignore
= {};
if
(
exists
$options
->{ignore} ) {
assert_isa(
$options
->{ignore},
'ARRAY'
);
$ignore
= {
map
{(
$_
, 1)} @{
$options
->{ignore}} };
}
my
$specs
= {};
if
(
exists
$options
->{specs} ) {
assert_isa(
$options
->{specs},
'HASH'
);
$specs
=
$options
->{specs};
foreach
my
$field_name
(
keys
%{
$specs
} ) {
assert_isa(
$specs
->{
$field_name
},
'HASH'
);
assert_in(
$_
, [
'fill'
,
'maxlength'
] )
foreach
(
keys
%{
$specs
->{
$field_name
}} );
}
}
my
@inputs
=
$self
->find_all_inputs(
type_regex
=>
qr/^(text|textarea|password)$/
);
foreach
my
$field
(
@inputs
) {
next
if
$field
->readonly();
next
if
$field
->disabled();
my
$name
=
$field
->name();
next
if
exists
$ignore
->{
$name
};
my
$maxlength
= 66000;
if
(
$field
->type ne
'textarea'
) {
if
(
exists
$field
->{maxlength} ) {
$maxlength
=
$field
->{maxlength};
}
}
my
$fill
=
$default_fill
;
if
(
exists
$specs
->{
$name
} ) {
if
(
exists
$specs
->{
$name
}->{fill} &&
defined
$specs
->{
$name
}->{fill} &&
length
(
$specs
->{
$name
}->{fill}) > 0 ) {
$fill
=
$specs
->{
$name
}->{fill};
}
if
(
exists
$specs
->{
$name
}->{maxlength} &&
defined
$specs
->{
$name
}->{maxlength} ) {
$maxlength
=
$specs
->{
$name
}->{maxlength};
}
}
if
( (
$maxlength
%
length
(
$fill
)) == 0 ) {
$field
->value(
$fill
x (
$maxlength
/
length
(
$fill
)) );
}
else
{
$field
->value(
substr
(
$fill
x
int
((
$maxlength
+
length
(
$fill
) - 1)/
length
(
$fill
)), 0,
$maxlength
) );
}
}
return
;
}
sub
followable_links {
my
$self
=
shift
;
return
$self
->find_all_links(
url_abs_regex
=>
qr{^(?:https?|file)://}
);
}
sub
lacks_uncapped_inputs {
my
$self
=
shift
;
my
$comment
=
shift
;
$comment
=
'All text inputs should have maxlength attributes'
unless
defined
(
$comment
);
local
$Test::Builder::Level
=
$Test::Builder::Level
+ 1;
my
@uncapped
;
my
@inputs
=
$self
->grep_inputs( {
type
=>
qr/^(?:text|password)$/
} );
foreach
my
$field
(
@inputs
) {
next
if
$field
->readonly();
next
if
$field
->disabled();
if
( not
defined
(
$field
->{maxlength}) ) {
push
(
@uncapped
,
$field
->name .
' has no maxlength attribute'
);
next
;
}
my
$val
=
$field
->{maxlength};
if
( (
$val
!~ /^\s*\d+\s*$/) || (
$val
+0 <= 0) ) {
push
(
@uncapped
,
$field
->name .
qq{ has an invalid maxlength attribute of "$val"}
);
}
}
my
$ok
=
$TB
->ok(
@uncapped
== 0,
$comment
);
$TB
->diag(
$_
)
for
@uncapped
;
return
$ok
;
}
sub
check_all_images_ok {
my
$self
=
shift
;
my
@args
=
@_
;
my
$comment
;
if
(
@args
% 2 ) {
$comment
=
pop
@args
;
}
$comment
=
'All images in the page should exist'
unless
defined
(
$comment
);
my
@not_ok
;
foreach
my
$img
(
map
{
$_
->URI }
$self
->find_all_images(
@args
) ) {
my
$abs
=
$img
->
abs
;
state
$head_cache
;
if
( !
$head_cache
->{
$abs
}++ ) {
my
$res
=
$self
->_make_request( HTTP::Request::Common::HEAD(
$abs
) );
if
( not
$res
->is_success ) {
push
(
@not_ok
,
$img
.
' returned code '
.
$res
->code );
}
}
}
my
$ok
=
$TB
->ok(
@not_ok
== 0,
$comment
);
$TB
->diag(
$_
)
for
@not_ok
;
return
$ok
;
}
sub
_diag_url {
my
$self
=
shift
;
my
$uri
=
$self
->uri;
return
$uri
?
$uri
->as_string :
'Unable to determine URL'
;
}
1;