Procházet zdrojové kódy

Merge pull request #179 from gempesaw/fix-179-locator-strategy-in-T-S-R-D

'find_element'-like functions in Test::Selenium::Remote::Driver should support a locator strategy as an argument
Daniel Gempesaw před 11 roky
rodič
revize
8d9fefdc58

+ 1 - 0
lib/Selenium/Remote/Driver.pm

@@ -36,6 +36,7 @@ use constant FINDERS => {
     xpath             => 'xpath',
 };
 
+
 =head1 SYNOPSIS
 
     use Selenium::Remote::Driver;

+ 0 - 5
lib/Selenium/Remote/Mock/RemoteConnection.pm

@@ -147,10 +147,6 @@ sub request {
     my $ret = { cmd_status => 'OK', cmd_return => 1 };
     if ( defined( $spec->{$cmd} ) ) {
         my $return_sub = $spec->{$cmd};
-        if ($no_content_success) {
-            $ret->{cmd_return} = 1;
-        }
-        else {
             my $mock_return = $return_sub->( $url_params, $params );
             if ( ref($mock_return) eq 'HASH' ) {
                 $ret->{cmd_status} = $mock_return->{status};
@@ -160,7 +156,6 @@ sub request {
             else {
                 $ret = $mock_return;
             }
-        }
         $ret->{session_id} = $self->fake_session_id if ( ref($ret) eq 'HASH' );
     }
     else {

+ 158 - 121
lib/Test/Selenium/Remote/Driver.pm

@@ -33,7 +33,7 @@ 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_child_element_ok', 'find_child_elements_ok', 'find_no_element_ok',
             'compare_elements_ok', 'click_ok', 'double_click_ok',
             'body_like',
         ];
@@ -44,8 +44,12 @@ sub has_args {
     my $self          = shift;
     my $fun_name      = shift;
     my $hash_fun_args = {
-        'find_element'     => 1,
-        'find_elements'     => 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,
     };
@@ -235,66 +239,173 @@ more documentation, see the related test methods in L<Selenium::Remote::Driver>
 
     click_ok
     double_click_ok
+=cut
+
 
-=head2 $twd->type_element_ok($search_target, $keys, [, $desc ]);
+# function composing a find_element with locator with a webelement test
 
-   $twd->type_element_ok( $search_target, $keys [, $desc ] );
+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});
+    }
+    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->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->default_finder;
+                }
+            }
+            else { 
+                $self->croak('Not enough arguments');
+            }
+        }
+    }
+    unless ($desc) {
+        $desc = $method;
+        $desc .= "'" . join( " ", ($params // '') ) . "'";
+    }
+    return $self->find_element($locator,$locator_strategy)->$method( $params, $desc );
+}
+
+
+=head2 $twd->type_element_ok($search_target [,$locator], $keys, [, $desc ]);
+
+   $twd->type_element_ok( $search_target [,$locator], $keys [, $desc ] );
 
 Use L<Selenium::Remote::Driver/find_element> to resolve the C<$search_target>
-to a web element, and then type C<$keys> into it, providing an optional test
+to a web element and an optional locator, and then type C<$keys> into it, providing an optional test
 label.
 
-Currently, other finders besides the default are not supported for C<type_ok()>.
 
 =cut
 
 sub type_element_ok {
     my $self    = shift;
-    my $locator = shift;
-    my $keys    = shift;
-    my $desc    = shift;
-    return $self->find_element($locator)->send_keys_ok( $keys, $desc );
+    my $method = 'send_keys_ok'; 
+    return $self->_find_element_with_action($method,@_);
+}
+
+
+=head2 $twd->element_text_is($search_target[,$finder],$expected_text [,$desc]);
+
+    $twd->element_text_is($search_target[,$finder],$expected_text [,$desc]);
+
+=cut
+
+sub element_text_is {
+    my $self = shift; 
+    my $method = 'text_is';
+    return $self->_find_element_with_action($method,@_);
 }
 
+=head2 $twd->element_value_is($search_target[,$finder],$expected_value [,$desc]);
 
-=head2 $twd->find_element_ok($search_target [, $desc ]);
+    $twd->element_value_is($search_target[,$finder],$expected_value [,$desc]);
+
+=cut
 
-   $twd->find_element_ok( $search_target [, $desc ] );
+sub element_value_is {
+    my $self = shift; 
+    my $method = 'value_is';
+    return $self->_find_element_with_action($method,@_);
+}
+
+=head2 $twd->click_element_ok($search_target [,$desc]);
+
+    $twd->click_element_ok($search_target [,$desc]);
+
+Find an element and then click on it.
+
+=cut
+
+sub click_element_ok {
+    my $self = shift; 
+    my $method = 'click_ok';
+    return $self->_find_element_with_action($method,@_);
+}
+
+=head2 $twd->clear_element_ok($search_target [,$desc]);
+
+    $twd->clear_element_ok($search_target [,$desc]);
+
+Find an element and then clear on it.
+
+=cut
+
+sub clear_element_ok {
+    my $self = shift; 
+    my $method = 'clear_ok';
+    return $self->_find_element_with_action($method,@_);
+}
+
+=head2 $twd->is_element_displayed_ok($search_target [,$desc]);
+
+    $twd->is_element_displayed_ok($search_target [,$desc]);
+
+Find an element and check to confirm that it is displayed. (visible)
+
+=cut
+
+sub is_element_displayed_ok {
+    my $self = shift; 
+    my $method = 'is_displayed_ok';
+    return $self->_find_element_with_action($method,@_);
+}
+
+=head2 $twd->is_element_enabled_ok($search_target [,$desc]);
+
+    $twd->is_element_enabled_ok($search_target [,$desc]);
+
+Find an element and check to confirm that it is enabled.
+
+=cut
+
+sub is_element_enabled_ok {
+    my $self = shift; 
+    my $method = 'is_enabled_ok';
+    return $self->_find_element_with_action($method,@_);
+}
+
+
+=head2 $twd->find_element_ok($search_target [,$finder, $desc ]);
+
+   $twd->find_element_ok( $search_target [,$finder, $desc ] );
 
 Returns true if C<$search_target> is successfully found on the page. L<$search_target>
-is passed to L<Selenium::Remote::Driver/find_element> using the C<default_finder>. See
-there for more details on the format. Currently, other finders besides the default are not supported
-for C<find_element_ok()>.
+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 [, $desc ]);
+=head2 $twd->find_no_element_ok($search_target [,$finder, $desc ]);
 
-   $twd->find_no_element_ok( $search_target [, $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>
-is passed to L<Selenium::Remote::Driver/find_element> using the C<default_finder>. See
-there for more details on the format. Currently, other finders besides the default are not supported
+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()>.
 
 =cut
 
-sub find_no_element_ok {
-    my $self          = shift;
-    my $search_target = shift;
-    my $desc          = shift;
-    my $rv = 0 ;
-    local $Test::Builder::Level = $Test::Builder::Level + 1;
-    try {
-        $self->find_element($search_target)
-    } catch {
-        $rv = 1 if ($_);
-    };
-    return $self->ok($rv == 1,$desc);
-}
 
 =head2 $twd->content_like( $regex [, $desc ] )
 
@@ -349,18 +460,17 @@ sub content_unlike {
     my $self  = shift;
     my $regex = shift;
     my $desc  = shift;
-
     local $Test::Builder::Level = $Test::Builder::Level + 1;
 
     my $content = $self->get_page_source();
 
     if ( not ref $regex eq 'ARRAY' ) {
-        my $desc = qq{Content is unlike "$regex"} if ( not defined $desc );
+        $desc = qq{Content is unlike "$regex"} if ( not defined $desc );
         return unlike_string( $content, $regex, $desc );
     }
     elsif ( ref $regex eq 'ARRAY' ) {
         for my $re (@$regex) {
-            my $desc = qq{Content is unlike "$re"} if ( not defined $desc );
+            $desc = qq{Content is unlike "$re"} if ( not defined $desc );
             unlike_string( $content, $re, $desc );
         }
     }
@@ -393,12 +503,12 @@ sub body_text_like {
     my $text = $self->get_body();
 
     if ( not ref $regex eq 'ARRAY' ) {
-        my $desc = qq{Text is like "$regex"} if ( not defined $desc );
+        $desc = qq{Text is like "$regex"} if ( not defined $desc );
         return like_string( $text, $regex, $desc );
     }
     elsif ( ref $regex eq 'ARRAY' ) {
         for my $re (@$regex) {
-            my $desc = qq{Text is like "$re"} if ( not defined $desc );
+            $desc = qq{Text is like "$re"} if ( not defined $desc );
             like_string( $text, $re, $desc );
         }
     }
@@ -431,12 +541,12 @@ sub body_text_unlike {
     my $text = $self->get_body();
 
     if ( not ref $regex eq 'ARRAY' ) {
-        my $desc = qq{Text is unlike "$regex"} if ( not defined $desc );
+        $desc = qq{Text is unlike "$regex"} if ( not defined $desc );
         return unlike_string( $text, $regex, $desc );
     }
     elsif ( ref $regex eq 'ARRAY' ) {
         for my $re (@$regex) {
-            my $desc = qq{Text is unlike "$re"} if ( not defined $desc );
+            $desc = qq{Text is unlike "$re"} if ( not defined $desc );
             unlike_string( $text, $re, $desc );
         }
     }
@@ -468,12 +578,12 @@ sub content_contains {
     my $content = $self->get_page_source();
 
     if ( not ref $str eq 'ARRAY' ) {
-        my $desc = qq{Content contains "$str"} if ( not defined $desc );
+        $desc = qq{Content contains "$str"} if ( not defined $desc );
         return contains_string( $content, $str, $desc );
     }
     elsif ( ref $str eq 'ARRAY' ) {
         for my $s (@$str) {
-            my $desc = qq{Content contains "$s"} if ( not defined $desc );
+            $desc = qq{Content contains "$s"} if ( not defined $desc );
             contains_string( $content, $s, $desc );
         }
     }
@@ -503,12 +613,12 @@ sub content_lacks {
     my $content = $self->get_page_source();
 
     if ( not ref $str eq 'ARRAY' ) {
-        my $desc = qq{Content lacks "$str"} if ( not defined $desc );
+        $desc = qq{Content lacks "$str"} if ( not defined $desc );
         return lacks_string( $content, $str, $desc );
     }
     elsif ( ref $str eq 'ARRAY' ) {
         for my $s (@$str) {
-            my $desc = qq{Content lacks "$s"} if ( not defined $desc );
+            $desc = qq{Content lacks "$s"} if ( not defined $desc );
             lacks_string( $content, $s, $desc );
         }
     }
@@ -541,12 +651,12 @@ sub body_text_contains {
     my $text = $self->get_body();
 
     if ( not ref $str eq 'ARRAY' ) {
-        my $desc = qq{Text contains "$str"} if ( not defined $desc );
+        $desc = qq{Text contains "$str"} if ( not defined $desc );
         return contains_string( $text, $str, $desc );
     }
     elsif ( ref $str eq 'ARRAY' ) {
         for my $s (@$str) {
-            my $desc = qq{Text contains "$s"} if ( not defined $desc );
+            $desc = qq{Text contains "$s"} if ( not defined $desc );
             contains_string( $text, $s, $desc );
         }
     }
@@ -579,90 +689,17 @@ sub body_text_lacks {
     my $text = $self->get_body();
 
     if ( not ref $str eq 'ARRAY' ) {
-        my $desc = qq{Text is lacks "$str"} if ( not defined $desc );
+        $desc = qq{Text is lacks "$str"} if ( not defined $desc );
         return lacks_string( $text, $str, $desc );
     }
     elsif ( ref $str eq 'ARRAY' ) {
         for my $s (@$str) {
-            my $desc = qq{Text is lacks "$s"} if ( not defined $desc );
+            $desc = qq{Text is lacks "$s"} if ( not defined $desc );
             lacks_string( $text, $s, $desc );
         }
     }
 }
 
-=head2 $twd->element_text_is($search_target,$expected_text [,$desc]);
-
-    $twd->element_text_is($search_target,$expected_text [,$desc]);
-
-=cut
-
-sub element_text_is {
-    my ( $self, $search_target, $expected, $desc ) = @_;
-    return $self->find_element($search_target)->text_is( $expected, $desc );
-}
-
-=head2 $twd->element_value_is($search_target,$expected_value [,$desc]);
-
-    $twd->element_value_is($search_target,$expected_value [,$desc]);
-
-=cut
-
-sub element_value_is {
-    my ( $self, $search_target, $expected, $desc ) = @_;
-    return $self->find_element($search_target)->value_is( $expected, $desc );
-}
-
-=head2 $twd->click_element_ok($search_target [,$desc]);
-
-    $twd->click_element_ok($search_target [,$desc]);
-
-Find an element and then click on it.
-
-=cut
-
-sub click_element_ok {
-    my ( $self, $search_target, $desc ) = @_;
-    return $self->find_element($search_target)->click_ok($desc);
-}
-
-=head2 $twd->clear_element_ok($search_target [,$desc]);
-
-    $twd->clear_element_ok($search_target [,$desc]);
-
-Find an element and then clear on it.
-
-=cut
-
-sub clear_element_ok {
-    my ( $self, $search_target, $desc ) = @_;
-    return $self->find_element($search_target)->clear_ok($desc);
-}
-
-=head2 $twd->is_element_displayed_ok($search_target [,$desc]);
-
-    $twd->is_element_displayed_ok($search_target [,$desc]);
-
-Find an element and check to confirm that it is displayed. (visible)
-
-=cut
-
-sub is_element_displayed_ok {
-    my ( $self, $search_target, $desc ) = @_;
-    return $self->find_element($search_target)->is_displayed_ok($desc);
-}
-
-=head2 $twd->is_element_enabled_ok($search_target [,$desc]);
-
-    $twd->is_element_enabled_ok($search_target [,$desc]);
-
-Find an element and check to confirm that it is enabled.
-
-=cut
-
-sub is_element_enabled_ok {
-    my ( $self, $search_target, $desc ) = @_;
-    return $self->find_element($search_target)->is_enabled_ok($desc);
-}
 
 1;
 

+ 33 - 1
lib/Test/Selenium/Remote/Role/DoesTesting.pm

@@ -5,6 +5,7 @@ package Test::Selenium::Remote::Role::DoesTesting;
 use Moo::Role;
 use Test::Builder;
 use Try::Tiny;
+use List::MoreUtils qw/any/;
 use namespace::clean;
 
 requires qw(func_list has_args);
@@ -38,19 +39,50 @@ sub _check_method {
 }
 
 # main method for _ok tests
+# a bit hacked so that find_no_element_ok can also be processed
 
 sub _check_ok {
     my $self   = shift;
     my $method = shift;
+    my $real_method = '';
     my @args   = @_;
     my ($rv, $num_of_args, @r_args);
     try {
         $num_of_args = $self->has_args($method);
         @r_args = splice( @args, 0, $num_of_args );
+        if ($method =~ m/^find(_no|_child)?_element/) { 
+            # case find_element_ok was called with no arguments    
+            if (scalar(@r_args) - $num_of_args == 1) { 
+                push @r_args, $self->default_finder; 
+            }
+            else { 
+                if (scalar(@r_args) == $num_of_args) {
+                    # case find_element was called with no finder but
+                    # a test description
+                    my $finder = $r_args[$num_of_args - 1]; 
+                    my @FINDERS = keys (%{$self->FINDERS});
+                    unless ( any { $finder eq $_ } @FINDERS) { 
+                        $r_args[$num_of_args - 1] = $self->default_finder; 
+                        push @args, $finder; 
+                    }
+                }
+            }
+        }
+        # quick hack to fit 'find_no_element' into check_ok logic
+        if ($method eq 'find_no_element') { 
+            $real_method = $method;
+            $method = 'find_element'; 
+        }
         $rv = $self->$method(@r_args);
     }
     catch {
-        $self->croak($_);
+        if ($real_method) {
+            $method = $real_method;
+            $rv     = 1;
+        }
+        else {
+            $self->croak($_);
+        }
     };
 
     my $default_test_name = $method;

+ 111 - 14
t/Test-Selenium-Remote-Driver.t

@@ -6,21 +6,86 @@ use Selenium::Remote::WebElement;
 use Selenium::Remote::Mock::Commands;
 use Selenium::Remote::Mock::RemoteConnection;
 
+my $find_element = sub {
+    my ( undef, $searched_item ) = @_;
+    if ( $searched_item->{value} eq 'q' ) {
+        return { status => 'OK', return => { ELEMENT => '123456' } };
+    }
+    if (   $searched_item->{value} eq 'p'
+        && $searched_item->{using} eq 'class name' )
+    {
+        return { status => 'OK', return => { ELEMENT => '123457' } };
+    }
+    if ( $searched_item->{value} eq '//body' && $searched_item->{using} eq 'xpath') { 
+        return { status => 'OK', return => { ELEMENT => '123458' } };
+    }
+    return { status => 'NOK', return => 0, error => 'element not found' };
+};
+my $find_child_element = sub {
+    my ( $session_object, $searched_item ) = @_;
+    my $elem_id = $session_object->{id};
+    if ( $elem_id == 1 && $searched_item->{value} eq 'p' ) {
+        if ( $searched_item->{using} eq 'class name' ) {
+            return { status => 'OK', return => { ELEMENT => '11223344' } };
+        }
+
+        if ( $searched_item->{using} eq 'xpath' ) {
+            return { status => 'OK',
+                return => [ { ELEMENT => '112' }, { ELEMENT => '113' } ] };
+        }
+    }
+
+    return {
+        status => 'NOK', return => 0,
+        error  => 'child element not found'
+    };
+};
+
+my $find_elements = sub {
+    my ( undef, $searched_expr ) = @_;
+    if (   $searched_expr->{value} eq 'abc'
+        && $searched_expr->{using} eq 'xpath' )
+    {
+        return { status => 'OK',
+            return => [ { ELEMENT => '123456' }, { ELEMENT => '12341234' } ] };
+    }
+};
+
+my $send_keys = sub {
+    my ( $session_object, $val ) = @_;
+    my $keys = shift @{ $val->{value} };
+    return { status => 'OK', return => 1 } if ( $keys =~ /abc|def/ );
+    return { status => 'NOK', return => 0, error => 'cannot send keys' };
+};
+
+my $get_text = sub { 
+    my ($session_object) =@_; 
+    return 'abc' if ($session_object->{id} eq '123456');
+    return 'def' if ($session_object->{id} eq '123457');
+    return 'this output matches' if ($session_object->{id} eq '123458');
+    return;
+};
+
+my $get_attr = sub { 
+    my ($session_object) = @_;
+    return 'foo';
+};
+
 my $spec = {
-    findElement => sub {
-        my (undef,$searched_item) = @_;
-        return { status => 'OK', return => { ELEMENT => '123456' } }
-          if ( $searched_item->{value} eq 'q' );
-        return { status => 'NOK', return => 0, error => 'element not found' };
-    },
+    findElement => $find_element,
+    findChildElement => $find_child_element,
     getPageSource => sub { return 'this output matches regex'},
-    findElements => sub { 
-        my (undef,$searched_expr) = @_;
-        if ($searched_expr) { 
-            return { status => 'OK', return => [ { ELEMENT => '123456' }, { ELEMENT => '12341234' } ] }
-        }
-    },
+    findElements => $find_elements,
+    findChildElements => $find_child_element,
+    getElementText => $get_text,
+    sendKeysToElement => $send_keys,
+    getElementAttribute => $get_attr, 
+    clickElement => sub { return { status => 'OK', return => 1 }; }, 
+    clearElement =>  sub { return { status => 'OK', return => 1 }; }, 
+    isElementDisplayed =>  sub { return { status => 'OK', return => 1 }; }, 
+    isElementEnabled =>  sub { return { status => 'OK', return => 1 }; }, 
 };
+
 my $mock_commands = Selenium::Remote::Mock::Commands->new;
 
 my $successful_driver =
@@ -28,11 +93,43 @@ my $successful_driver =
     remote_conn => Selenium::Remote::Mock::RemoteConnection->new( spec => $spec, mock_cmds => $mock_commands ),
     commands => $mock_commands,
 );
+
+# find element ok tests 
 $successful_driver->find_element_ok('q','find_element_ok works');
+$successful_driver->find_element_ok('p','class','find_element_ok with a locator works');
 dies_ok { $successful_driver->find_element_ok('notq') } 'find_element_ok dies if element not found';
-$successful_driver->find_no_element_ok('notq','find_no_element_ok works');
+$successful_driver->find_elements_ok('abc','find_elements_ok works');
+
+# find child element ok tests 
+$successful_driver->find_child_elements_ok({id => 1},'p','find_child_elements_ok works');
+$successful_driver->find_child_element_ok({id => 1},'p','class','find_child_element_ok with a locator works');
+dies_ok{ $successful_driver->find_child_element_ok({id => 1200}) } 'find_child_element_ok dies if the element is not found';
+
+# find no element ok test
+
+$successful_driver->find_no_element_ok('notq','xpath','find_no_element_ok works');
+
+# body and content function family 
 $successful_driver->content_like( qr/matches/, 'content_like works');
 $successful_driver->content_unlike( qr/nomatch/, 'content_unlike works');
-$successful_driver->find_elements_ok('abc','find_elements_ok works');
+$successful_driver->content_contains( 'matches', 'content_contains works');
+$successful_driver->content_lacks( 'nomatch', 'content_lacks works');
+$successful_driver->body_text_contains( ['match','output'], 'body_text_contains works');
+$successful_driver->body_text_lacks( 'nomatch', 'body_text_lacks works');
+$successful_driver->body_text_like( qr/this/, 'body_text_like works');
+$successful_driver->body_text_unlike( qr/notthis/, 'body_text_unlike works');
+
+$successful_driver->type_element_ok('q','abc');
+$successful_driver->type_element_ok('p','class','def','type_element_ok works with a locator');
+
+$successful_driver->element_text_is('q','abc','element has a correct text');
+$successful_driver->element_text_is('p','class','def');
+
+$successful_driver->element_value_is('p','class','foo');
+$successful_driver->click_element_ok('p','class','click_element_ok works');
+$successful_driver->clear_element_ok('q','element is cleared ok');
+$successful_driver->is_element_enabled_ok('p','class','element is enabled');
+$successful_driver->is_element_displayed_ok('q','element is displayed');
+
 
 done_testing();