File Coverage

blib/lib/Selenium/Remote/WebElement.pm
Criterion Covered Total %
statement 70 147 47.6
branch 11 48 22.9
condition 8 35 22.8
subroutine 19 33 57.5
pod 28 28 100.0
total 136 291 46.7


line stmt bran cond sub pod time code
1             package Selenium::Remote::WebElement;
2             $Selenium::Remote::WebElement::VERSION = '1.47';
3             # ABSTRACT: Representation of an HTML Element used by Selenium Remote Driver
4              
5 14     14   2635 use strict;
  14         28  
  14         365  
6 14     14   62 use warnings;
  14         27  
  14         291  
7              
8 14     14   64 use Moo;
  14         31  
  14         62  
9 14     14   3922 use Carp qw(carp croak);
  14         29  
  14         28470  
10              
11              
12             has 'id' => (
13             is => 'ro',
14             required => 1,
15             coerce => sub {
16             my ($value) = @_;
17             if ( ref($value) eq 'HASH' ) {
18             if ( exists $value->{ELEMENT} ) {
19              
20             # The JSONWireProtocol web element object looks like
21             #
22             # { "ELEMENT": $INTEGER_ID }
23             return $value->{ELEMENT};
24             }
25             elsif ( exists $value->{'element-6066-11e4-a52e-4f735466cecf'} ) {
26              
27             # but the WebDriver spec web element uses a magic
28             # string. See the spec for more information:
29             #
30             # https://www.w3.org/TR/webdriver/#elements
31             return $value->{'element-6066-11e4-a52e-4f735466cecf'};
32             }
33             else {
34             croak
35             '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.';
36             }
37             }
38             else {
39             return $value;
40             }
41             }
42             );
43              
44             has 'driver' => (
45             is => 'ro',
46             required => 1,
47             handles => [qw(_execute_command)],
48             );
49              
50              
51             sub child {
52 0     0 1 0 return $_[0]->{driver}->find_child_element(@_);
53             }
54              
55             sub children {
56 0     0 1 0 return $_[0]->{driver}->find_child_elements(@_);
57             }
58              
59              
60             sub click {
61 9     9 1 546 my ($self) = @_;
62 9         36 my $res = { 'command' => 'clickElement', 'id' => $self->id };
63 9         159 return $self->_execute_command($res);
64             }
65              
66              
67             sub execute_script {
68 0     0 1 0 my ($self, $script, @args) = @_;
69             return $self->driver->execute_script(
70             $script,
71             { 'element-6066-11e4-a52e-4f735466cecf' => $self->{id} },
72 0         0 @args );
73             }
74              
75             sub execute_async_script {
76 0     0 1 0 my ($self, $script, @args) = @_;
77             return $self->driver->execute_async_script(
78             $script,
79             { 'element-6066-11e4-a52e-4f735466cecf' => $self->{id} },
80 0         0 @args );
81             }
82              
83              
84              
85             sub submit {
86 1     1 1 3 my ($self) = @_;
87 1 50 33     8 if (
88             $self->driver->{is_wd3}
89             && !(
90 0         0 grep { $self->driver->browser_name eq $_ } qw{MicrosoftEdge}
91             )
92             )
93             {
94 0 0       0 if ( $self->get_tag_name() ne 'form' ) {
95             return $self->driver->execute_script(
96             "return arguments[0].form.submit();",
97 0         0 { 'element-6066-11e4-a52e-4f735466cecf' => $self->{id} } );
98             }
99             else {
100             return $self->driver->execute_script(
101             "return arguments[0].submit();",
102 0         0 { 'element-6066-11e4-a52e-4f735466cecf' => $self->{id} } );
103             }
104             }
105 1         5 my $res = { 'command' => 'submitElement', 'id' => $self->id };
106 1         20 return $self->_execute_command($res);
107             }
108              
109              
110             sub send_keys {
111 3     3 1 9 my ( $self, @strings ) = @_;
112 3 50       11 croak "no keys to send" unless scalar @strings >= 1;
113 3         20 my $res = { 'command' => 'sendKeysToElement', 'id' => $self->id };
114              
115             # We need to send an array of single characters to be WebDriver
116             # spec compatible. That is, for @strings = ('hel', 'lo'), the
117             # corresponding value must be ('h', 'e', 'l', 'l', 'o' ). This
118             # format conforms with the Spec AND works with the Selenium
119             # standalone server.
120 3         8 my $strings = join( '', map { $_ . "" } @strings );
  3         15  
121 3         34 my $params = {
122             'value' => [ split( '', $strings ) ],
123             text => $strings,
124             };
125 3         62 return $self->_execute_command( $res, $params );
126             }
127              
128              
129             sub is_selected {
130 4     4 1 142 my ($self) = @_;
131              
132 4 50       13 my $to_check = $self->get_tag_name() eq 'option' ? 'selected' : 'checked';
133             return $self->get_property($to_check)
134             if $self->driver->{is_wd3}
135 4 50 33     28 && !( grep { $self->driver->browser_name eq $_ }
  0         0  
136             qw{chrome MicrosoftEdge} );
137 4         15 my $res = { 'command' => 'isElementSelected', 'id' => $self->id };
138 4         68 return $self->_execute_command($res);
139             }
140              
141              
142             sub set_selected {
143 0     0 1 0 my ($self) = @_;
144 0 0       0 if ( $self->driver->{is_wd3} ) {
145 0 0       0 return if $self->is_selected();
146 0         0 return $self->click();
147             }
148 0         0 my $res = { 'command' => 'setElementSelected', 'id' => $self->id };
149 0         0 return $self->_execute_command($res);
150             }
151              
152              
153             sub toggle {
154 1     1 1 443 my ($self) = @_;
155 1 50       7 if ( $self->driver->{is_wd3} ) {
156 0 0       0 return $self->click() unless $self->is_selected();
157             return $self->driver->execute_script(
158             qq/ if (arguments[0].checked) { arguments[0].checked = 0 }; return arguments[0].checked; /,
159             { 'element-6066-11e4-a52e-4f735466cecf' => $self->{id} }
160 0         0 );
161             }
162 1         6 my $res = { 'command' => 'toggleElement', 'id' => $self->id };
163 1         21 return $self->_execute_command($res);
164             }
165              
166              
167             sub is_enabled {
168 2     2 1 4 my ($self) = @_;
169 2 50 33     17 if (
170             $self->driver->{is_wd3}
171             && !(
172 0         0 grep { $self->driver->browser_name eq $_ } qw{chrome MicrosoftEdge}
173             )
174             )
175             {
176 0 0       0 return 1 if $self->get_tag_name() ne 'input';
177 0 0       0 return $self->get_property('disabled') ? 0 : 1;
178             }
179 2         9 my $res = { 'command' => 'isElementEnabled', 'id' => $self->id };
180 2         37 return $self->_execute_command($res);
181             }
182              
183              
184             sub get_element_location {
185 1     1 1 844 my ($self) = @_;
186 1 50 33     8 if (
187             $self->driver->{is_wd3}
188             && !(
189 0         0 grep { $self->driver->browser_name eq $_ } qw{chrome MicrosoftEdge}
190             )
191             )
192             {
193 0         0 my $data = $self->get_element_rect();
194 0         0 delete $data->{height};
195 0         0 delete $data->{width};
196 0         0 return $data;
197             }
198 1         6 my $res = { 'command' => 'getElementLocation', 'id' => $self->id };
199 1         20 return $self->_execute_command($res);
200             }
201              
202              
203             sub get_size {
204 1     1 1 15 my ($self) = @_;
205 1 50 33     7 if (
206             $self->driver->{is_wd3}
207             && !(
208 0         0 grep { $self->driver->browser_name eq $_ } qw{chrome MicrosoftEdge}
209             )
210             )
211             {
212 0         0 my $data = $self->get_element_rect();
213 0         0 delete $data->{x};
214 0         0 delete $data->{y};
215 0         0 return $data;
216             }
217 1         5 my $res = { 'command' => 'getElementSize', 'id' => $self->id };
218 1         16 return $self->_execute_command($res);
219             }
220              
221              
222             sub get_element_rect {
223 0     0 1 0 my ($self) = @_;
224 0         0 my $res = { 'command' => 'getElementRect', 'id' => $self->id };
225 0         0 return $self->_execute_command($res);
226             }
227              
228              
229             sub get_element_location_in_view {
230 0     0 1 0 my ($self) = @_;
231              
232             #XXX chrome is dopey here
233             return $self->driver->execute_script(
234             qq{
235             if (typeof(arguments[0]) !== 'undefined' && arguments[0].nodeType === Node.ELEMENT_NODE) {
236             arguments[0].scrollIntoView();
237             var pos = arguments[0].getBoundingClientRect();
238             return {y:pos.top,x:pos.left};
239             }
240             return {};
241             }, { 'element-6066-11e4-a52e-4f735466cecf' => $self->{id} }
242             )
243 0 0 0     0 if $self->driver->{is_wd3} && grep { $self->driver->browser_name eq $_ }
  0         0  
244             ( 'firefox', 'internet explorer', 'chrome' );
245 0         0 my $res = { 'command' => 'getElementLocationInView', 'id' => $self->id };
246 0         0 return $self->_execute_command($res);
247             }
248              
249              
250             sub get_tag_name {
251 5     5 1 267 my ($self) = @_;
252 5         23 my $res = { 'command' => 'getElementTagName', 'id' => $self->id };
253 5         110 return $self->_execute_command($res);
254             }
255              
256              
257             sub clear {
258 2     2 1 6 my ($self) = @_;
259 2         11 my $res = { 'command' => 'clearElement', 'id' => $self->id };
260 2         45 return $self->_execute_command($res);
261             }
262              
263              
264             sub get_attribute {
265 17     17 1 1423 my ( $self, $attr_name, $no_i_really_mean_it ) = @_;
266 17 50       64 if ( not defined $attr_name ) {
267 0         0 croak 'Attribute name not provided';
268             }
269              
270             #Handle global JSONWire emulation flag
271 17 50       47 $no_i_really_mean_it = 1 unless $self->{driver}->{emulate_jsonwire};
272              
273             return $self->get_property($attr_name)
274             if $self->driver->{is_wd3}
275 17 0 33     62 && !( grep { $self->driver->browser_name eq $_ }
  0   33     0  
276             qw{chrome MicrosoftEdge} )
277             && !$no_i_really_mean_it;
278              
279 17         61 my $res = {
280             'command' => 'getElementAttribute',
281             'id' => $self->id,
282             'name' => $attr_name,
283             };
284 17         322 return $self->_execute_command($res);
285             }
286              
287              
288             sub get_property {
289 0     0 1 0 my ( $self, $prop ) = @_;
290             return $self->get_attribute($prop)
291             if $self->driver->{is_wd3}
292 0 0 0     0 && ( grep { $self->driver->browser_name eq $_ }
  0         0  
293             qw{chrome MicrosoftEdge} );
294 0         0 my $res =
295             { 'command' => 'getElementProperty', id => $self->id, name => $prop };
296 0         0 return $self->_execute_command($res);
297             }
298              
299              
300             sub get_value {
301 9     9 1 793 my ($self) = @_;
302 9         32 return $self->get_attribute('value');
303             }
304              
305              
306             sub is_displayed {
307 6     6 1 48 my ($self) = @_;
308 6 50 33     31 if (
309             $self->driver->{is_wd3}
310             && !(
311 0         0 grep { $self->driver->browser_name eq $_ } qw{chrome MicrosoftEdge}
312             )
313             )
314             {
315 0 0 0     0 return 0
316             if $self->get_tag_name() eq 'input'
317             && $self->get_property('type') eq 'hidden'; #hidden type inputs
318 0 0       0 return 0 unless $self->_is_in_viewport();
319 0         0 return int( $self->get_css_attribute('display') ne 'none' );
320             }
321 6         24 my $res = { 'command' => 'isElementDisplayed', 'id' => $self->id };
322 6         110 return $self->_execute_command($res);
323             }
324              
325             sub _is_in_viewport {
326 0     0   0 my ($self) = @_;
327             return $self->driver->execute_script(
328             qq{
329             var rect = arguments[0].getBoundingClientRect();
330             return (
331             rect.top >= 0 &&
332             rect.left >= 0 &&
333             rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
334             rect.right <= (window.innerWidth || document.documentElement.clientWidth)
335             );
336             }, { 'element-6066-11e4-a52e-4f735466cecf' => $self->{id} }
337 0         0 );
338             }
339              
340              
341             sub is_hidden {
342 2     2 1 253 my ($self) = @_;
343 2         6 return !$self->is_displayed();
344             }
345              
346              
347             sub drag {
348 0     0 1 0 my ( $self, $target ) = @_;
349 0         0 require Selenium::ActionChains;
350 0         0 my $chain = Selenium::ActionChains->new( driver => $self->driver );
351 0         0 return $chain->drag_and_drop( $self, $target )->perform();
352             }
353              
354              
355             sub get_text {
356 21     21 1 119 my ($self) = @_;
357 21         75 my $res = { 'command' => 'getElementText', 'id' => $self->id };
358 21         345 return $self->_execute_command($res);
359             }
360              
361              
362             sub get_css_attribute {
363 0     0 1   my ( $self, $attr_name ) = @_;
364 0 0         if ( not defined $attr_name ) {
365 0           croak 'CSS attribute name not provided';
366             }
367 0           my $res = {
368             'command' => 'getElementValueOfCssProperty',
369             'id' => $self->id,
370             'property_name' => $attr_name,
371             };
372 0           return $self->_execute_command($res);
373             }
374              
375              
376             sub describe {
377 0     0 1   my ($self) = @_;
378 0           my $res = { 'command' => 'describeElement', 'id' => $self->id };
379 0           return $self->_execute_command($res);
380             }
381              
382              
383             sub screenshot {
384 0     0 1   my ( $self, $scroll ) = @_;
385 0   0       $scroll //= 1;
386 0           my $res = { 'command' => 'elementScreenshot', id => $self->id };
387 0           my $input = { scroll => int($scroll) };
388 0           return $self->_execute_command( $res, $input );
389             }
390              
391              
392             sub capture_screenshot {
393 0     0 1   my ( $self, $filename, $scroll ) = @_;
394 0 0         croak '$filename is required' unless $filename;
395              
396 0           open( my $fh, '>', $filename );
397 0           binmode $fh;
398 0           print $fh MIME::Base64::decode_base64( $self->screenshot($scroll) );
399 0           CORE::close $fh;
400 0           return 1;
401             }
402              
403             1;
404              
405             __END__