|
|
@@ -33,7 +33,8 @@ has func_list => (
|
|
|
'send_modifier_ok', 'accept_alert_ok', 'dismiss_alert_ok',
|
|
|
'get_ok', 'go_back_ok', 'go_forward_ok', 'add_cookie_ok',
|
|
|
'get_page_source_ok', 'find_element_ok', 'find_elements_ok',
|
|
|
- 'find_child_element_ok', 'find_child_elements_ok', 'find_no_element_ok',
|
|
|
+ 'find_child_element_ok', 'find_child_elements_ok',
|
|
|
+ 'find_no_element_ok',
|
|
|
'compare_elements_ok', 'click_ok', 'double_click_ok',
|
|
|
'body_like',
|
|
|
];
|
|
|
@@ -44,14 +45,14 @@ sub has_args {
|
|
|
my $self = shift;
|
|
|
my $fun_name = shift;
|
|
|
my $hash_fun_args = {
|
|
|
- 'find_element' => 2,
|
|
|
- 'find_no_element' => 2,
|
|
|
- 'find_child_element' => 3,
|
|
|
- 'find_child_elements' => 3,
|
|
|
- 'find_element' => 2,
|
|
|
- 'find_elements' => 2,
|
|
|
- 'compare_elements' => 2,
|
|
|
- 'get' => 1,
|
|
|
+ 'find_element' => 2,
|
|
|
+ 'find_no_element' => 2,
|
|
|
+ 'find_child_element' => 3,
|
|
|
+ 'find_child_elements' => 3,
|
|
|
+ 'find_element' => 2,
|
|
|
+ 'find_elements' => 2,
|
|
|
+ 'compare_elements' => 2,
|
|
|
+ 'get' => 1,
|
|
|
};
|
|
|
return ( $hash_fun_args->{$fun_name} // 0 );
|
|
|
}
|
|
|
@@ -62,13 +63,6 @@ has verbose => (
|
|
|
is => 'rw',
|
|
|
);
|
|
|
|
|
|
-has error_callback => (
|
|
|
- is => 'rw',
|
|
|
- default => sub {
|
|
|
- sub { }
|
|
|
- },
|
|
|
-);
|
|
|
-
|
|
|
|
|
|
sub BUILD {
|
|
|
my $self = shift;
|
|
|
@@ -158,6 +152,56 @@ sub server_is_running {
|
|
|
|
|
|
}
|
|
|
|
|
|
+=head2 error_handler
|
|
|
+
|
|
|
+As for L<Selenium::Remote::Driver>, this class also supports adding an
|
|
|
+optional C<error_handler> attribute during instantiation :
|
|
|
+
|
|
|
+ my $test_driver = Test::Selenium::Remote::Driver->new(
|
|
|
+ error_handler => sub { print $_[1]; croak 'goodbye'; }
|
|
|
+ );
|
|
|
+
|
|
|
+Additionally, you can set and/or clear it at any time on an
|
|
|
+already-instantiated driver:
|
|
|
+
|
|
|
+ # later, change the error handler to something else
|
|
|
+ $driver->error_handler( sub { print $_[1]; croak 'hello'; } );
|
|
|
+
|
|
|
+ # stop handling errors manually and use the default S:R:D behavior
|
|
|
+ # (we will croak about the exception)
|
|
|
+ $driver->clear_error_handler;
|
|
|
+
|
|
|
+Your error handler will receive two arguments,
|
|
|
+The first argument is the C<$driver> object itself.
|
|
|
+Due to some specificities of this class, the second argument passed to the
|
|
|
+handler can be:
|
|
|
+
|
|
|
+=over
|
|
|
+
|
|
|
+=item the error message from the Webdriver
|
|
|
+
|
|
|
+This is the case when the error message is raised by a WebDriver failure
|
|
|
+
|
|
|
+=item "Failed to find ..."
|
|
|
+
|
|
|
+This message is raised when the Webdriver call is successful but the failure
|
|
|
+occurs on the test performed aftwerwards. This is the case for functions like
|
|
|
+C<body_text_like>, C<body_text_unlike>, C<body_text_contains>, C<body_text_lacks>,
|
|
|
+C<content_like>, C<content_unlike>, C<content_contains>, C<content_lacks>.
|
|
|
+
|
|
|
+=back
|
|
|
+
|
|
|
+If you set your own handler, you should not rely that much on the message returned.
|
|
|
+You should also remember that you are entirely responsible for handling exceptions,
|
|
|
+which means that should the error handler be called, it means that the test you are
|
|
|
+doing has failed, so you should croak.
|
|
|
+
|
|
|
+You should also call fail() in your handler, in case the function called raised a
|
|
|
+webdriver error, because, as exceptions are not caught anymore when you specify a
|
|
|
+handler, the function will not fail anymore, which translates to a 'ok' in your TAP
|
|
|
+output if you do not handle it properly.
|
|
|
+
|
|
|
+
|
|
|
=head1 Testing Methods
|
|
|
|
|
|
The following testing methods are available. For
|
|
|
@@ -239,46 +283,53 @@ more documentation, see the related test methods in L<Selenium::Remote::Driver>
|
|
|
|
|
|
click_ok
|
|
|
double_click_ok
|
|
|
+
|
|
|
=cut
|
|
|
|
|
|
|
|
|
# function composing a find_element with locator with a webelement test
|
|
|
|
|
|
-sub _find_element_with_action {
|
|
|
- my $self = shift;
|
|
|
+sub _find_element_with_action {
|
|
|
+ my $self = shift;
|
|
|
my $method = shift;
|
|
|
- my ($locator,$locator_strategy,$params,$desc) = @_;
|
|
|
- # case 4 args
|
|
|
- if ($desc) {
|
|
|
- $self->croak('Invalid locator strategy') unless ($self->FINDERS->{$locator_strategy});
|
|
|
+ my ( $locator, $locator_strategy, $params, $desc ) = @_;
|
|
|
+
|
|
|
+ # case 4 args
|
|
|
+ if ($desc) {
|
|
|
+ $self->croak('Invalid locator strategy')
|
|
|
+ unless ( $self->FINDERS->{$locator_strategy} );
|
|
|
}
|
|
|
- else {
|
|
|
- if ($params) {
|
|
|
+ else {
|
|
|
+ if ($params) {
|
|
|
+
|
|
|
# means that we called it the 'old way' (no locator strategy)
|
|
|
- if (!defined($self->FINDERS->{$locator_strategy})) {
|
|
|
- $desc = $params;
|
|
|
- $params = $locator_strategy;
|
|
|
- $locator_strategy = $self->_get_finder_key($self->default_finder);
|
|
|
+ if ( !defined( $self->FINDERS->{$locator_strategy} ) ) {
|
|
|
+ $desc = $params;
|
|
|
+ $params = $locator_strategy;
|
|
|
+ $locator_strategy =
|
|
|
+ $self->_get_finder_key( $self->default_finder );
|
|
|
}
|
|
|
}
|
|
|
- else {
|
|
|
- # means it was called with no locator strategy and no desc
|
|
|
- if ($locator_strategy) {
|
|
|
- if (!defined($self->FINDERS->{$locator_strategy})) {
|
|
|
- $params = $locator_strategy;
|
|
|
- $locator_strategy = $self->_get_finder_key($self->default_finder);
|
|
|
+ else {
|
|
|
+ # means it was called with no locator strategy and no desc
|
|
|
+ if ($locator_strategy) {
|
|
|
+ if ( !defined( $self->FINDERS->{$locator_strategy} ) ) {
|
|
|
+ $params = $locator_strategy;
|
|
|
+ $locator_strategy =
|
|
|
+ $self->_get_finder_key( $self->default_finder );
|
|
|
}
|
|
|
}
|
|
|
- else {
|
|
|
+ else {
|
|
|
$self->croak('Not enough arguments');
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
unless ($desc) {
|
|
|
$desc = $method;
|
|
|
- $desc .= "'" . join( " ", ($params // '') ) . "'";
|
|
|
+ $desc .= "'" . join( " ", ( $params // '' ) ) . "'";
|
|
|
}
|
|
|
- return $self->find_element($locator,$locator_strategy)->$method( $params, $desc );
|
|
|
+ return $self->find_element( $locator, $locator_strategy )
|
|
|
+ ->$method( $params, $desc );
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -294,9 +345,9 @@ label.
|
|
|
=cut
|
|
|
|
|
|
sub type_element_ok {
|
|
|
- my $self = shift;
|
|
|
- my $method = 'send_keys_ok';
|
|
|
- return $self->_find_element_with_action($method,@_);
|
|
|
+ my $self = shift;
|
|
|
+ my $method = 'send_keys_ok';
|
|
|
+ return $self->_find_element_with_action( $method, @_ );
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -307,9 +358,9 @@ sub type_element_ok {
|
|
|
=cut
|
|
|
|
|
|
sub element_text_is {
|
|
|
- my $self = shift;
|
|
|
+ my $self = shift;
|
|
|
my $method = 'text_is';
|
|
|
- return $self->_find_element_with_action($method,@_);
|
|
|
+ return $self->_find_element_with_action( $method, @_ );
|
|
|
}
|
|
|
|
|
|
=head2 $twd->element_value_is($search_target[,$finder],$expected_value [,$desc]);
|
|
|
@@ -319,9 +370,9 @@ sub element_text_is {
|
|
|
=cut
|
|
|
|
|
|
sub element_value_is {
|
|
|
- my $self = shift;
|
|
|
+ my $self = shift;
|
|
|
my $method = 'value_is';
|
|
|
- return $self->_find_element_with_action($method,@_);
|
|
|
+ return $self->_find_element_with_action( $method, @_ );
|
|
|
}
|
|
|
|
|
|
=head2 $twd->click_element_ok($search_target [,$desc]);
|
|
|
@@ -333,9 +384,9 @@ Find an element and then click on it.
|
|
|
=cut
|
|
|
|
|
|
sub click_element_ok {
|
|
|
- my $self = shift;
|
|
|
+ my $self = shift;
|
|
|
my $method = 'click_ok';
|
|
|
- return $self->_find_element_with_action($method,@_);
|
|
|
+ return $self->_find_element_with_action( $method, @_ );
|
|
|
}
|
|
|
|
|
|
=head2 $twd->clear_element_ok($search_target [,$desc]);
|
|
|
@@ -347,9 +398,9 @@ Find an element and then clear on it.
|
|
|
=cut
|
|
|
|
|
|
sub clear_element_ok {
|
|
|
- my $self = shift;
|
|
|
+ my $self = shift;
|
|
|
my $method = 'clear_ok';
|
|
|
- return $self->_find_element_with_action($method,@_);
|
|
|
+ return $self->_find_element_with_action( $method, @_ );
|
|
|
}
|
|
|
|
|
|
=head2 $twd->is_element_displayed_ok($search_target [,$desc]);
|
|
|
@@ -361,9 +412,9 @@ Find an element and check to confirm that it is displayed. (visible)
|
|
|
=cut
|
|
|
|
|
|
sub is_element_displayed_ok {
|
|
|
- my $self = shift;
|
|
|
+ my $self = shift;
|
|
|
my $method = 'is_displayed_ok';
|
|
|
- return $self->_find_element_with_action($method,@_);
|
|
|
+ return $self->_find_element_with_action( $method, @_ );
|
|
|
}
|
|
|
|
|
|
=head2 $twd->is_element_enabled_ok($search_target [,$desc]);
|
|
|
@@ -375,9 +426,9 @@ Find an element and check to confirm that it is enabled.
|
|
|
=cut
|
|
|
|
|
|
sub is_element_enabled_ok {
|
|
|
- my $self = shift;
|
|
|
+ my $self = shift;
|
|
|
my $method = 'is_enabled_ok';
|
|
|
- return $self->_find_element_with_action($method,@_);
|
|
|
+ return $self->_find_element_with_action( $method, @_ );
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -385,21 +436,19 @@ sub is_element_enabled_ok {
|
|
|
|
|
|
$twd->find_element_ok( $search_target [,$finder, $desc ] );
|
|
|
|
|
|
-Returns true if C<$search_target> is successfully found on the page. L<$search_target>
|
|
|
+Returns true if C<$search_target> is successfully found on the page. C<$search_target>
|
|
|
is passed to L<Selenium::Remote::Driver/find_element> using a finder or the C<default_finder>
|
|
|
if none passed.
|
|
|
See there for more details on the format for C<find_element_ok()>.
|
|
|
|
|
|
=cut
|
|
|
|
|
|
-# Eventually, it would be nice to support other finds like Test::WWW::Selenium does, like this:
|
|
|
-# 'xpath=//foo', or 'css=.foo', etc.
|
|
|
|
|
|
=head2 $twd->find_no_element_ok($search_target [,$finder, $desc ]);
|
|
|
|
|
|
$twd->find_no_element_ok( $search_target [,$finder, $desc ] );
|
|
|
|
|
|
-Returns true if C<$search_target> is I<not> found on the page. L<$search_target>
|
|
|
+Returns true if C<$search_target> is I<not> found on the page. C<$search_target>
|
|
|
is passed to L<Selenium::Remote::Driver/find_element> using a finder or the
|
|
|
C<default_finder> if none passed.See there for more details on the format.
|
|
|
for C<find_no_element_ok()>.
|
|
|
@@ -429,15 +478,23 @@ sub content_like {
|
|
|
local $Test::Builder::Level = $Test::Builder::Level + 1;
|
|
|
|
|
|
my $content = $self->get_page_source();
|
|
|
+ my $ret;
|
|
|
|
|
|
if ( not ref $regex eq 'ARRAY' ) {
|
|
|
$desc = qq{Content is like "$regex"} if ( not defined $desc );
|
|
|
- return like_string( $content, $regex, $desc );
|
|
|
+ $ret = like_string( $content, $regex, $desc );
|
|
|
+ if ( !$ret ) {
|
|
|
+ $self->error_handler->($self,"Failed to find $regex");
|
|
|
+ }
|
|
|
+ return $ret;
|
|
|
}
|
|
|
elsif ( ref $regex eq 'ARRAY' ) {
|
|
|
for my $re (@$regex) {
|
|
|
$desc = qq{Content is like "$re"} if ( not defined $desc );
|
|
|
- like_string( $content, $re, $desc );
|
|
|
+ $ret = like_string( $content, $re, $desc );
|
|
|
+ if ( !$ret ) {
|
|
|
+ $self->error_handler->($self,"Failed to find $re");
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -463,15 +520,22 @@ sub content_unlike {
|
|
|
local $Test::Builder::Level = $Test::Builder::Level + 1;
|
|
|
|
|
|
my $content = $self->get_page_source();
|
|
|
+ my $ret;
|
|
|
|
|
|
if ( not ref $regex eq 'ARRAY' ) {
|
|
|
$desc = qq{Content is unlike "$regex"} if ( not defined $desc );
|
|
|
- return unlike_string( $content, $regex, $desc );
|
|
|
+ $ret = unlike_string( $content, $regex, $desc );
|
|
|
+ if ( !$ret ) {
|
|
|
+ $self->error_handler->($self,"Failed to find $regex");
|
|
|
+ }
|
|
|
}
|
|
|
elsif ( ref $regex eq 'ARRAY' ) {
|
|
|
for my $re (@$regex) {
|
|
|
$desc = qq{Content is unlike "$re"} if ( not defined $desc );
|
|
|
- unlike_string( $content, $re, $desc );
|
|
|
+ $ret = unlike_string( $content, $re, $desc );
|
|
|
+ if ( !$ret ) {
|
|
|
+ $self->error_handler->($self,"Failed to find $re");
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -501,15 +565,23 @@ sub body_text_like {
|
|
|
local $Test::Builder::Level = $Test::Builder::Level + 1;
|
|
|
|
|
|
my $text = $self->get_body();
|
|
|
+ my $ret;
|
|
|
|
|
|
if ( not ref $regex eq 'ARRAY' ) {
|
|
|
$desc = qq{Text is like "$regex"} if ( not defined $desc );
|
|
|
- return like_string( $text, $regex, $desc );
|
|
|
+ $ret = like_string( $text, $regex, $desc );
|
|
|
+ if ( !$ret ) {
|
|
|
+ $self->error_handler->($self,"Failed to find $regex");
|
|
|
+ }
|
|
|
+ return $ret;
|
|
|
}
|
|
|
elsif ( ref $regex eq 'ARRAY' ) {
|
|
|
for my $re (@$regex) {
|
|
|
$desc = qq{Text is like "$re"} if ( not defined $desc );
|
|
|
- like_string( $text, $re, $desc );
|
|
|
+ $ret = like_string( $text, $re, $desc );
|
|
|
+ if ( !$ret ) {
|
|
|
+ $self->error_handler->($self,"Failed to find $re");
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -539,15 +611,24 @@ sub body_text_unlike {
|
|
|
local $Test::Builder::Level = $Test::Builder::Level + 1;
|
|
|
|
|
|
my $text = $self->get_body();
|
|
|
+ my $ret;
|
|
|
|
|
|
if ( not ref $regex eq 'ARRAY' ) {
|
|
|
$desc = qq{Text is unlike "$regex"} if ( not defined $desc );
|
|
|
- return unlike_string( $text, $regex, $desc );
|
|
|
+ $ret = unlike_string( $text, $regex, $desc );
|
|
|
+ if ( !$ret ) {
|
|
|
+ $self->error_handler->($self,"Failed to find $regex");
|
|
|
+ }
|
|
|
+ return $ret;
|
|
|
+
|
|
|
}
|
|
|
elsif ( ref $regex eq 'ARRAY' ) {
|
|
|
for my $re (@$regex) {
|
|
|
$desc = qq{Text is unlike "$re"} if ( not defined $desc );
|
|
|
- unlike_string( $text, $re, $desc );
|
|
|
+ $ret = unlike_string( $text, $re, $desc );
|
|
|
+ if ( !$ret ) {
|
|
|
+ $self->error_handler->($self,"Failed to find $re");
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -576,15 +657,24 @@ sub content_contains {
|
|
|
local $Test::Builder::Level = $Test::Builder::Level + 1;
|
|
|
|
|
|
my $content = $self->get_page_source();
|
|
|
+ my $ret;
|
|
|
|
|
|
if ( not ref $str eq 'ARRAY' ) {
|
|
|
$desc = qq{Content contains "$str"} if ( not defined $desc );
|
|
|
- return contains_string( $content, $str, $desc );
|
|
|
+ $ret = contains_string( $content, $str, $desc );
|
|
|
+ if ( !$ret ) {
|
|
|
+ $self->error_handler->($self,"Failed to find $str");
|
|
|
+ }
|
|
|
+ return $ret;
|
|
|
}
|
|
|
elsif ( ref $str eq 'ARRAY' ) {
|
|
|
for my $s (@$str) {
|
|
|
$desc = qq{Content contains "$s"} if ( not defined $desc );
|
|
|
- contains_string( $content, $s, $desc );
|
|
|
+ $ret = contains_string( $content, $s, $desc );
|
|
|
+
|
|
|
+ if ( !$ret ) {
|
|
|
+ $self->error_handler->($self,"Failed to find $s");
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -611,15 +701,23 @@ sub content_lacks {
|
|
|
local $Test::Builder::Level = $Test::Builder::Level + 1;
|
|
|
|
|
|
my $content = $self->get_page_source();
|
|
|
+ my $ret;
|
|
|
|
|
|
if ( not ref $str eq 'ARRAY' ) {
|
|
|
$desc = qq{Content lacks "$str"} if ( not defined $desc );
|
|
|
- return lacks_string( $content, $str, $desc );
|
|
|
+ $ret = lacks_string( $content, $str, $desc );
|
|
|
+ if ( !$ret ) {
|
|
|
+ $self->error_handler->($self,"Failed to find $str");
|
|
|
+ }
|
|
|
+ return $ret;
|
|
|
}
|
|
|
elsif ( ref $str eq 'ARRAY' ) {
|
|
|
for my $s (@$str) {
|
|
|
$desc = qq{Content lacks "$s"} if ( not defined $desc );
|
|
|
- lacks_string( $content, $s, $desc );
|
|
|
+ $ret = lacks_string( $content, $s, $desc );
|
|
|
+ if ( !$ret ) {
|
|
|
+ $self->error_handler->($self,"Failed to find $s");
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -649,15 +747,23 @@ sub body_text_contains {
|
|
|
local $Test::Builder::Level = $Test::Builder::Level + 1;
|
|
|
|
|
|
my $text = $self->get_body();
|
|
|
+ my $ret;
|
|
|
|
|
|
if ( not ref $str eq 'ARRAY' ) {
|
|
|
$desc = qq{Text contains "$str"} if ( not defined $desc );
|
|
|
- return contains_string( $text, $str, $desc );
|
|
|
+ $ret = contains_string( $text, $str, $desc );
|
|
|
+ if ( !$ret ) {
|
|
|
+ $self->error_handler->($self,"Failed to find $str");
|
|
|
+ }
|
|
|
+ return $ret;
|
|
|
}
|
|
|
elsif ( ref $str eq 'ARRAY' ) {
|
|
|
for my $s (@$str) {
|
|
|
$desc = qq{Text contains "$s"} if ( not defined $desc );
|
|
|
- contains_string( $text, $s, $desc );
|
|
|
+ $ret = contains_string( $text, $s, $desc );
|
|
|
+ if ( !$ret ) {
|
|
|
+ $self->error_handler->($self,"Failed to find $s");
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -672,7 +778,7 @@ Tells if the text of the page (as returned by C<< get_body() >>)
|
|
|
are provided, one 'test' is run for each regex against the content of the
|
|
|
current page.
|
|
|
|
|
|
-A default description of 'Text is lacks "$str"' will be provided if there
|
|
|
+A default description of 'Text lacks "$str"' will be provided if there
|
|
|
is no description.
|
|
|
|
|
|
To also match the HTML see, C<< content_lacks() >>.
|
|
|
@@ -687,15 +793,23 @@ sub body_text_lacks {
|
|
|
local $Test::Builder::Level = $Test::Builder::Level + 1;
|
|
|
|
|
|
my $text = $self->get_body();
|
|
|
+ my $ret;
|
|
|
|
|
|
if ( not ref $str eq 'ARRAY' ) {
|
|
|
- $desc = qq{Text is lacks "$str"} if ( not defined $desc );
|
|
|
- return lacks_string( $text, $str, $desc );
|
|
|
+ $desc = qq{Text lacks "$str"} if ( not defined $desc );
|
|
|
+ $ret = lacks_string( $text, $str, $desc );
|
|
|
+ if ( !$ret ) {
|
|
|
+ $self->error_handler->($self,"Failed to find $str");
|
|
|
+ }
|
|
|
+ return $ret;
|
|
|
}
|
|
|
elsif ( ref $str eq 'ARRAY' ) {
|
|
|
for my $s (@$str) {
|
|
|
- $desc = qq{Text is lacks "$s"} if ( not defined $desc );
|
|
|
- lacks_string( $text, $s, $desc );
|
|
|
+ $desc = qq{Text lacks "$s"} if ( not defined $desc );
|
|
|
+ $ret = lacks_string( $text, $s, $desc );
|
|
|
+ if ( !$ret ) {
|
|
|
+ $self->error_handler->($self,"Failed to find $s");
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|