Pārlūkot izejas kodu

Handle spec-appropriate element id extraction

The spec has a different key name in the response to find_element for
the JSON element object: https://www.w3.org/TR/webdriver/#elements

We want to handle both cases, because the SRD driver jar still uses the
old format, but in theory in the future all of the drivers and the SRD
shoul conform to the spec.
Daniel Gempesaw 9 gadi atpakaļ
vecāks
revīzija
67eff3da50

+ 23 - 12
lib/Selenium/Remote/Driver.pm

@@ -1417,8 +1417,10 @@ sub execute_async_script {
             and ( ref($ret) eq 'HASH' )
             and exists $ret->{'ELEMENT'} )
         {
-            $ret = $self->webelement_class->new( id => $ret->{ELEMENT},
-                driver => $self );
+            $ret = $self->webelement_class->new(
+                id => $ret,
+                driver => $self
+            );
         }
         return $ret;
     }
@@ -1494,8 +1496,10 @@ sub _convert_to_webelement {
         if ( ( keys %$ret == 1 ) and exists $ret->{'ELEMENT'} ) {
 
             # replace an ELEMENT with WebElement
-            return $self->webelement_class->new( id => $ret->{ELEMENT},
-                driver => $self );
+            return $self->webelement_class->new(
+                id => $ret,
+                driver => $self
+            );
         }
 
         my %hash;
@@ -1945,8 +1949,10 @@ sub find_element {
                 die $@;
             }
         }
-        return $self->webelement_class->new( id => $ret_data->{ELEMENT},
-            driver => $self );
+        return $self->webelement_class->new(
+            id => $ret_data,
+            driver => $self
+        );
     }
     else {
         croak "Bad method, expected: " . join(', ', keys %{ $self->FINDERS });
@@ -2009,7 +2015,8 @@ sub find_elements {
             push(
                 @$elem_obj_arr,
                 $self->webelement_class->new(
-                    id => $_->{ELEMENT}, driver => $self
+                    id => $_,
+                    driver => $self
                 )
             );
         }
@@ -2077,7 +2084,7 @@ sub find_child_element {
                 die $@;
             }
         }
-        return $self->webelement_class->new( id => $ret_data->{ELEMENT},
+        return $self->webelement_class->new( id => $ret_data,
             driver => $self );
     }
     else {
@@ -2143,8 +2150,10 @@ sub find_child_elements {
         my $i = 0;
         foreach (@$ret_data) {
             $elem_obj_arr->[$i] =
-              $self->webelement_class->new( id => $_->{ELEMENT},
-                driver => $self );
+              $self->webelement_class->new(
+                  id => $_,
+                  driver => $self
+              );
             $i++;
         }
         return wantarray ? @{$elem_obj_arr} : $elem_obj_arr;
@@ -2216,8 +2225,10 @@ sub get_active_element {
         croak $@;
     }
     else {
-        return $self->webelement_class->new( id => $ret_data->{ELEMENT},
-            driver => $self );
+        return $self->webelement_class->new(
+            id => $ret_data,
+            driver => $self
+        );
     }
 }
 

+ 81 - 10
lib/Selenium/Remote/WebElement.pm

@@ -7,29 +7,100 @@ use Carp qw(carp croak);
 
 =head1 DESCRIPTION
 
-Selenium Webdriver represents all the HTML elements as WebElement. This module
-provides a mechanism to represent them as objects & perform various actions on
-the related elements. This module should not be instantiated directly by the end
-user. Selenium::Remote::Driver instantiates this module when required. Typically,
-the find_element method in Selenium::Remote::Driver returns this object on which
+Selenium Webdriver represents all the HTML elements as WebElements.
+This module provides a mechanism to represent them as objects &
+perform various actions on the related elements. This module should
+not be instantiated directly by the end user. Selenium::Remote::Driver
+instantiates this module when required. Typically, the find_element
+method in Selenium::Remote::Driver returns this object on which
 various element related operations can be carried out.
 
-=cut
+What is probably most useful on this page is the list of methods below
+that you can perform on an element once you've found one and S::R::D
+has made an instance of this for you.
 
-=head1 FUNCTIONS
+=attr id
+
+Required: Pass in a string representing the ID of the object. The
+string should be obtained from the response object of making one of
+the C<find_element> calls from L</Selenium::Remote::Driver>.
+
+The attribute is also set up to handle spec compliant element response
+objects via its `coerce` such that any of the following will work and
+are all equivalent:
+
+    my $old_elem = Selenium::Remote::WebElement->new(
+        id => 1,
+        driver => $driver
+    );
+
+    my $new_remote_elem = Selenium::Remote::WebElement->new(
+        id => { ELEMENT => 1 },
+        driver => $driver
+    );
+
+    my $new_spec_elem = Selenium::Remote::WebElement->new(
+        id => { 'element-6066-11e4-a52e-4f735466cecf' => 1 },
+        driver => $driver
+    );
+
+and then after instantiation, all three would give the following for
+`id`:
+
+    print $elem->id; # prints 1
+
+Again, for typical usage of S::R::D and this module, none of this
+matters and it should Just Work without you having to worry about it
+at all. For further reading, the L<W3C
+spec|https://www.w3.org/TR/webdriver/#elements> strictly dictates the
+exact behavior.
 
 =cut
 
 has 'id' => (
-    is => 'rw',
+    is => 'ro',
+    required => 1,
+    coerce => sub {
+        my ($value) = @_;
+        if (ref($value) eq 'HASH') {
+            if (exists $value->{ELEMENT}) {
+                # The JSONWireProtocol web element object looks like
+                #
+                #     { "ELEMENT": $INTEGER_ID }
+                return $value->{ELEMENT};
+            }
+            elsif (exists $value->{'element-6066-11e4-a52e-4f735466cecf'}) {
+                # but the WebDriver spec web element uses a magic
+                # string. See the spec for more information:
+                #
+                # https://www.w3.org/TR/webdriver/#elements
+                return $value->{'element-6066-11e4-a52e-4f735466cecf'};
+            }
+            else {
+                croak 'When passing in an object to the WebElement id attribute, it must have at least one of the ELEMENT or element-6066-11e4-a52e-4f735466cecf keys.';
+            }
+        }
+        else {
+            return $value;
+        }
+    }
 );
 
+=attr driver
+
+Required: Pass in a Selenium::Remote::Driver instance or one of its
+subclasses. The WebElement needs the appropriate Driver session to
+execute its commands properly.
+
+=cut
+
 has 'driver' => (
-    is => 'rw',
+    is => 'ro',
+    required => 1,
     handles => [qw(_execute_command)],
 );
 
-
+=head1 FUNCTIONS
 
 =head2 click
 

+ 25 - 0
t/02-webelement.t

@@ -109,4 +109,29 @@ QUIT: {
     ok((not defined $driver->{'session_id'}), 'Killed the remote session');
 }
 
+OBJECT_INSTANTIATION: {
+  SRD: {
+        my $value = { ELEMENT => 0 };
+        my $elem = Selenium::Remote::WebElement->new(
+            id => $value,
+            driver => ''
+        );
+        is($elem->id, 0,
+           'Can make element with standard SRD response');
+    }
+
+  GECKODRIVER:{
+        my $value = {
+            'element-6066-11e4-a52e-4f735466cecf' => '4f134cd0-4873-1148-aac8-5d496bea013f'
+        };
+        my $elem = Selenium::Remote::WebElement->new(
+            id => $value,
+            driver => ''
+        );
+        is($elem->id, '4f134cd0-4873-1148-aac8-5d496bea013f',
+           'Can make element with Geckodriver response');
+
+    }
+}
+
 done_testing;

+ 5 - 3
t/Test-Selenium-Remote-WebElement.t

@@ -19,14 +19,16 @@ $spec->{getElementValue} = sub { return { status => 'OK', return => 'my_value' }
 $spec->{getElementText} = sub { return { status => 'OK', return => "my_text\nis fantastic" }};
 $spec->{getElementAttribute}  = sub { my @args = @_; my $name = $args[0]->{name};  return { status => 'OK', return => "my_$name" }};
 
-my $driver =
-  Test::Selenium::Remote::Driver->new(
+my $driver = Test::Selenium::Remote::Driver->new(
     remote_conn => Selenium::Remote::Mock::RemoteConnection->new( spec => $spec, mock_cmds => $mock_commands ),
     commands => $mock_commands,
 );
 
 
-my $successful_element = Test::Selenium::Remote::WebElement->new(driver => $driver);
+my $successful_element = Test::Selenium::Remote::WebElement->new(
+    id => 'placeholder_id',
+    driver => $driver
+);
 $successful_element->clear_ok;
 $successful_element->click_ok;
 $successful_element->submit_ok;