| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Selenium::Remote::Driver; |
|
2
|
|
|
|
|
|
|
$Selenium::Remote::Driver::VERSION = '1.49'; |
|
3
|
14
|
|
|
14
|
|
514339
|
use strict; |
|
|
14
|
|
|
|
|
99
|
|
|
|
14
|
|
|
|
|
477
|
|
|
4
|
14
|
|
|
14
|
|
76
|
use warnings; |
|
|
14
|
|
|
|
|
30
|
|
|
|
14
|
|
|
|
|
389
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
# ABSTRACT: Perl Client for Selenium Remote Driver |
|
7
|
|
|
|
|
|
|
|
|
8
|
14
|
|
|
14
|
|
4498
|
use Moo; |
|
|
14
|
|
|
|
|
87464
|
|
|
|
14
|
|
|
|
|
88
|
|
|
9
|
14
|
|
|
14
|
|
17123
|
use Try::Tiny; |
|
|
14
|
|
|
|
|
8197
|
|
|
|
14
|
|
|
|
|
815
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
14
|
|
|
14
|
|
296
|
use 5.006; |
|
|
14
|
|
|
|
|
54
|
|
|
12
|
14
|
|
|
14
|
|
150
|
use v5.10.0; # Before 5.006, v5.10.0 would not be understood. |
|
|
14
|
|
|
|
|
45
|
|
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
# See http://perldoc.perl.org/5.10.0/functions/use.html#use-VERSION |
|
15
|
|
|
|
|
|
|
# and http://www.dagolden.com/index.php/369/version-numbers-should-be-boring/ |
|
16
|
|
|
|
|
|
|
# for details. |
|
17
|
|
|
|
|
|
|
|
|
18
|
14
|
|
|
14
|
|
92
|
use Carp; |
|
|
14
|
|
|
|
|
27
|
|
|
|
14
|
|
|
|
|
1025
|
|
|
19
|
|
|
|
|
|
|
our @CARP_NOT; |
|
20
|
|
|
|
|
|
|
|
|
21
|
14
|
|
|
14
|
|
7201
|
use IO::String; |
|
|
14
|
|
|
|
|
45447
|
|
|
|
14
|
|
|
|
|
495
|
|
|
22
|
14
|
|
|
14
|
|
9171
|
use Archive::Zip qw( :ERROR_CODES ); |
|
|
14
|
|
|
|
|
1087227
|
|
|
|
14
|
|
|
|
|
2040
|
|
|
23
|
14
|
|
|
14
|
|
170
|
use Scalar::Util; |
|
|
14
|
|
|
|
|
33
|
|
|
|
14
|
|
|
|
|
611
|
|
|
24
|
14
|
|
|
14
|
|
7020
|
use Selenium::Remote::RemoteConnection; |
|
|
14
|
|
|
|
|
54
|
|
|
|
14
|
|
|
|
|
596
|
|
|
25
|
14
|
|
|
14
|
|
7016
|
use Selenium::Remote::Commands; |
|
|
14
|
|
|
|
|
56
|
|
|
|
14
|
|
|
|
|
436
|
|
|
26
|
14
|
|
|
14
|
|
6763
|
use Selenium::Remote::Spec; |
|
|
14
|
|
|
|
|
56
|
|
|
|
14
|
|
|
|
|
452
|
|
|
27
|
14
|
|
|
14
|
|
4929
|
use Selenium::Remote::WebElement; |
|
|
14
|
|
|
|
|
59
|
|
|
|
14
|
|
|
|
|
402
|
|
|
28
|
14
|
|
|
14
|
|
6188
|
use Selenium::Remote::WDKeys; |
|
|
14
|
|
|
|
|
43
|
|
|
|
14
|
|
|
|
|
672
|
|
|
29
|
14
|
|
|
14
|
|
6788
|
use File::Spec::Functions (); |
|
|
14
|
|
|
|
|
12804
|
|
|
|
14
|
|
|
|
|
420
|
|
|
30
|
14
|
|
|
14
|
|
127
|
use File::Basename qw(basename); |
|
|
14
|
|
|
|
|
217
|
|
|
|
14
|
|
|
|
|
2409
|
|
|
31
|
14
|
|
|
14
|
|
4832
|
use Sub::Install (); |
|
|
14
|
|
|
|
|
16391
|
|
|
|
14
|
|
|
|
|
403
|
|
|
32
|
14
|
|
|
14
|
|
5922
|
use MIME::Base64 (); |
|
|
14
|
|
|
|
|
8956
|
|
|
|
14
|
|
|
|
|
401
|
|
|
33
|
14
|
|
|
14
|
|
7278
|
use Time::HiRes qw(usleep); |
|
|
14
|
|
|
|
|
20639
|
|
|
|
14
|
|
|
|
|
63
|
|
|
34
|
14
|
|
|
14
|
|
3207
|
use Clone qw{clone}; |
|
|
14
|
|
|
|
|
41
|
|
|
|
14
|
|
|
|
|
1129
|
|
|
35
|
14
|
|
|
14
|
|
109
|
use List::Util qw{any}; |
|
|
14
|
|
|
|
|
55
|
|
|
|
14
|
|
|
|
|
1219
|
|
|
36
|
|
|
|
|
|
|
|
|
37
|
14
|
|
|
|
|
153151
|
use constant FINDERS => { |
|
38
|
|
|
|
|
|
|
class => 'class name', |
|
39
|
|
|
|
|
|
|
class_name => 'class name', |
|
40
|
|
|
|
|
|
|
css => 'css selector', |
|
41
|
|
|
|
|
|
|
id => 'id', |
|
42
|
|
|
|
|
|
|
link => 'link text', |
|
43
|
|
|
|
|
|
|
link_text => 'link text', |
|
44
|
|
|
|
|
|
|
name => 'name', |
|
45
|
|
|
|
|
|
|
partial_link_text => 'partial link text', |
|
46
|
|
|
|
|
|
|
tag_name => 'tag name', |
|
47
|
|
|
|
|
|
|
xpath => 'xpath', |
|
48
|
14
|
|
|
14
|
|
108
|
}; |
|
|
14
|
|
|
|
|
60
|
|
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
our $FORCE_WD2 = 0; |
|
51
|
|
|
|
|
|
|
our $FORCE_WD3 = 0; |
|
52
|
|
|
|
|
|
|
our %CURRENT_ACTION_CHAIN = ( actions => [] ); |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
has 'remote_server_addr' => ( |
|
58
|
|
|
|
|
|
|
is => 'rw', |
|
59
|
|
|
|
|
|
|
coerce => sub { ( defined( $_[0] ) ? $_[0] : 'localhost' ) }, |
|
60
|
|
|
|
|
|
|
default => sub { 'localhost' }, |
|
61
|
|
|
|
|
|
|
predicate => 1 |
|
62
|
|
|
|
|
|
|
); |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
has 'browser_name' => ( |
|
65
|
|
|
|
|
|
|
is => 'rw', |
|
66
|
|
|
|
|
|
|
coerce => sub { ( defined( $_[0] ) ? $_[0] : 'firefox' ) }, |
|
67
|
|
|
|
|
|
|
default => sub { 'firefox' }, |
|
68
|
|
|
|
|
|
|
); |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
has 'base_url' => ( |
|
71
|
|
|
|
|
|
|
is => 'lazy', |
|
72
|
|
|
|
|
|
|
coerce => sub { |
|
73
|
|
|
|
|
|
|
my $base_url = shift; |
|
74
|
|
|
|
|
|
|
$base_url =~ s|/$||; |
|
75
|
|
|
|
|
|
|
return $base_url; |
|
76
|
|
|
|
|
|
|
}, |
|
77
|
|
|
|
|
|
|
predicate => 'has_base_url', |
|
78
|
|
|
|
|
|
|
); |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
has 'platform' => ( |
|
81
|
|
|
|
|
|
|
is => 'rw', |
|
82
|
|
|
|
|
|
|
coerce => sub { ( defined( $_[0] ) ? $_[0] : 'ANY' ) }, |
|
83
|
|
|
|
|
|
|
default => sub { 'ANY' }, |
|
84
|
|
|
|
|
|
|
); |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
has 'port' => ( |
|
87
|
|
|
|
|
|
|
is => 'rw', |
|
88
|
|
|
|
|
|
|
coerce => sub { ( defined( $_[0] ) ? $_[0] : '4444' ) }, |
|
89
|
|
|
|
|
|
|
default => sub { '4444' }, |
|
90
|
|
|
|
|
|
|
predicate => 1 |
|
91
|
|
|
|
|
|
|
); |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
has 'version' => ( |
|
94
|
|
|
|
|
|
|
is => 'rw', |
|
95
|
|
|
|
|
|
|
default => sub { '' }, |
|
96
|
|
|
|
|
|
|
); |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
has 'webelement_class' => ( |
|
99
|
|
|
|
|
|
|
is => 'rw', |
|
100
|
|
|
|
|
|
|
default => sub { 'Selenium::Remote::WebElement' }, |
|
101
|
|
|
|
|
|
|
); |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
has 'default_finder' => ( |
|
104
|
|
|
|
|
|
|
is => 'rw', |
|
105
|
|
|
|
|
|
|
coerce => sub { __PACKAGE__->FINDERS->{ $_[0] } }, |
|
106
|
|
|
|
|
|
|
default => sub { 'xpath' }, |
|
107
|
|
|
|
|
|
|
); |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
has 'session_id' => ( |
|
110
|
|
|
|
|
|
|
is => 'rw', |
|
111
|
|
|
|
|
|
|
default => sub { undef }, |
|
112
|
|
|
|
|
|
|
); |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
has 'remote_conn' => ( |
|
115
|
|
|
|
|
|
|
is => 'lazy', |
|
116
|
|
|
|
|
|
|
builder => sub { |
|
117
|
11
|
|
|
11
|
|
115
|
my $self = shift; |
|
118
|
11
|
|
|
|
|
198
|
return Selenium::Remote::RemoteConnection->new( |
|
119
|
|
|
|
|
|
|
remote_server_addr => $self->remote_server_addr, |
|
120
|
|
|
|
|
|
|
port => $self->port, |
|
121
|
|
|
|
|
|
|
ua => $self->ua, |
|
122
|
|
|
|
|
|
|
wd_context_prefix => $self->wd_context_prefix |
|
123
|
|
|
|
|
|
|
); |
|
124
|
|
|
|
|
|
|
}, |
|
125
|
|
|
|
|
|
|
); |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
has 'error_handler' => ( |
|
128
|
|
|
|
|
|
|
is => 'rw', |
|
129
|
|
|
|
|
|
|
coerce => sub { |
|
130
|
|
|
|
|
|
|
my ($maybe_coderef) = @_; |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
if ( ref($maybe_coderef) eq 'CODE' ) { |
|
133
|
|
|
|
|
|
|
return $maybe_coderef; |
|
134
|
|
|
|
|
|
|
} |
|
135
|
|
|
|
|
|
|
else { |
|
136
|
|
|
|
|
|
|
croak 'The error handler must be a code ref.'; |
|
137
|
|
|
|
|
|
|
} |
|
138
|
|
|
|
|
|
|
}, |
|
139
|
|
|
|
|
|
|
clearer => 1, |
|
140
|
|
|
|
|
|
|
predicate => 1 |
|
141
|
|
|
|
|
|
|
); |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
has 'ua' => ( |
|
144
|
|
|
|
|
|
|
is => 'lazy', |
|
145
|
3
|
|
|
3
|
|
170
|
builder => sub { return LWP::UserAgent->new } |
|
146
|
|
|
|
|
|
|
); |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
has 'commands' => ( |
|
149
|
|
|
|
|
|
|
is => 'lazy', |
|
150
|
|
|
|
|
|
|
builder => sub { |
|
151
|
24
|
|
|
24
|
|
592
|
return Selenium::Remote::Commands->new; |
|
152
|
|
|
|
|
|
|
}, |
|
153
|
|
|
|
|
|
|
); |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
has 'commands_v3' => ( |
|
156
|
|
|
|
|
|
|
is => 'lazy', |
|
157
|
|
|
|
|
|
|
builder => sub { |
|
158
|
29
|
|
|
29
|
|
744
|
return Selenium::Remote::Spec->new; |
|
159
|
|
|
|
|
|
|
}, |
|
160
|
|
|
|
|
|
|
); |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
has 'auto_close' => ( |
|
163
|
|
|
|
|
|
|
is => 'rw', |
|
164
|
|
|
|
|
|
|
coerce => sub { ( defined( $_[0] ) ? $_[0] : 1 ) }, |
|
165
|
|
|
|
|
|
|
default => sub { 1 }, |
|
166
|
|
|
|
|
|
|
); |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
has 'pid' => ( |
|
169
|
|
|
|
|
|
|
is => 'lazy', |
|
170
|
35
|
|
|
35
|
|
635
|
builder => sub { return $$ } |
|
171
|
|
|
|
|
|
|
); |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
has 'javascript' => ( |
|
174
|
|
|
|
|
|
|
is => 'rw', |
|
175
|
|
|
|
|
|
|
coerce => sub { $_[0] ? JSON::true : JSON::false }, |
|
176
|
|
|
|
|
|
|
default => sub { return JSON::true } |
|
177
|
|
|
|
|
|
|
); |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
has 'accept_ssl_certs' => ( |
|
180
|
|
|
|
|
|
|
is => 'rw', |
|
181
|
|
|
|
|
|
|
coerce => sub { $_[0] ? JSON::true : JSON::false }, |
|
182
|
|
|
|
|
|
|
default => sub { return JSON::true } |
|
183
|
|
|
|
|
|
|
); |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
has 'proxy' => ( |
|
186
|
|
|
|
|
|
|
is => 'rw', |
|
187
|
|
|
|
|
|
|
coerce => sub { |
|
188
|
|
|
|
|
|
|
my $proxy = $_[0]; |
|
189
|
|
|
|
|
|
|
if ( $proxy->{proxyType} =~ /^pac$/i ) { |
|
190
|
|
|
|
|
|
|
if ( not defined $proxy->{proxyAutoconfigUrl} ) { |
|
191
|
|
|
|
|
|
|
croak "proxyAutoconfigUrl not provided\n"; |
|
192
|
|
|
|
|
|
|
} |
|
193
|
|
|
|
|
|
|
elsif ( not( $proxy->{proxyAutoconfigUrl} =~ /^(http|file)/g ) ) { |
|
194
|
|
|
|
|
|
|
croak |
|
195
|
|
|
|
|
|
|
"proxyAutoconfigUrl should be of format http:// or file://"; |
|
196
|
|
|
|
|
|
|
} |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
if ( $proxy->{proxyAutoconfigUrl} =~ /^file/ ) { |
|
199
|
|
|
|
|
|
|
my $pac_url = $proxy->{proxyAutoconfigUrl}; |
|
200
|
|
|
|
|
|
|
my $file = $pac_url; |
|
201
|
|
|
|
|
|
|
$file =~ s{^file://}{}; |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
if ( !-e $file ) { |
|
204
|
|
|
|
|
|
|
warn "proxyAutoConfigUrl file does not exist: '$pac_url'"; |
|
205
|
|
|
|
|
|
|
} |
|
206
|
|
|
|
|
|
|
} |
|
207
|
|
|
|
|
|
|
} |
|
208
|
|
|
|
|
|
|
$proxy; |
|
209
|
|
|
|
|
|
|
}, |
|
210
|
|
|
|
|
|
|
); |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
has 'extra_capabilities' => ( |
|
213
|
|
|
|
|
|
|
is => 'rw', |
|
214
|
|
|
|
|
|
|
default => sub { {} } |
|
215
|
|
|
|
|
|
|
); |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
has 'firefox_profile' => ( |
|
218
|
|
|
|
|
|
|
is => 'rw', |
|
219
|
|
|
|
|
|
|
coerce => sub { |
|
220
|
|
|
|
|
|
|
my $profile = shift; |
|
221
|
|
|
|
|
|
|
unless ( Scalar::Util::blessed($profile) |
|
222
|
|
|
|
|
|
|
&& $profile->isa('Selenium::Firefox::Profile') ) |
|
223
|
|
|
|
|
|
|
{ |
|
224
|
|
|
|
|
|
|
croak "firefox_profile should be a Selenium::Firefox::Profile\n"; |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
return $profile; |
|
228
|
|
|
|
|
|
|
}, |
|
229
|
|
|
|
|
|
|
predicate => 'has_firefox_profile', |
|
230
|
|
|
|
|
|
|
clearer => 1 |
|
231
|
|
|
|
|
|
|
); |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
has debug => ( |
|
234
|
|
|
|
|
|
|
is => 'lazy', |
|
235
|
|
|
|
|
|
|
default => sub { 0 }, |
|
236
|
|
|
|
|
|
|
); |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
has 'desired_capabilities' => ( |
|
239
|
|
|
|
|
|
|
is => 'lazy', |
|
240
|
|
|
|
|
|
|
predicate => 'has_desired_capabilities' |
|
241
|
|
|
|
|
|
|
); |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
has 'inner_window_size' => ( |
|
244
|
|
|
|
|
|
|
is => 'lazy', |
|
245
|
|
|
|
|
|
|
predicate => 1, |
|
246
|
|
|
|
|
|
|
coerce => sub { |
|
247
|
|
|
|
|
|
|
my $size = shift; |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
croak "inner_window_size must have two elements: [ height, width ]" |
|
250
|
|
|
|
|
|
|
unless scalar @$size == 2; |
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
foreach my $dim (@$size) { |
|
253
|
|
|
|
|
|
|
croak 'inner_window_size only accepts integers, not: ' . $dim |
|
254
|
|
|
|
|
|
|
unless Scalar::Util::looks_like_number($dim); |
|
255
|
|
|
|
|
|
|
} |
|
256
|
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
return $size; |
|
258
|
|
|
|
|
|
|
}, |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
); |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
# At the time of writing, Geckodriver uses a different endpoint than |
|
263
|
|
|
|
|
|
|
# the java bindings for executing synchronous and asynchronous |
|
264
|
|
|
|
|
|
|
# scripts. As a matter of fact, Geckodriver does conform to the W3C |
|
265
|
|
|
|
|
|
|
# spec, but as are bound to support both while the java bindings |
|
266
|
|
|
|
|
|
|
# transition to full spec support, we need some way to handle the |
|
267
|
|
|
|
|
|
|
# difference. |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
has '_execute_script_suffix' => ( |
|
270
|
|
|
|
|
|
|
is => 'lazy', |
|
271
|
|
|
|
|
|
|
default => '' |
|
272
|
|
|
|
|
|
|
); |
|
273
|
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
with 'Selenium::Remote::Finders'; |
|
275
|
|
|
|
|
|
|
with 'Selenium::Remote::Driver::CanSetWebdriverContext'; |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
sub BUILD { |
|
278
|
32
|
|
|
32
|
0
|
263
|
my $self = shift; |
|
279
|
|
|
|
|
|
|
|
|
280
|
32
|
100
|
|
|
|
219
|
if ( !( defined $self->session_id ) ) { |
|
281
|
31
|
100
|
|
|
|
171
|
if ( $self->has_desired_capabilities ) { |
|
282
|
5
|
|
|
|
|
88
|
$self->new_desired_session( $self->desired_capabilities ); |
|
283
|
|
|
|
|
|
|
} |
|
284
|
|
|
|
|
|
|
else { |
|
285
|
|
|
|
|
|
|
# Connect to remote server & establish a new session |
|
286
|
26
|
|
|
|
|
185
|
$self->new_session( $self->extra_capabilities ); |
|
287
|
|
|
|
|
|
|
} |
|
288
|
|
|
|
|
|
|
} |
|
289
|
|
|
|
|
|
|
|
|
290
|
30
|
50
|
|
|
|
277
|
if ( !( defined $self->session_id ) ) { |
|
|
|
50
|
|
|
|
|
|
|
291
|
0
|
|
|
|
|
0
|
croak "Could not establish a session with the remote server\n"; |
|
292
|
|
|
|
|
|
|
} |
|
293
|
|
|
|
|
|
|
elsif ( $self->has_inner_window_size ) { |
|
294
|
0
|
|
|
|
|
0
|
my $size = $self->inner_window_size; |
|
295
|
0
|
|
|
|
|
0
|
$self->set_inner_window_size(@$size); |
|
296
|
|
|
|
|
|
|
} |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
#Set debug if needed |
|
299
|
30
|
50
|
|
|
|
734
|
$self->debug_on() if $self->debug; |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
# Setup non-croaking, parameter versions of finders |
|
302
|
30
|
|
|
|
|
66
|
foreach my $by ( keys %{ $self->FINDERS } ) { |
|
|
30
|
|
|
|
|
250
|
|
|
303
|
300
|
|
|
|
|
5355
|
my $finder_name = 'find_element_by_' . $by; |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# In case we get instantiated multiple times, we don't want to |
|
306
|
|
|
|
|
|
|
# install into the name space every time. |
|
307
|
300
|
100
|
|
|
|
1681
|
unless ( $self->can($finder_name) ) { |
|
308
|
110
|
|
|
|
|
376
|
my $find_sub = $self->_build_find_by($by); |
|
309
|
|
|
|
|
|
|
|
|
310
|
110
|
|
|
|
|
572
|
Sub::Install::install_sub( |
|
311
|
|
|
|
|
|
|
{ |
|
312
|
|
|
|
|
|
|
code => $find_sub, |
|
313
|
|
|
|
|
|
|
into => __PACKAGE__, |
|
314
|
|
|
|
|
|
|
as => $finder_name, |
|
315
|
|
|
|
|
|
|
} |
|
316
|
|
|
|
|
|
|
); |
|
317
|
|
|
|
|
|
|
} |
|
318
|
|
|
|
|
|
|
} |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
sub new_from_caps { |
|
322
|
4
|
|
|
4
|
1
|
12108
|
my ( $self, %args ) = @_; |
|
323
|
|
|
|
|
|
|
|
|
324
|
4
|
100
|
|
|
|
21
|
if ( not exists $args{desired_capabilities} ) { |
|
325
|
2
|
|
|
|
|
8
|
$args{desired_capabilities} = {}; |
|
326
|
|
|
|
|
|
|
} |
|
327
|
|
|
|
|
|
|
|
|
328
|
4
|
|
|
|
|
114
|
return $self->new(%args); |
|
329
|
|
|
|
|
|
|
} |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
sub DEMOLISH { |
|
332
|
38
|
|
|
38
|
0
|
35611
|
my ( $self, $in_global_destruction ) = @_; |
|
333
|
38
|
50
|
|
|
|
750
|
return if $$ != $self->pid; |
|
334
|
38
|
50
|
|
|
|
160
|
return if $in_global_destruction; |
|
335
|
38
|
100
|
100
|
|
|
693
|
$self->quit() if ( $self->auto_close && defined $self->session_id ); |
|
336
|
|
|
|
|
|
|
} |
|
337
|
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
# We install an 'around' because we can catch more exceptions this way |
|
339
|
|
|
|
|
|
|
# than simply wrapping the explicit croaks in _execute_command. |
|
340
|
|
|
|
|
|
|
# @args should be fed to the handler to provide context |
|
341
|
|
|
|
|
|
|
# return_value could be assigned from the handler if we want to allow the |
|
342
|
|
|
|
|
|
|
# error_handler to handle the errors |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
around '_execute_command' => sub { |
|
345
|
|
|
|
|
|
|
my $orig = shift; |
|
346
|
|
|
|
|
|
|
my $self = shift; |
|
347
|
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
# copy @_ because it gets lost in the way |
|
349
|
|
|
|
|
|
|
my @args = @_; |
|
350
|
|
|
|
|
|
|
my $return_value; |
|
351
|
|
|
|
|
|
|
try { |
|
352
|
|
|
|
|
|
|
$return_value = $orig->( $self, @args ); |
|
353
|
|
|
|
|
|
|
} |
|
354
|
|
|
|
|
|
|
catch { |
|
355
|
|
|
|
|
|
|
if ( $self->has_error_handler ) { |
|
356
|
|
|
|
|
|
|
$return_value = $self->error_handler->( $self, $_, @args ); |
|
357
|
|
|
|
|
|
|
} |
|
358
|
|
|
|
|
|
|
else { |
|
359
|
|
|
|
|
|
|
croak $_; |
|
360
|
|
|
|
|
|
|
} |
|
361
|
|
|
|
|
|
|
}; |
|
362
|
|
|
|
|
|
|
return $return_value; |
|
363
|
|
|
|
|
|
|
}; |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
# This is an internal method used the Driver & is not supposed to be used by |
|
366
|
|
|
|
|
|
|
# end user. This method is used by Driver to set up all the parameters |
|
367
|
|
|
|
|
|
|
# (url & JSON), send commands & receive processed response from the server. |
|
368
|
|
|
|
|
|
|
sub _execute_command { |
|
369
|
|
|
|
|
|
|
my ( $self, $res, $params ) = @_; |
|
370
|
|
|
|
|
|
|
$res->{'session_id'} = $self->session_id; |
|
371
|
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
print "Prepping $res->{command}\n" if $self->{debug}; |
|
373
|
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
#webdriver 3 shims |
|
375
|
|
|
|
|
|
|
return $self->{capabilities} |
|
376
|
|
|
|
|
|
|
if $res->{command} eq 'getCapabilities' && $self->{capabilities}; |
|
377
|
|
|
|
|
|
|
$res->{ms} = $params->{ms} if $params->{ms}; |
|
378
|
|
|
|
|
|
|
$res->{type} = $params->{type} if $params->{type}; |
|
379
|
|
|
|
|
|
|
$res->{text} = $params->{text} if $params->{text}; |
|
380
|
|
|
|
|
|
|
$res->{using} = $params->{using} if $params->{using}; |
|
381
|
|
|
|
|
|
|
$res->{value} = $params->{value} if $params->{value}; |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
print "Executing $res->{command}\n" if $self->{debug}; |
|
384
|
|
|
|
|
|
|
my $resource = |
|
385
|
|
|
|
|
|
|
$self->{is_wd3} |
|
386
|
|
|
|
|
|
|
? $self->commands_v3->get_params($res) |
|
387
|
|
|
|
|
|
|
: $self->commands->get_params($res); |
|
388
|
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
#Fall-back to legacy if wd3 command doesn't exist |
|
390
|
|
|
|
|
|
|
if ( !$resource && $self->{is_wd3} ) { |
|
391
|
|
|
|
|
|
|
print "Falling back to legacy selenium method for $res->{command}\n" |
|
392
|
|
|
|
|
|
|
if $self->{debug}; |
|
393
|
|
|
|
|
|
|
$resource = $self->commands->get_params($res); |
|
394
|
|
|
|
|
|
|
} |
|
395
|
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
#XXX InternetExplorerDriver quirks |
|
397
|
|
|
|
|
|
|
if ( $self->{is_wd3} && $self->browser_name eq 'internet explorer' ) { |
|
398
|
|
|
|
|
|
|
delete $params->{ms}; |
|
399
|
|
|
|
|
|
|
delete $params->{type}; |
|
400
|
|
|
|
|
|
|
delete $resource->{payload}->{type}; |
|
401
|
|
|
|
|
|
|
my $oldvalue = delete $params->{'page load'}; |
|
402
|
|
|
|
|
|
|
$params->{pageLoad} = $oldvalue if $oldvalue; |
|
403
|
|
|
|
|
|
|
} |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
if ($resource) { |
|
406
|
|
|
|
|
|
|
$params = {} unless $params; |
|
407
|
|
|
|
|
|
|
my $resp = $self->remote_conn->request( $resource, $params ); |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
#In general, the parse_response for v3 is better, which is why we use it *even if* we are falling back. |
|
410
|
|
|
|
|
|
|
return $self->commands_v3->parse_response( $res, $resp ) |
|
411
|
|
|
|
|
|
|
if $self->{is_wd3}; |
|
412
|
|
|
|
|
|
|
return $self->commands->parse_response( $res, $resp ); |
|
413
|
|
|
|
|
|
|
} |
|
414
|
|
|
|
|
|
|
else { |
|
415
|
|
|
|
|
|
|
#Tell the use about the offending setting. |
|
416
|
|
|
|
|
|
|
croak "Couldn't retrieve command settings properly ".$res->{command}."\n"; |
|
417
|
|
|
|
|
|
|
} |
|
418
|
|
|
|
|
|
|
} |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
sub new_session { |
|
422
|
22
|
|
|
22
|
1
|
69
|
my ( $self, $extra_capabilities ) = @_; |
|
423
|
22
|
|
50
|
|
|
129
|
$extra_capabilities ||= {}; |
|
424
|
|
|
|
|
|
|
|
|
425
|
22
|
|
50
|
|
|
468
|
my $args = { |
|
426
|
|
|
|
|
|
|
'desiredCapabilities' => { |
|
427
|
|
|
|
|
|
|
'browserName' => $self->browser_name, |
|
428
|
|
|
|
|
|
|
'platform' => $self->platform, |
|
429
|
|
|
|
|
|
|
'javascriptEnabled' => $self->javascript, |
|
430
|
|
|
|
|
|
|
'version' => $self->version // '', |
|
431
|
|
|
|
|
|
|
'acceptSslCerts' => $self->accept_ssl_certs, |
|
432
|
|
|
|
|
|
|
%$extra_capabilities, |
|
433
|
|
|
|
|
|
|
}, |
|
434
|
|
|
|
|
|
|
}; |
|
435
|
22
|
100
|
|
|
|
1997
|
$args->{'extra_capabilities'} = \%$extra_capabilities unless $FORCE_WD2; |
|
436
|
|
|
|
|
|
|
|
|
437
|
22
|
100
|
|
|
|
438
|
if ( defined $self->proxy ) { |
|
438
|
2
|
|
|
|
|
44
|
$args->{desiredCapabilities}->{proxy} = $self->proxy; |
|
439
|
|
|
|
|
|
|
} |
|
440
|
|
|
|
|
|
|
|
|
441
|
22
|
50
|
66
|
|
|
388
|
if ( $args->{desiredCapabilities}->{browserName} =~ /firefox/i |
|
442
|
|
|
|
|
|
|
&& $self->has_firefox_profile ) |
|
443
|
|
|
|
|
|
|
{ |
|
444
|
|
|
|
|
|
|
$args->{desiredCapabilities}->{firefox_profile} = |
|
445
|
0
|
|
|
|
|
0
|
$self->firefox_profile->_encode; |
|
446
|
|
|
|
|
|
|
} |
|
447
|
|
|
|
|
|
|
|
|
448
|
22
|
|
|
|
|
104
|
$self->_request_new_session($args); |
|
449
|
|
|
|
|
|
|
} |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
sub new_desired_session { |
|
453
|
5
|
|
|
5
|
1
|
53
|
my ( $self, $caps ) = @_; |
|
454
|
|
|
|
|
|
|
|
|
455
|
5
|
|
|
|
|
32
|
$self->_request_new_session( |
|
456
|
|
|
|
|
|
|
{ |
|
457
|
|
|
|
|
|
|
desiredCapabilities => $caps |
|
458
|
|
|
|
|
|
|
} |
|
459
|
|
|
|
|
|
|
); |
|
460
|
|
|
|
|
|
|
} |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
sub _request_new_session { |
|
463
|
29
|
|
|
29
|
|
2878
|
my ( $self, $args ) = @_; |
|
464
|
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
#XXX UGLY shim for webdriver3 |
|
466
|
|
|
|
|
|
|
$args->{capabilities}->{alwaysMatch} = |
|
467
|
29
|
|
|
|
|
609
|
clone( $args->{desiredCapabilities} ); |
|
468
|
29
|
|
|
|
|
666
|
my $cmap = $self->commands_v3->get_caps_map(); |
|
469
|
29
|
|
|
|
|
799
|
my $caps = $self->commands_v3->get_caps(); |
|
470
|
29
|
|
|
|
|
96
|
foreach my $cap ( keys( %{ $args->{capabilities}->{alwaysMatch} } ) ) { |
|
|
29
|
|
|
|
|
201
|
|
|
471
|
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
#Handle browser specific capabilities |
|
473
|
136
|
100
|
66
|
|
|
1067
|
if ( exists( $args->{desiredCapabilities}->{browserName} ) |
|
474
|
|
|
|
|
|
|
&& $cap eq 'extra_capabilities' ) |
|
475
|
|
|
|
|
|
|
{ |
|
476
|
|
|
|
|
|
|
|
|
477
|
2
|
100
|
|
|
|
8
|
if ( |
|
478
|
|
|
|
|
|
|
exists $args->{capabilities}->{alwaysMatch} |
|
479
|
|
|
|
|
|
|
->{'moz:firefoxOptions'}->{args} ) |
|
480
|
|
|
|
|
|
|
{ |
|
481
|
|
|
|
|
|
|
$args->{capabilities}->{alwaysMatch}->{$cap}->{args} = |
|
482
|
|
|
|
|
|
|
$args->{capabilities}->{alwaysMatch}->{'moz:firefoxOptions'} |
|
483
|
1
|
|
|
|
|
5
|
->{args}; |
|
484
|
|
|
|
|
|
|
} |
|
485
|
|
|
|
|
|
|
$args->{capabilities}->{alwaysMatch}->{'moz:firefoxOptions'} = |
|
486
|
|
|
|
|
|
|
$args->{capabilities}->{alwaysMatch}->{$cap} |
|
487
|
2
|
100
|
|
|
|
8
|
if $args->{desiredCapabilities}->{browserName} eq 'firefox'; |
|
488
|
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
#XXX the chrome documentation is lies, you can't do this yet |
|
490
|
|
|
|
|
|
|
#$args->{capabilities}->{alwaysMatch}->{'goog:chromeOptions'} = $args->{capabilities}->{alwaysMatch}->{$cap} if $args->{desiredCapabilities}->{browserName} eq 'chrome'; |
|
491
|
|
|
|
|
|
|
#Does not appear there are any MSIE based options, so let's just let that be |
|
492
|
|
|
|
|
|
|
} |
|
493
|
136
|
100
|
66
|
|
|
640
|
if ( exists( $args->{desiredCapabilities}->{browserName} ) |
|
|
|
|
100
|
|
|
|
|
|
494
|
|
|
|
|
|
|
&& $args->{desiredCapabilities}->{browserName} eq 'firefox' |
|
495
|
|
|
|
|
|
|
&& $cap eq 'firefox_profile' ) |
|
496
|
|
|
|
|
|
|
{ |
|
497
|
2
|
100
|
|
|
|
11
|
if ( |
|
498
|
|
|
|
|
|
|
ref $args->{capabilities}->{alwaysMatch}->{$cap} eq |
|
499
|
|
|
|
|
|
|
'Selenium::Firefox::Profile' ) |
|
500
|
|
|
|
|
|
|
{ |
|
501
|
|
|
|
|
|
|
#XXX not sure if I need to keep a ref to the File::Temp::Tempdir object to prevent reaping |
|
502
|
|
|
|
|
|
|
$args->{capabilities}->{alwaysMatch}->{'moz:firefoxOptions'} |
|
503
|
|
|
|
|
|
|
->{args} = [ |
|
504
|
|
|
|
|
|
|
'-profile', |
|
505
|
|
|
|
|
|
|
$args->{capabilities}->{alwaysMatch}->{$cap}->{profile_dir} |
|
506
|
|
|
|
|
|
|
->dirname() |
|
507
|
1
|
|
|
|
|
6
|
]; |
|
508
|
|
|
|
|
|
|
} |
|
509
|
|
|
|
|
|
|
} |
|
510
|
136
|
|
|
|
|
382
|
foreach my $newkey ( keys(%$cmap) ) { |
|
511
|
467
|
100
|
|
|
|
929
|
if ( $newkey eq $cap ) { |
|
512
|
103
|
100
|
|
|
|
275
|
last if $cmap->{$newkey} eq $cap; |
|
513
|
|
|
|
|
|
|
$args->{capabilities}->{alwaysMatch}->{ $cmap->{$newkey} } = |
|
514
|
72
|
|
|
|
|
248
|
$args->{capabilities}->{alwaysMatch}->{$cap}; |
|
515
|
72
|
|
|
|
|
166
|
delete $args->{capabilities}->{alwaysMatch}->{$cap}; |
|
516
|
72
|
|
|
|
|
204
|
last; |
|
517
|
|
|
|
|
|
|
} |
|
518
|
|
|
|
|
|
|
} |
|
519
|
|
|
|
|
|
|
delete $args->{capabilities}->{alwaysMatch}->{$cap} |
|
520
|
136
|
100
|
|
1295
|
|
647
|
if !any { $_ eq $cap } @$caps; |
|
|
1295
|
|
|
|
|
2096
|
|
|
521
|
|
|
|
|
|
|
} |
|
522
|
|
|
|
|
|
|
delete $args->{desiredCapabilities} |
|
523
|
29
|
50
|
|
|
|
124
|
if $FORCE_WD3; #XXX fork working-around busted fallback in firefox |
|
524
|
|
|
|
|
|
|
delete $args->{capabilities} |
|
525
|
29
|
100
|
|
|
|
158
|
if $FORCE_WD2; #XXX 'secret' feature to help the legacy unit tests to work |
|
526
|
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
#Delete compatibility layer when using drivers directly |
|
528
|
29
|
50
|
66
|
|
|
580
|
if ( $self->isa('Selenium::Firefox') || $self->isa('Selenium::Chrome') || $self->isa('Selenium::Edge') ) { |
|
|
|
|
66
|
|
|
|
|
|
529
|
2
|
0
|
33
|
|
|
8
|
if ( exists $args->{capabilities} |
|
530
|
|
|
|
|
|
|
&& exists $args->{capabilities}->{alwaysMatch} ) |
|
531
|
|
|
|
|
|
|
{ |
|
532
|
0
|
|
|
|
|
0
|
delete $args->{capabilities}->{alwaysMatch}->{browserName}; |
|
533
|
0
|
|
|
|
|
0
|
delete $args->{capabilities}->{alwaysMatch}->{browserVersion}; |
|
534
|
0
|
|
|
|
|
0
|
delete $args->{capabilities}->{alwaysMatch}->{platformName}; |
|
535
|
|
|
|
|
|
|
} |
|
536
|
|
|
|
|
|
|
} |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
#Fix broken out of the box chrome because they hate the maintainers of their interfaces |
|
539
|
29
|
50
|
|
|
|
168
|
if ( $self->isa('Selenium::Chrome') ) { |
|
540
|
0
|
0
|
|
|
|
0
|
if ( exists $args->{desiredCapabilities} ) { |
|
541
|
0
|
|
0
|
|
|
0
|
$args->{desiredCapabilities}{'goog:chromeOptions'}{args} //= []; |
|
542
|
0
|
|
|
|
|
0
|
push(@{$args->{desiredCapabilities}{'goog:chromeOptions'}{args}}, qw{no-sandbox disable-dev-shm-usage}); |
|
|
0
|
|
|
|
|
0
|
|
|
543
|
|
|
|
|
|
|
} |
|
544
|
|
|
|
|
|
|
} |
|
545
|
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
# Get actual status |
|
547
|
29
|
|
|
|
|
794
|
$self->remote_conn->check_status(); |
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
# command => 'newSession' to fool the tests of commands implemented |
|
550
|
|
|
|
|
|
|
# TODO: rewrite the testing better, this is so fragile. |
|
551
|
28
|
|
|
|
|
646
|
my $resource_new_session = { |
|
552
|
|
|
|
|
|
|
method => $self->commands->get_method('newSession'), |
|
553
|
|
|
|
|
|
|
url => $self->commands->get_url('newSession'), |
|
554
|
|
|
|
|
|
|
no_content_success => |
|
555
|
|
|
|
|
|
|
$self->commands->get_no_content_success('newSession'), |
|
556
|
|
|
|
|
|
|
}; |
|
557
|
28
|
|
|
|
|
724
|
my $rc = $self->remote_conn; |
|
558
|
28
|
|
|
|
|
321
|
my $resp = $rc->request( $resource_new_session, $args, ); |
|
559
|
|
|
|
|
|
|
|
|
560
|
28
|
50
|
33
|
|
|
219
|
if ( $resp->{cmd_status} && $resp->{cmd_status} eq 'NOT OK' ) { |
|
561
|
0
|
|
|
|
|
0
|
croak "Could not obtain new session: ". $resp->{cmd_return}{message}; |
|
562
|
|
|
|
|
|
|
} |
|
563
|
|
|
|
|
|
|
|
|
564
|
28
|
100
|
66
|
|
|
213
|
if ( ( defined $resp->{'sessionId'} ) && $resp->{'sessionId'} ne '' ) { |
|
565
|
27
|
|
|
|
|
137
|
$self->session_id( $resp->{'sessionId'} ); |
|
566
|
|
|
|
|
|
|
} |
|
567
|
|
|
|
|
|
|
else { |
|
568
|
1
|
|
|
|
|
3
|
my $error = 'Could not create new session'; |
|
569
|
|
|
|
|
|
|
|
|
570
|
1
|
50
|
|
|
|
5
|
if ( ref $resp->{cmd_return} eq 'HASH' ) { |
|
571
|
1
|
|
|
|
|
12
|
$error .= ': ' . $resp->{cmd_return}->{message}; |
|
572
|
|
|
|
|
|
|
} |
|
573
|
|
|
|
|
|
|
else { |
|
574
|
0
|
|
|
|
|
0
|
$error .= ': ' . $resp->{cmd_return}; |
|
575
|
|
|
|
|
|
|
} |
|
576
|
1
|
|
|
|
|
196
|
croak $error; |
|
577
|
|
|
|
|
|
|
} |
|
578
|
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
#Webdriver 3 - best guess that this is 'whats goin on' |
|
580
|
27
|
100
|
100
|
|
|
138
|
if ( ref $resp->{cmd_return} eq 'HASH' |
|
581
|
|
|
|
|
|
|
&& $resp->{cmd_return}->{capabilities} ) |
|
582
|
|
|
|
|
|
|
{ |
|
583
|
2
|
|
|
|
|
5
|
$self->{is_wd3} = 1; |
|
584
|
2
|
|
|
|
|
6
|
$self->{emulate_jsonwire} = 1; |
|
585
|
2
|
|
|
|
|
5
|
$self->{capabilities} = $resp->{cmd_return}->{capabilities}; |
|
586
|
|
|
|
|
|
|
} |
|
587
|
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
#XXX chromedriver DOES NOT FOLLOW SPEC! |
|
589
|
27
|
100
|
100
|
|
|
146
|
if ( ref $resp->{cmd_return} eq 'HASH' && $resp->{cmd_return}->{chrome} ) { |
|
590
|
1
|
50
|
|
|
|
5
|
if ( defined $resp->{cmd_return}->{setWindowRect} ) |
|
591
|
|
|
|
|
|
|
{ #XXX i'm inferring we are wd3 based on the presence of this |
|
592
|
0
|
|
|
|
|
0
|
$self->{is_wd3} = 1; |
|
593
|
0
|
|
|
|
|
0
|
$self->{emulate_jsonwire} = 1; |
|
594
|
0
|
|
|
|
|
0
|
$self->{capabilities} = $resp->{cmd_return}; |
|
595
|
|
|
|
|
|
|
} |
|
596
|
|
|
|
|
|
|
} |
|
597
|
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
#XXX unsurprisingly, neither does microsoft |
|
599
|
27
|
50
|
100
|
|
|
324
|
if ( ref $resp->{cmd_return} eq 'HASH' |
|
|
|
|
66
|
|
|
|
|
|
600
|
|
|
|
|
|
|
&& $resp->{cmd_return}->{pageLoadStrategy} |
|
601
|
|
|
|
|
|
|
&& $self->browser_name eq 'MicrosoftEdge' ) |
|
602
|
|
|
|
|
|
|
{ |
|
603
|
0
|
|
|
|
|
0
|
$self->{is_wd3} = 1; |
|
604
|
0
|
|
|
|
|
0
|
$self->{emulate_jsonwire} = 1; |
|
605
|
0
|
|
|
|
|
0
|
$self->{capabilities} = $resp->{cmd_return}; |
|
606
|
|
|
|
|
|
|
} |
|
607
|
|
|
|
|
|
|
|
|
608
|
27
|
|
|
|
|
289
|
return ( $args, $resp ); |
|
609
|
|
|
|
|
|
|
} |
|
610
|
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
sub is_webdriver_3 { |
|
613
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
614
|
0
|
|
|
|
|
0
|
return $self->{is_wd3}; |
|
615
|
|
|
|
|
|
|
} |
|
616
|
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
sub debug_on { |
|
619
|
1
|
|
|
1
|
1
|
596
|
my ($self) = @_; |
|
620
|
1
|
|
|
|
|
3
|
$self->{debug} = 1; |
|
621
|
1
|
|
|
|
|
27
|
$self->remote_conn->debug(1); |
|
622
|
|
|
|
|
|
|
} |
|
623
|
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
sub debug_off { |
|
626
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_; |
|
627
|
0
|
|
|
|
|
0
|
$self->{debug} = 0; |
|
628
|
0
|
|
|
|
|
0
|
$self->remote_conn->debug(0); |
|
629
|
|
|
|
|
|
|
} |
|
630
|
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
sub get_sessions { |
|
633
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_; |
|
634
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'getSessions' }; |
|
635
|
0
|
|
|
|
|
0
|
return $self->_execute_command($res); |
|
636
|
|
|
|
|
|
|
} |
|
637
|
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
sub status { |
|
640
|
1
|
|
|
1
|
1
|
681
|
my ($self) = @_; |
|
641
|
1
|
|
|
|
|
6
|
my $res = { 'command' => 'status' }; |
|
642
|
1
|
|
|
|
|
27
|
return $self->_execute_command($res); |
|
643
|
|
|
|
|
|
|
} |
|
644
|
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
sub get_alert_text { |
|
647
|
6
|
|
|
6
|
1
|
39
|
my ($self) = @_; |
|
648
|
6
|
|
|
|
|
19
|
my $res = { 'command' => 'getAlertText' }; |
|
649
|
6
|
|
|
|
|
145
|
return $self->_execute_command($res); |
|
650
|
|
|
|
|
|
|
} |
|
651
|
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
sub send_keys_to_active_element { |
|
654
|
0
|
|
|
0
|
1
|
0
|
my ( $self, @strings ) = @_; |
|
655
|
|
|
|
|
|
|
|
|
656
|
0
|
0
|
0
|
|
|
0
|
if ( $self->{is_wd3} |
|
657
|
0
|
|
|
|
|
0
|
&& !( grep { $self->browser_name eq $_ } qw{MicrosoftEdge} ) ) |
|
658
|
|
|
|
|
|
|
{ |
|
659
|
0
|
|
|
|
|
0
|
@strings = map { split( '', $_ ) } @strings; |
|
|
0
|
|
|
|
|
0
|
|
|
660
|
|
|
|
|
|
|
my @acts = map { |
|
661
|
0
|
|
|
|
|
0
|
( |
|
662
|
|
|
|
|
|
|
{ |
|
663
|
0
|
|
|
|
|
0
|
type => 'keyDown', |
|
664
|
|
|
|
|
|
|
value => $_, |
|
665
|
|
|
|
|
|
|
}, |
|
666
|
|
|
|
|
|
|
{ |
|
667
|
|
|
|
|
|
|
type => 'keyUp', |
|
668
|
|
|
|
|
|
|
value => $_, |
|
669
|
|
|
|
|
|
|
} |
|
670
|
|
|
|
|
|
|
) |
|
671
|
|
|
|
|
|
|
} @strings; |
|
672
|
|
|
|
|
|
|
|
|
673
|
0
|
|
|
|
|
0
|
my $action = { |
|
674
|
|
|
|
|
|
|
actions => [ |
|
675
|
|
|
|
|
|
|
{ |
|
676
|
|
|
|
|
|
|
id => 'key', |
|
677
|
|
|
|
|
|
|
type => 'key', |
|
678
|
|
|
|
|
|
|
actions => \@acts, |
|
679
|
|
|
|
|
|
|
} |
|
680
|
|
|
|
|
|
|
] |
|
681
|
|
|
|
|
|
|
}; |
|
682
|
0
|
|
|
|
|
0
|
return $self->general_action(%$action); |
|
683
|
|
|
|
|
|
|
} |
|
684
|
|
|
|
|
|
|
|
|
685
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'sendKeysToActiveElement' }; |
|
686
|
0
|
|
|
|
|
0
|
my $params = { 'value' => \@strings, }; |
|
687
|
0
|
|
|
|
|
0
|
return $self->_execute_command( $res, $params ); |
|
688
|
|
|
|
|
|
|
} |
|
689
|
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
sub send_keys_to_alert { |
|
692
|
0
|
|
|
0
|
1
|
0
|
return shift->send_keys_to_prompt(@_); |
|
693
|
|
|
|
|
|
|
} |
|
694
|
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
|
|
696
|
|
|
|
|
|
|
sub send_keys_to_prompt { |
|
697
|
1
|
|
|
1
|
1
|
4
|
my ( $self, $keys ) = @_; |
|
698
|
1
|
|
|
|
|
5
|
my $res = { 'command' => 'sendKeysToPrompt' }; |
|
699
|
1
|
|
|
|
|
4
|
my $params = { 'text' => $keys }; |
|
700
|
1
|
|
|
|
|
30
|
return $self->_execute_command( $res, $params ); |
|
701
|
|
|
|
|
|
|
} |
|
702
|
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
sub accept_alert { |
|
705
|
6
|
|
|
6
|
1
|
977
|
my ($self) = @_; |
|
706
|
6
|
|
|
|
|
20
|
my $res = { 'command' => 'acceptAlert' }; |
|
707
|
6
|
|
|
|
|
182
|
return $self->_execute_command($res); |
|
708
|
|
|
|
|
|
|
} |
|
709
|
|
|
|
|
|
|
|
|
710
|
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
sub dismiss_alert { |
|
712
|
3
|
|
|
3
|
1
|
8
|
my ($self) = @_; |
|
713
|
3
|
|
|
|
|
11
|
my $res = { 'command' => 'dismissAlert' }; |
|
714
|
3
|
|
|
|
|
78
|
return $self->_execute_command($res); |
|
715
|
|
|
|
|
|
|
} |
|
716
|
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
sub general_action { |
|
719
|
0
|
|
|
0
|
1
|
0
|
my ( $self, %action ) = @_; |
|
720
|
|
|
|
|
|
|
|
|
721
|
0
|
|
|
|
|
0
|
_queue_action(%action); |
|
722
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'generalAction' }; |
|
723
|
0
|
|
|
|
|
0
|
my $out = $self->_execute_command( $res, \%CURRENT_ACTION_CHAIN ); |
|
724
|
0
|
|
|
|
|
0
|
%CURRENT_ACTION_CHAIN = ( actions => [] ); |
|
725
|
0
|
|
|
|
|
0
|
return $out; |
|
726
|
|
|
|
|
|
|
} |
|
727
|
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
sub _queue_action { |
|
729
|
0
|
|
|
0
|
|
0
|
my (%action) = @_; |
|
730
|
0
|
0
|
|
|
|
0
|
if ( ref $action{actions} eq 'ARRAY' ) { |
|
731
|
0
|
|
|
|
|
0
|
foreach my $live_action ( @{ $action{actions} } ) { |
|
|
0
|
|
|
|
|
0
|
|
|
732
|
0
|
|
|
|
|
0
|
my $existing_action; |
|
733
|
0
|
|
|
|
|
0
|
foreach my $global_action ( @{ $CURRENT_ACTION_CHAIN{actions} } ) { |
|
|
0
|
|
|
|
|
0
|
|
|
734
|
0
|
0
|
|
|
|
0
|
if ( $global_action->{id} eq $live_action->{id} ) { |
|
735
|
0
|
|
|
|
|
0
|
$existing_action = $global_action; |
|
736
|
0
|
|
|
|
|
0
|
last; |
|
737
|
|
|
|
|
|
|
} |
|
738
|
|
|
|
|
|
|
} |
|
739
|
0
|
0
|
|
|
|
0
|
if ($existing_action) { |
|
740
|
|
|
|
|
|
|
push( |
|
741
|
0
|
|
|
|
|
0
|
@{ $existing_action->{actions} }, |
|
742
|
0
|
|
|
|
|
0
|
@{ $live_action->{actions} } |
|
|
0
|
|
|
|
|
0
|
|
|
743
|
|
|
|
|
|
|
); |
|
744
|
|
|
|
|
|
|
} |
|
745
|
|
|
|
|
|
|
else { |
|
746
|
0
|
|
|
|
|
0
|
push( @{ $CURRENT_ACTION_CHAIN{actions} }, $live_action ); |
|
|
0
|
|
|
|
|
0
|
|
|
747
|
|
|
|
|
|
|
} |
|
748
|
|
|
|
|
|
|
} |
|
749
|
|
|
|
|
|
|
} |
|
750
|
|
|
|
|
|
|
} |
|
751
|
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
sub release_general_action { |
|
754
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_; |
|
755
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'releaseGeneralAction' }; |
|
756
|
0
|
|
|
|
|
0
|
%CURRENT_ACTION_CHAIN = ( actions => [] ); |
|
757
|
0
|
|
|
|
|
0
|
return $self->_execute_command($res); |
|
758
|
|
|
|
|
|
|
} |
|
759
|
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
sub mouse_move_to_location { |
|
762
|
0
|
|
|
0
|
1
|
0
|
my ( $self, %params ) = @_; |
|
763
|
0
|
0
|
|
|
|
0
|
$params{element} = $params{element}{id} if exists $params{element}; |
|
764
|
|
|
|
|
|
|
|
|
765
|
0
|
0
|
0
|
|
|
0
|
if ( $self->{is_wd3} |
|
766
|
0
|
|
|
|
|
0
|
&& !( grep { $self->browser_name eq $_ } qw{MicrosoftEdge} ) ) |
|
767
|
|
|
|
|
|
|
{ |
|
768
|
0
|
|
|
|
|
0
|
my $origin = $params{element}; |
|
769
|
|
|
|
|
|
|
my $move_action = { |
|
770
|
|
|
|
|
|
|
type => "pointerMove", |
|
771
|
|
|
|
|
|
|
duration => 0, |
|
772
|
|
|
|
|
|
|
x => $params{xoffset} // 0, |
|
773
|
0
|
|
0
|
|
|
0
|
y => $params{yoffset} // 0, |
|
|
|
|
0
|
|
|
|
|
|
774
|
|
|
|
|
|
|
}; |
|
775
|
|
|
|
|
|
|
$move_action->{origin} = |
|
776
|
0
|
0
|
|
|
|
0
|
{ 'element-6066-11e4-a52e-4f735466cecf' => $origin } |
|
777
|
|
|
|
|
|
|
if $origin; |
|
778
|
|
|
|
|
|
|
|
|
779
|
0
|
|
|
|
|
0
|
_queue_action( |
|
780
|
|
|
|
|
|
|
actions => [ |
|
781
|
|
|
|
|
|
|
{ |
|
782
|
|
|
|
|
|
|
type => "pointer", |
|
783
|
|
|
|
|
|
|
id => 'mouse', |
|
784
|
|
|
|
|
|
|
"parameters" => { "pointerType" => "mouse" }, |
|
785
|
|
|
|
|
|
|
actions => [$move_action], |
|
786
|
|
|
|
|
|
|
} |
|
787
|
|
|
|
|
|
|
] |
|
788
|
|
|
|
|
|
|
); |
|
789
|
0
|
|
|
|
|
0
|
return 1; |
|
790
|
|
|
|
|
|
|
} |
|
791
|
|
|
|
|
|
|
|
|
792
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'mouseMoveToLocation' }; |
|
793
|
0
|
|
|
|
|
0
|
return $self->_execute_command( $res, \%params ); |
|
794
|
|
|
|
|
|
|
} |
|
795
|
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
sub move_to { |
|
798
|
0
|
|
|
0
|
1
|
0
|
return shift->mouse_move_to_location(@_); |
|
799
|
|
|
|
|
|
|
} |
|
800
|
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
sub get_capabilities { |
|
803
|
1
|
|
|
1
|
1
|
1347
|
my $self = shift; |
|
804
|
1
|
|
|
|
|
5
|
my $res = { 'command' => 'getCapabilities' }; |
|
805
|
1
|
|
|
|
|
29
|
return $self->_execute_command($res); |
|
806
|
|
|
|
|
|
|
} |
|
807
|
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
sub get_timeouts { |
|
810
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
811
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'getTimeouts' }; |
|
812
|
0
|
|
|
|
|
0
|
return $self->_execute_command( $res, {} ); |
|
813
|
|
|
|
|
|
|
} |
|
814
|
|
|
|
|
|
|
|
|
815
|
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
sub set_timeout { |
|
817
|
1
|
|
|
1
|
1
|
405
|
my ( $self, $type, $ms ) = @_; |
|
818
|
1
|
50
|
|
|
|
6
|
if ( not defined $type ) { |
|
819
|
0
|
|
|
|
|
0
|
croak "Expecting type"; |
|
820
|
|
|
|
|
|
|
} |
|
821
|
1
|
|
|
|
|
5
|
$ms = _coerce_timeout_ms($ms); |
|
822
|
0
|
0
|
0
|
|
|
0
|
$type = 'pageLoad' |
|
823
|
|
|
|
|
|
|
if $type eq 'page load' |
|
824
|
|
|
|
|
|
|
&& $self->browser_name ne |
|
825
|
|
|
|
|
|
|
'MicrosoftEdge'; #XXX SHIM they changed the WC3 standard mid stream |
|
826
|
|
|
|
|
|
|
|
|
827
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'setTimeout' }; |
|
828
|
0
|
|
|
|
|
0
|
my $params = { $type => $ms }; |
|
829
|
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
#XXX edge still follows earlier versions of the WC3 standard |
|
831
|
0
|
0
|
|
|
|
0
|
if ( $self->browser_name eq 'MicrosoftEdge' ) { |
|
832
|
0
|
|
|
|
|
0
|
$params->{ms} = $ms; |
|
833
|
0
|
|
|
|
|
0
|
$params->{type} = $type; |
|
834
|
|
|
|
|
|
|
} |
|
835
|
0
|
|
|
|
|
0
|
return $self->_execute_command( $res, $params ); |
|
836
|
|
|
|
|
|
|
} |
|
837
|
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
sub set_async_script_timeout { |
|
840
|
1
|
|
|
1
|
1
|
396
|
my ( $self, $ms ) = @_; |
|
841
|
|
|
|
|
|
|
|
|
842
|
1
|
50
|
|
|
|
6
|
return $self->set_timeout( 'script', $ms ) if $self->{is_wd3}; |
|
843
|
|
|
|
|
|
|
|
|
844
|
1
|
|
|
|
|
3
|
$ms = _coerce_timeout_ms($ms); |
|
845
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'setAsyncScriptTimeout' }; |
|
846
|
0
|
|
|
|
|
0
|
my $params = { 'ms' => $ms }; |
|
847
|
0
|
|
|
|
|
0
|
return $self->_execute_command( $res, $params ); |
|
848
|
|
|
|
|
|
|
} |
|
849
|
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
sub set_implicit_wait_timeout { |
|
852
|
3
|
|
|
3
|
1
|
1012
|
my ( $self, $ms ) = @_; |
|
853
|
3
|
50
|
|
|
|
11
|
return $self->set_timeout( 'implicit', $ms ) if $self->{is_wd3}; |
|
854
|
|
|
|
|
|
|
|
|
855
|
3
|
|
|
|
|
12
|
$ms = _coerce_timeout_ms($ms); |
|
856
|
2
|
|
|
|
|
8
|
my $res = { 'command' => 'setImplicitWaitTimeout' }; |
|
857
|
2
|
|
|
|
|
6
|
my $params = { 'ms' => $ms }; |
|
858
|
2
|
|
|
|
|
57
|
return $self->_execute_command( $res, $params ); |
|
859
|
|
|
|
|
|
|
} |
|
860
|
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
sub pause { |
|
863
|
1
|
|
|
1
|
1
|
13
|
my $self = shift; |
|
864
|
1
|
|
50
|
|
|
9
|
my $timeout = ( shift // 1000 ) * 1000; |
|
865
|
1
|
|
|
|
|
1000507
|
usleep($timeout); |
|
866
|
|
|
|
|
|
|
} |
|
867
|
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
sub close { |
|
870
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
871
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'close' }; |
|
872
|
0
|
|
|
|
|
0
|
$self->_execute_command($res); |
|
873
|
|
|
|
|
|
|
} |
|
874
|
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
sub quit { |
|
877
|
27
|
|
|
27
|
1
|
4593
|
my $self = shift; |
|
878
|
27
|
|
|
|
|
97
|
my $res = { 'command' => 'quit' }; |
|
879
|
27
|
|
|
|
|
561
|
$self->_execute_command($res); |
|
880
|
27
|
|
|
|
|
496
|
$self->session_id(undef); |
|
881
|
|
|
|
|
|
|
} |
|
882
|
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
sub get_current_window_handle { |
|
885
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
886
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'getCurrentWindowHandle' }; |
|
887
|
0
|
|
|
|
|
0
|
return $self->_execute_command($res); |
|
888
|
|
|
|
|
|
|
} |
|
889
|
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
sub get_window_handles { |
|
892
|
2
|
|
|
2
|
1
|
1553
|
my $self = shift; |
|
893
|
2
|
|
|
|
|
8
|
my $res = { 'command' => 'getWindowHandles' }; |
|
894
|
2
|
|
|
|
|
54
|
return $self->_execute_command($res); |
|
895
|
|
|
|
|
|
|
} |
|
896
|
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
sub get_window_size { |
|
899
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $window ) = @_; |
|
900
|
0
|
0
|
|
|
|
0
|
$window = ( defined $window ) ? $window : 'current'; |
|
901
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'getWindowSize', 'window_handle' => $window }; |
|
902
|
|
|
|
|
|
|
$res = { 'command' => 'getWindowRect', handle => $window } |
|
903
|
0
|
0
|
|
|
|
0
|
if $self->{is_wd3}; |
|
904
|
0
|
|
|
|
|
0
|
return $self->_execute_command($res); |
|
905
|
|
|
|
|
|
|
} |
|
906
|
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
|
|
908
|
|
|
|
|
|
|
sub get_window_position { |
|
909
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $window ) = @_; |
|
910
|
0
|
0
|
|
|
|
0
|
$window = ( defined $window ) ? $window : 'current'; |
|
911
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'getWindowPosition', 'window_handle' => $window }; |
|
912
|
|
|
|
|
|
|
$res = { 'command' => 'getWindowRect', handle => $window } |
|
913
|
0
|
0
|
|
|
|
0
|
if $self->{is_wd3}; |
|
914
|
0
|
|
|
|
|
0
|
return $self->_execute_command($res); |
|
915
|
|
|
|
|
|
|
} |
|
916
|
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
sub get_current_url { |
|
919
|
2
|
|
|
2
|
1
|
602
|
my $self = shift; |
|
920
|
2
|
|
|
|
|
9
|
my $res = { 'command' => 'getCurrentUrl' }; |
|
921
|
2
|
|
|
|
|
50
|
return $self->_execute_command($res); |
|
922
|
|
|
|
|
|
|
} |
|
923
|
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
sub navigate { |
|
926
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $url ) = @_; |
|
927
|
0
|
|
|
|
|
0
|
$self->get($url); |
|
928
|
|
|
|
|
|
|
} |
|
929
|
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
sub get { |
|
932
|
28
|
|
|
28
|
1
|
10054
|
my ( $self, $url ) = @_; |
|
933
|
|
|
|
|
|
|
|
|
934
|
28
|
100
|
100
|
|
|
178
|
if ( $self->has_base_url && $url !~ m|://| ) { |
|
935
|
5
|
|
|
|
|
18
|
$url =~ s|^/||; |
|
936
|
5
|
|
|
|
|
91
|
$url = $self->base_url . "/" . $url; |
|
937
|
|
|
|
|
|
|
} |
|
938
|
|
|
|
|
|
|
|
|
939
|
28
|
|
|
|
|
134
|
my $res = { 'command' => 'get' }; |
|
940
|
28
|
|
|
|
|
89
|
my $params = { 'url' => $url }; |
|
941
|
28
|
|
|
|
|
647
|
return $self->_execute_command( $res, $params ); |
|
942
|
|
|
|
|
|
|
} |
|
943
|
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
|
|
945
|
|
|
|
|
|
|
sub get_title { |
|
946
|
14
|
|
|
14
|
1
|
1902
|
my $self = shift; |
|
947
|
14
|
|
|
|
|
53
|
my $res = { 'command' => 'getTitle' }; |
|
948
|
14
|
|
|
|
|
362
|
return $self->_execute_command($res); |
|
949
|
|
|
|
|
|
|
} |
|
950
|
|
|
|
|
|
|
|
|
951
|
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
sub go_back { |
|
953
|
2
|
|
|
2
|
1
|
762
|
my $self = shift; |
|
954
|
2
|
|
|
|
|
6
|
my $res = { 'command' => 'goBack' }; |
|
955
|
2
|
|
|
|
|
50
|
return $self->_execute_command($res); |
|
956
|
|
|
|
|
|
|
} |
|
957
|
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
sub go_forward { |
|
960
|
1
|
|
|
1
|
1
|
627
|
my $self = shift; |
|
961
|
1
|
|
|
|
|
4
|
my $res = { 'command' => 'goForward' }; |
|
962
|
1
|
|
|
|
|
28
|
return $self->_execute_command($res); |
|
963
|
|
|
|
|
|
|
} |
|
964
|
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
sub refresh { |
|
967
|
1
|
|
|
1
|
1
|
559
|
my $self = shift; |
|
968
|
1
|
|
|
|
|
5
|
my $res = { 'command' => 'refresh' }; |
|
969
|
1
|
|
|
|
|
27
|
return $self->_execute_command($res); |
|
970
|
|
|
|
|
|
|
} |
|
971
|
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
sub has_javascript { |
|
974
|
10
|
|
|
10
|
1
|
23
|
my $self = shift; |
|
975
|
10
|
|
|
|
|
230
|
return int( $self->javascript ); |
|
976
|
|
|
|
|
|
|
} |
|
977
|
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
sub execute_async_script { |
|
980
|
2
|
|
|
2
|
1
|
2610
|
my ( $self, $script, @args ) = @_; |
|
981
|
2
|
50
|
|
|
|
9
|
if ( $self->has_javascript ) { |
|
982
|
2
|
50
|
|
|
|
34
|
if ( not defined $script ) { |
|
983
|
0
|
|
|
|
|
0
|
croak 'No script provided'; |
|
984
|
|
|
|
|
|
|
} |
|
985
|
2
|
|
|
|
|
41
|
my $res = |
|
986
|
|
|
|
|
|
|
{ 'command' => 'executeAsyncScript' . $self->_execute_script_suffix }; |
|
987
|
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
# Check the args array if the elem obj is provided & replace it with |
|
989
|
|
|
|
|
|
|
# JSON representation |
|
990
|
2
|
|
|
|
|
27
|
for ( my $i = 0 ; $i < @args ; $i++ ) { |
|
991
|
2
|
100
|
66
|
|
|
21
|
if ( Scalar::Util::blessed( $args[$i] ) |
|
992
|
|
|
|
|
|
|
and $args[$i]->isa('Selenium::Remote::WebElement') ) |
|
993
|
|
|
|
|
|
|
{ |
|
994
|
1
|
50
|
|
|
|
4
|
if ( $self->{is_wd3} ) { |
|
995
|
|
|
|
|
|
|
$args[$i] = |
|
996
|
|
|
|
|
|
|
{ 'element-6066-11e4-a52e-4f735466cecf' => |
|
997
|
0
|
|
|
|
|
0
|
( $args[$i] )->{id} }; |
|
998
|
|
|
|
|
|
|
} |
|
999
|
|
|
|
|
|
|
else { |
|
1000
|
1
|
|
|
|
|
6
|
$args[$i] = { 'ELEMENT' => ( $args[$i] )->{id} }; |
|
1001
|
|
|
|
|
|
|
} |
|
1002
|
|
|
|
|
|
|
} |
|
1003
|
|
|
|
|
|
|
} |
|
1004
|
|
|
|
|
|
|
|
|
1005
|
2
|
|
|
|
|
9
|
my $params = { 'script' => $script, 'args' => \@args }; |
|
1006
|
2
|
|
|
|
|
45
|
my $ret = $self->_execute_command( $res, $params ); |
|
1007
|
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
# replace any ELEMENTS with WebElement |
|
1009
|
2
|
50
|
33
|
|
|
57
|
if ( ref($ret) |
|
|
|
|
33
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
and ( ref($ret) eq 'HASH' ) |
|
1011
|
|
|
|
|
|
|
and $self->_looks_like_element($ret) ) |
|
1012
|
|
|
|
|
|
|
{ |
|
1013
|
2
|
|
|
|
|
57
|
$ret = $self->webelement_class->new( |
|
1014
|
|
|
|
|
|
|
id => $ret, |
|
1015
|
|
|
|
|
|
|
driver => $self |
|
1016
|
|
|
|
|
|
|
); |
|
1017
|
|
|
|
|
|
|
} |
|
1018
|
2
|
|
|
|
|
27
|
return $ret; |
|
1019
|
|
|
|
|
|
|
} |
|
1020
|
|
|
|
|
|
|
else { |
|
1021
|
0
|
|
|
|
|
0
|
croak 'Javascript is not enabled on remote driver instance.'; |
|
1022
|
|
|
|
|
|
|
} |
|
1023
|
|
|
|
|
|
|
} |
|
1024
|
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
sub execute_script { |
|
1027
|
8
|
|
|
8
|
1
|
1367
|
my ( $self, $script, @args ) = @_; |
|
1028
|
8
|
50
|
|
|
|
42
|
if ( $self->has_javascript ) { |
|
1029
|
8
|
50
|
|
|
|
137
|
if ( not defined $script ) { |
|
1030
|
0
|
|
|
|
|
0
|
croak 'No script provided'; |
|
1031
|
|
|
|
|
|
|
} |
|
1032
|
8
|
|
|
|
|
151
|
my $res = |
|
1033
|
|
|
|
|
|
|
{ 'command' => 'executeScript' . $self->_execute_script_suffix }; |
|
1034
|
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
# Check the args array if the elem obj is provided & replace it with |
|
1036
|
|
|
|
|
|
|
# JSON representation |
|
1037
|
8
|
|
|
|
|
120
|
for ( my $i = 0 ; $i < @args ; $i++ ) { |
|
1038
|
2
|
100
|
66
|
|
|
20
|
if ( Scalar::Util::blessed( $args[$i] ) |
|
1039
|
|
|
|
|
|
|
and $args[$i]->isa('Selenium::Remote::WebElement') ) |
|
1040
|
|
|
|
|
|
|
{ |
|
1041
|
1
|
50
|
|
|
|
6
|
if ( $self->{is_wd3} ) { |
|
1042
|
|
|
|
|
|
|
$args[$i] = |
|
1043
|
|
|
|
|
|
|
{ 'element-6066-11e4-a52e-4f735466cecf' => |
|
1044
|
0
|
|
|
|
|
0
|
( $args[$i] )->{id} }; |
|
1045
|
|
|
|
|
|
|
} |
|
1046
|
|
|
|
|
|
|
else { |
|
1047
|
1
|
|
|
|
|
6
|
$args[$i] = { 'ELEMENT' => ( $args[$i] )->{id} }; |
|
1048
|
|
|
|
|
|
|
} |
|
1049
|
|
|
|
|
|
|
} |
|
1050
|
|
|
|
|
|
|
} |
|
1051
|
|
|
|
|
|
|
|
|
1052
|
8
|
|
|
|
|
38
|
my $params = { 'script' => $script, 'args' => [@args] }; |
|
1053
|
8
|
|
|
|
|
163
|
my $ret = $self->_execute_command( $res, $params ); |
|
1054
|
|
|
|
|
|
|
|
|
1055
|
8
|
|
|
|
|
46
|
return $self->_convert_to_webelement($ret); |
|
1056
|
|
|
|
|
|
|
} |
|
1057
|
|
|
|
|
|
|
else { |
|
1058
|
0
|
|
|
|
|
0
|
croak 'Javascript is not enabled on remote driver instance.'; |
|
1059
|
|
|
|
|
|
|
} |
|
1060
|
|
|
|
|
|
|
} |
|
1061
|
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
# _looks_like_element |
|
1063
|
|
|
|
|
|
|
# An internal method to check if a return value might be an element |
|
1064
|
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
sub _looks_like_element { |
|
1066
|
5
|
|
|
5
|
|
15
|
my ( $self, $maybe_element ) = @_; |
|
1067
|
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
return ( |
|
1069
|
|
|
|
|
|
|
exists $maybe_element->{ELEMENT} |
|
1070
|
5
|
|
33
|
|
|
25
|
or exists $maybe_element->{'element-6066-11e4-a52e-4f735466cecf'} |
|
1071
|
|
|
|
|
|
|
); |
|
1072
|
|
|
|
|
|
|
} |
|
1073
|
|
|
|
|
|
|
|
|
1074
|
|
|
|
|
|
|
# _convert_to_webelement |
|
1075
|
|
|
|
|
|
|
# An internal method used to traverse a data structure |
|
1076
|
|
|
|
|
|
|
# and convert any ELEMENTS with WebElements |
|
1077
|
|
|
|
|
|
|
|
|
1078
|
|
|
|
|
|
|
sub _convert_to_webelement { |
|
1079
|
9
|
|
|
9
|
|
28
|
my ( $self, $ret ) = @_; |
|
1080
|
|
|
|
|
|
|
|
|
1081
|
9
|
100
|
100
|
|
|
58
|
if ( ref($ret) and ( ref($ret) eq 'HASH' ) ) { |
|
1082
|
3
|
50
|
|
|
|
15
|
if ( $self->_looks_like_element($ret) ) { |
|
1083
|
|
|
|
|
|
|
|
|
1084
|
|
|
|
|
|
|
# replace an ELEMENT with WebElement |
|
1085
|
3
|
|
|
|
|
72
|
return $self->webelement_class->new( |
|
1086
|
|
|
|
|
|
|
id => $ret, |
|
1087
|
|
|
|
|
|
|
driver => $self |
|
1088
|
|
|
|
|
|
|
); |
|
1089
|
|
|
|
|
|
|
} |
|
1090
|
|
|
|
|
|
|
|
|
1091
|
0
|
|
|
|
|
0
|
my %hash; |
|
1092
|
0
|
|
|
|
|
0
|
foreach my $key ( keys %$ret ) { |
|
1093
|
0
|
|
|
|
|
0
|
$hash{$key} = $self->_convert_to_webelement( $ret->{$key} ); |
|
1094
|
|
|
|
|
|
|
} |
|
1095
|
0
|
|
|
|
|
0
|
return \%hash; |
|
1096
|
|
|
|
|
|
|
} |
|
1097
|
|
|
|
|
|
|
|
|
1098
|
6
|
100
|
66
|
|
|
33
|
if ( ref($ret) and ( ref($ret) eq 'ARRAY' ) ) { |
|
1099
|
1
|
|
|
|
|
5
|
my @array = map { $self->_convert_to_webelement($_) } @$ret; |
|
|
1
|
|
|
|
|
7
|
|
|
1100
|
1
|
|
|
|
|
26
|
return \@array; |
|
1101
|
|
|
|
|
|
|
} |
|
1102
|
|
|
|
|
|
|
|
|
1103
|
5
|
|
|
|
|
28
|
return $ret; |
|
1104
|
|
|
|
|
|
|
} |
|
1105
|
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
|
|
1107
|
|
|
|
|
|
|
sub screenshot { |
|
1108
|
0
|
|
|
0
|
1
|
0
|
my ($self, $params) = @_; |
|
1109
|
0
|
|
0
|
|
|
0
|
$params //= { full => 0 }; |
|
1110
|
|
|
|
|
|
|
|
|
1111
|
0
|
0
|
0
|
|
|
0
|
croak "Full page screenshot only supported on geckodriver" if $params->{full} && ( $self->{browser_name} ne 'firefox' ); |
|
1112
|
|
|
|
|
|
|
|
|
1113
|
0
|
0
|
|
|
|
0
|
my $res = { 'command' => $params->{'full'} == 1 ? 'mozScreenshotFull' : 'screenshot' }; |
|
1114
|
0
|
|
|
|
|
0
|
return $self->_execute_command($res); |
|
1115
|
|
|
|
|
|
|
} |
|
1116
|
|
|
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
sub capture_screenshot { |
|
1119
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $filename, $params ) = @_; |
|
1120
|
0
|
0
|
|
|
|
0
|
croak '$filename is required' unless $filename; |
|
1121
|
|
|
|
|
|
|
|
|
1122
|
0
|
|
|
|
|
0
|
open( my $fh, '>', $filename ); |
|
1123
|
0
|
|
|
|
|
0
|
binmode $fh; |
|
1124
|
0
|
|
|
|
|
0
|
print $fh MIME::Base64::decode_base64( $self->screenshot($params) ); |
|
1125
|
0
|
|
|
|
|
0
|
CORE::close $fh; |
|
1126
|
0
|
|
|
|
|
0
|
return 1; |
|
1127
|
|
|
|
|
|
|
} |
|
1128
|
|
|
|
|
|
|
|
|
1129
|
|
|
|
|
|
|
|
|
1130
|
|
|
|
|
|
|
#TODO emulate behavior on wd3? |
|
1131
|
|
|
|
|
|
|
#grep { eval { Selenium::Remote::Driver->new( browser => $_ ) } } (qw{firefox MicrosoftEdge chrome opera safari htmlunit iphone phantomjs},'internet_explorer'); |
|
1132
|
|
|
|
|
|
|
#might do the trick |
|
1133
|
|
|
|
|
|
|
sub available_engines { |
|
1134
|
1
|
|
|
1
|
1
|
831
|
my ($self) = @_; |
|
1135
|
1
|
|
|
|
|
5
|
my $res = { 'command' => 'availableEngines' }; |
|
1136
|
1
|
|
|
|
|
29
|
return $self->_execute_command($res); |
|
1137
|
|
|
|
|
|
|
} |
|
1138
|
|
|
|
|
|
|
|
|
1139
|
|
|
|
|
|
|
|
|
1140
|
|
|
|
|
|
|
sub switch_to_frame { |
|
1141
|
1
|
|
|
1
|
1
|
9
|
my ( $self, $id ) = @_; |
|
1142
|
|
|
|
|
|
|
|
|
1143
|
1
|
|
|
|
|
6
|
my $json_null = JSON::null; |
|
1144
|
1
|
|
|
|
|
7
|
my $params; |
|
1145
|
1
|
50
|
|
|
|
5
|
$id = ( defined $id ) ? $id : $json_null; |
|
1146
|
|
|
|
|
|
|
|
|
1147
|
1
|
|
|
|
|
3
|
my $res = { 'command' => 'switchToFrame' }; |
|
1148
|
|
|
|
|
|
|
|
|
1149
|
1
|
50
|
|
|
|
9
|
if ( ref $id eq $self->webelement_class ) { |
|
1150
|
0
|
0
|
|
|
|
0
|
if ( $self->{is_wd3} ) { |
|
1151
|
|
|
|
|
|
|
$params = |
|
1152
|
|
|
|
|
|
|
{ 'id' => |
|
1153
|
0
|
|
|
|
|
0
|
{ 'element-6066-11e4-a52e-4f735466cecf' => $id->{'id'} } }; |
|
1154
|
|
|
|
|
|
|
} |
|
1155
|
|
|
|
|
|
|
else { |
|
1156
|
0
|
|
|
|
|
0
|
$params = { 'id' => { 'ELEMENT' => $id->{'id'} } }; |
|
1157
|
|
|
|
|
|
|
} |
|
1158
|
|
|
|
|
|
|
} |
|
1159
|
|
|
|
|
|
|
else { |
|
1160
|
1
|
|
|
|
|
4
|
$params = { 'id' => $id }; |
|
1161
|
|
|
|
|
|
|
} |
|
1162
|
1
|
|
|
|
|
23
|
return $self->_execute_command( $res, $params ); |
|
1163
|
|
|
|
|
|
|
} |
|
1164
|
|
|
|
|
|
|
|
|
1165
|
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
sub switch_to_parent_frame { |
|
1167
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_; |
|
1168
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'switchToParentFrame' }; |
|
1169
|
0
|
|
|
|
|
0
|
return $self->_execute_command($res); |
|
1170
|
|
|
|
|
|
|
} |
|
1171
|
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
|
|
1173
|
|
|
|
|
|
|
sub switch_to_window { |
|
1174
|
4
|
|
|
4
|
1
|
2827
|
my ( $self, $name ) = @_; |
|
1175
|
4
|
50
|
|
|
|
19
|
if ( not defined $name ) { |
|
1176
|
0
|
|
|
|
|
0
|
return 'Window name not provided'; |
|
1177
|
|
|
|
|
|
|
} |
|
1178
|
4
|
|
|
|
|
12
|
my $res = { 'command' => 'switchToWindow' }; |
|
1179
|
4
|
|
|
|
|
13
|
my $params = { 'name' => $name, 'handle' => $name }; |
|
1180
|
4
|
|
|
|
|
103
|
return $self->_execute_command( $res, $params ); |
|
1181
|
|
|
|
|
|
|
} |
|
1182
|
|
|
|
|
|
|
|
|
1183
|
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
sub set_window_position { |
|
1185
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $x, $y, $window ) = @_; |
|
1186
|
0
|
0
|
|
|
|
0
|
$window = ( defined $window ) ? $window : 'current'; |
|
1187
|
0
|
0
|
0
|
|
|
0
|
if ( not defined $x and not defined $y ) { |
|
1188
|
0
|
|
|
|
|
0
|
croak "X & Y co-ordinates are required"; |
|
1189
|
|
|
|
|
|
|
} |
|
1190
|
0
|
0
|
|
|
|
0
|
croak qq{Error: In set_window_size, argument x "$x" isn't numeric} |
|
1191
|
|
|
|
|
|
|
unless Scalar::Util::looks_like_number($x); |
|
1192
|
0
|
0
|
|
|
|
0
|
croak qq{Error: In set_window_size, argument y "$y" isn't numeric} |
|
1193
|
|
|
|
|
|
|
unless Scalar::Util::looks_like_number($y); |
|
1194
|
0
|
|
|
|
|
0
|
$x += |
|
1195
|
|
|
|
|
|
|
0; # convert to numeric if a string, otherwise they'll be sent as strings |
|
1196
|
0
|
|
|
|
|
0
|
$y += 0; |
|
1197
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'setWindowPosition', 'window_handle' => $window }; |
|
1198
|
0
|
|
|
|
|
0
|
my $params = { 'x' => $x, 'y' => $y }; |
|
1199
|
0
|
0
|
|
|
|
0
|
if ( $self->{is_wd3} ) { |
|
1200
|
0
|
|
|
|
|
0
|
$res = { 'command' => 'setWindowRect', handle => $window }; |
|
1201
|
|
|
|
|
|
|
} |
|
1202
|
0
|
|
|
|
|
0
|
my $ret = $self->_execute_command( $res, $params ); |
|
1203
|
0
|
0
|
|
|
|
0
|
return $ret ? 1 : 0; |
|
1204
|
|
|
|
|
|
|
} |
|
1205
|
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
sub set_window_size { |
|
1208
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $height, $width, $window ) = @_; |
|
1209
|
0
|
0
|
|
|
|
0
|
$window = ( defined $window ) ? $window : 'current'; |
|
1210
|
0
|
0
|
0
|
|
|
0
|
if ( not defined $height and not defined $width ) { |
|
1211
|
0
|
|
|
|
|
0
|
croak "height & width of browser are required"; |
|
1212
|
|
|
|
|
|
|
} |
|
1213
|
0
|
0
|
|
|
|
0
|
croak qq{Error: In set_window_size, argument height "$height" isn't numeric} |
|
1214
|
|
|
|
|
|
|
unless Scalar::Util::looks_like_number($height); |
|
1215
|
0
|
0
|
|
|
|
0
|
croak qq{Error: In set_window_size, argument width "$width" isn't numeric} |
|
1216
|
|
|
|
|
|
|
unless Scalar::Util::looks_like_number($width); |
|
1217
|
0
|
|
|
|
|
0
|
$height += |
|
1218
|
|
|
|
|
|
|
0; # convert to numeric if a string, otherwise they'll be sent as strings |
|
1219
|
0
|
|
|
|
|
0
|
$width += 0; |
|
1220
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'setWindowSize', 'window_handle' => $window }; |
|
1221
|
0
|
|
|
|
|
0
|
my $params = { 'height' => $height, 'width' => $width }; |
|
1222
|
0
|
0
|
|
|
|
0
|
if ( $self->{is_wd3} ) { |
|
1223
|
0
|
|
|
|
|
0
|
$res = { 'command' => 'setWindowRect', handle => $window }; |
|
1224
|
|
|
|
|
|
|
} |
|
1225
|
0
|
|
|
|
|
0
|
my $ret = $self->_execute_command( $res, $params ); |
|
1226
|
0
|
0
|
|
|
|
0
|
return $ret ? 1 : 0; |
|
1227
|
|
|
|
|
|
|
} |
|
1228
|
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
|
|
1230
|
|
|
|
|
|
|
sub maximize_window { |
|
1231
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $window ) = @_; |
|
1232
|
|
|
|
|
|
|
|
|
1233
|
0
|
0
|
|
|
|
0
|
$window = ( defined $window ) ? $window : 'current'; |
|
1234
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'maximizeWindow', 'window_handle' => $window }; |
|
1235
|
0
|
|
|
|
|
0
|
my $ret = $self->_execute_command($res); |
|
1236
|
0
|
0
|
|
|
|
0
|
return $ret ? 1 : 0; |
|
1237
|
|
|
|
|
|
|
} |
|
1238
|
|
|
|
|
|
|
|
|
1239
|
|
|
|
|
|
|
|
|
1240
|
|
|
|
|
|
|
sub minimize_window { |
|
1241
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $window ) = @_; |
|
1242
|
0
|
0
|
|
|
|
0
|
$window = ( defined $window ) ? $window : 'current'; |
|
1243
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'minimizeWindow', 'window_handle' => $window }; |
|
1244
|
0
|
|
|
|
|
0
|
my $ret = $self->_execute_command($res); |
|
1245
|
0
|
0
|
|
|
|
0
|
return $ret ? 1 : 0; |
|
1246
|
|
|
|
|
|
|
} |
|
1247
|
|
|
|
|
|
|
|
|
1248
|
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
sub fullscreen_window { |
|
1250
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $window ) = @_; |
|
1251
|
0
|
0
|
|
|
|
0
|
$window = ( defined $window ) ? $window : 'current'; |
|
1252
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'fullscreenWindow', 'window_handle' => $window }; |
|
1253
|
0
|
|
|
|
|
0
|
my $ret = $self->_execute_command($res); |
|
1254
|
0
|
0
|
|
|
|
0
|
return $ret ? 1 : 0; |
|
1255
|
|
|
|
|
|
|
} |
|
1256
|
|
|
|
|
|
|
|
|
1257
|
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
sub get_all_cookies { |
|
1259
|
4
|
|
|
4
|
1
|
964
|
my ($self) = @_; |
|
1260
|
4
|
|
|
|
|
14
|
my $res = { 'command' => 'getAllCookies' }; |
|
1261
|
4
|
|
|
|
|
101
|
return $self->_execute_command($res); |
|
1262
|
|
|
|
|
|
|
} |
|
1263
|
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
sub add_cookie { |
|
1266
|
1
|
|
|
1
|
1
|
26
|
my ( $self, $name, $value, $path, $domain, $secure, $httponly, $expiry ) = |
|
1267
|
|
|
|
|
|
|
@_; |
|
1268
|
|
|
|
|
|
|
|
|
1269
|
1
|
50
|
33
|
|
|
9
|
if ( ( not defined $name ) |
|
1270
|
|
|
|
|
|
|
|| ( not defined $value ) ) |
|
1271
|
|
|
|
|
|
|
{ |
|
1272
|
0
|
|
|
|
|
0
|
croak "Missing parameters"; |
|
1273
|
|
|
|
|
|
|
} |
|
1274
|
|
|
|
|
|
|
|
|
1275
|
1
|
|
|
|
|
6
|
my $res = { 'command' => 'addCookie' }; |
|
1276
|
1
|
|
|
|
|
8
|
my $json_false = JSON::false; |
|
1277
|
1
|
|
|
|
|
9
|
my $json_true = JSON::true; |
|
1278
|
1
|
50
|
33
|
|
|
8
|
$secure = ( defined $secure && $secure ) ? $json_true : $json_false; |
|
1279
|
|
|
|
|
|
|
|
|
1280
|
1
|
|
|
|
|
6
|
my $params = { |
|
1281
|
|
|
|
|
|
|
'cookie' => { |
|
1282
|
|
|
|
|
|
|
'name' => $name, |
|
1283
|
|
|
|
|
|
|
'value' => $value, |
|
1284
|
|
|
|
|
|
|
'path' => $path, |
|
1285
|
|
|
|
|
|
|
'secure' => $secure, |
|
1286
|
|
|
|
|
|
|
} |
|
1287
|
|
|
|
|
|
|
}; |
|
1288
|
1
|
50
|
|
|
|
4
|
$params->{cookie}->{domain} = $domain if $domain; |
|
1289
|
1
|
50
|
|
|
|
4
|
$params->{cookie}->{'httponly'} = $httponly if $httponly; |
|
1290
|
1
|
50
|
|
|
|
3
|
$params->{cookie}->{'expiry'} = $expiry if $expiry; |
|
1291
|
|
|
|
|
|
|
|
|
1292
|
1
|
|
|
|
|
21
|
return $self->_execute_command( $res, $params ); |
|
1293
|
|
|
|
|
|
|
} |
|
1294
|
|
|
|
|
|
|
|
|
1295
|
|
|
|
|
|
|
|
|
1296
|
|
|
|
|
|
|
sub delete_all_cookies { |
|
1297
|
2
|
|
|
2
|
1
|
1188
|
my ($self) = @_; |
|
1298
|
2
|
|
|
|
|
8
|
my $res = { 'command' => 'deleteAllCookies' }; |
|
1299
|
2
|
|
|
|
|
54
|
return $self->_execute_command($res); |
|
1300
|
|
|
|
|
|
|
} |
|
1301
|
|
|
|
|
|
|
|
|
1302
|
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
sub get_cookie_named { |
|
1304
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $cookie_name ) = @_; |
|
1305
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'getCookieNamed', 'name' => $cookie_name }; |
|
1306
|
0
|
|
|
|
|
0
|
return $self->_execute_command($res); |
|
1307
|
|
|
|
|
|
|
} |
|
1308
|
|
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
|
|
1310
|
|
|
|
|
|
|
sub delete_cookie_named { |
|
1311
|
1
|
|
|
1
|
1
|
1182
|
my ( $self, $cookie_name ) = @_; |
|
1312
|
1
|
50
|
|
|
|
5
|
if ( not defined $cookie_name ) { |
|
1313
|
0
|
|
|
|
|
0
|
croak "Cookie name not provided"; |
|
1314
|
|
|
|
|
|
|
} |
|
1315
|
1
|
|
|
|
|
5
|
my $res = { 'command' => 'deleteCookieNamed', 'name' => $cookie_name }; |
|
1316
|
1
|
|
|
|
|
27
|
return $self->_execute_command($res); |
|
1317
|
|
|
|
|
|
|
} |
|
1318
|
|
|
|
|
|
|
|
|
1319
|
|
|
|
|
|
|
|
|
1320
|
|
|
|
|
|
|
sub get_page_source { |
|
1321
|
13
|
|
|
13
|
1
|
3780
|
my ($self) = @_; |
|
1322
|
13
|
|
|
|
|
35
|
my $res = { 'command' => 'getPageSource' }; |
|
1323
|
13
|
|
|
|
|
342
|
return $self->_execute_command($res); |
|
1324
|
|
|
|
|
|
|
} |
|
1325
|
|
|
|
|
|
|
|
|
1326
|
|
|
|
|
|
|
|
|
1327
|
|
|
|
|
|
|
sub find_element { |
|
1328
|
67
|
|
|
67
|
1
|
5682
|
my ( $self, $query, $method ) = @_; |
|
1329
|
67
|
50
|
|
|
|
196
|
if ( not defined $query ) { |
|
1330
|
0
|
|
|
|
|
0
|
croak 'Search string to find element not provided.'; |
|
1331
|
|
|
|
|
|
|
} |
|
1332
|
|
|
|
|
|
|
|
|
1333
|
67
|
|
|
|
|
183
|
my $res = { 'command' => 'findElement' }; |
|
1334
|
67
|
|
|
|
|
185
|
my $params = $self->_build_find_params( $method, $query ); |
|
1335
|
67
|
|
|
|
|
138
|
my $ret_data = eval { $self->_execute_command( $res, $params ); }; |
|
|
67
|
|
|
|
|
1787
|
|
|
1336
|
67
|
100
|
|
|
|
773
|
if ($@) { |
|
1337
|
12
|
100
|
|
|
|
85
|
if ( $@ =~ |
|
1338
|
|
|
|
|
|
|
/(An element could not be located on the page using the given search parameters)/ |
|
1339
|
|
|
|
|
|
|
) |
|
1340
|
|
|
|
|
|
|
{ |
|
1341
|
|
|
|
|
|
|
# give details on what element wasn't found |
|
1342
|
11
|
|
|
|
|
58
|
$@ = "$1: $query,$params->{using}"; |
|
1343
|
11
|
|
|
|
|
40
|
local @CARP_NOT = ( "Selenium::Remote::Driver", @CARP_NOT ); |
|
1344
|
11
|
|
|
|
|
850
|
croak $@; |
|
1345
|
|
|
|
|
|
|
} |
|
1346
|
|
|
|
|
|
|
else { |
|
1347
|
|
|
|
|
|
|
# re throw if the exception wasn't what we expected |
|
1348
|
1
|
|
|
|
|
17
|
die $@; |
|
1349
|
|
|
|
|
|
|
} |
|
1350
|
|
|
|
|
|
|
} |
|
1351
|
55
|
|
|
|
|
1200
|
return $self->webelement_class->new( |
|
1352
|
|
|
|
|
|
|
id => $ret_data, |
|
1353
|
|
|
|
|
|
|
driver => $self |
|
1354
|
|
|
|
|
|
|
); |
|
1355
|
|
|
|
|
|
|
} |
|
1356
|
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
|
|
1358
|
|
|
|
|
|
|
sub find_elements { |
|
1359
|
6
|
|
|
6
|
1
|
1483
|
my ( $self, $query, $method ) = @_; |
|
1360
|
6
|
50
|
|
|
|
19
|
if ( not defined $query ) { |
|
1361
|
0
|
|
|
|
|
0
|
croak 'Search string to find element not provided.'; |
|
1362
|
|
|
|
|
|
|
} |
|
1363
|
|
|
|
|
|
|
|
|
1364
|
6
|
|
|
|
|
18
|
my $res = { 'command' => 'findElements' }; |
|
1365
|
6
|
|
|
|
|
17
|
my $params = $self->_build_find_params( $method, $query ); |
|
1366
|
6
|
|
|
|
|
12
|
my $ret_data = eval { $self->_execute_command( $res, $params ); }; |
|
|
6
|
|
|
|
|
137
|
|
|
1367
|
6
|
50
|
|
|
|
20
|
if ($@) { |
|
1368
|
0
|
0
|
|
|
|
0
|
if ( $@ =~ |
|
1369
|
|
|
|
|
|
|
/(An element could not be located on the page using the given search parameters)/ |
|
1370
|
|
|
|
|
|
|
) |
|
1371
|
|
|
|
|
|
|
{ |
|
1372
|
|
|
|
|
|
|
# give details on what element wasn't found |
|
1373
|
0
|
|
|
|
|
0
|
$@ = "$1: $query,$params->{using}"; |
|
1374
|
0
|
|
|
|
|
0
|
local @CARP_NOT = ( "Selenium::Remote::Driver", @CARP_NOT ); |
|
1375
|
0
|
|
|
|
|
0
|
croak $@; |
|
1376
|
|
|
|
|
|
|
} |
|
1377
|
|
|
|
|
|
|
else { |
|
1378
|
|
|
|
|
|
|
# re throw if the exception wasn't what we expected |
|
1379
|
0
|
|
|
|
|
0
|
die $@; |
|
1380
|
|
|
|
|
|
|
} |
|
1381
|
|
|
|
|
|
|
} |
|
1382
|
6
|
|
|
|
|
14
|
my $elem_obj_arr = []; |
|
1383
|
6
|
|
|
|
|
36
|
foreach (@$ret_data) { |
|
1384
|
6
|
|
|
|
|
137
|
push( |
|
1385
|
|
|
|
|
|
|
@$elem_obj_arr, |
|
1386
|
|
|
|
|
|
|
$self->webelement_class->new( |
|
1387
|
|
|
|
|
|
|
id => $_, |
|
1388
|
|
|
|
|
|
|
driver => $self |
|
1389
|
|
|
|
|
|
|
) |
|
1390
|
|
|
|
|
|
|
); |
|
1391
|
|
|
|
|
|
|
} |
|
1392
|
4
|
100
|
|
|
|
41
|
return wantarray ? @{$elem_obj_arr} : $elem_obj_arr; |
|
|
1
|
|
|
|
|
7
|
|
|
1393
|
|
|
|
|
|
|
} |
|
1394
|
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
|
|
1396
|
|
|
|
|
|
|
sub find_child_element { |
|
1397
|
4
|
|
|
4
|
1
|
45
|
my ( $self, $elem, $query, $method ) = @_; |
|
1398
|
4
|
100
|
66
|
|
|
26
|
if ( ( not defined $elem ) || ( not defined $query ) ) { |
|
1399
|
1
|
|
|
|
|
96
|
croak "Missing parameters"; |
|
1400
|
|
|
|
|
|
|
} |
|
1401
|
3
|
|
|
|
|
13
|
my $res = { 'command' => 'findChildElement', 'id' => $elem->{id} }; |
|
1402
|
3
|
|
|
|
|
10
|
my $params = $self->_build_find_params( $method, $query ); |
|
1403
|
3
|
|
|
|
|
8
|
my $ret_data = eval { $self->_execute_command( $res, $params ); }; |
|
|
3
|
|
|
|
|
65
|
|
|
1404
|
3
|
50
|
|
|
|
16
|
if ($@) { |
|
1405
|
0
|
0
|
|
|
|
0
|
if ( $@ =~ |
|
1406
|
|
|
|
|
|
|
/(An element could not be located on the page using the given search parameters)/ |
|
1407
|
|
|
|
|
|
|
) |
|
1408
|
|
|
|
|
|
|
{ |
|
1409
|
|
|
|
|
|
|
# give details on what element wasn't found |
|
1410
|
0
|
|
|
|
|
0
|
$@ = "$1: $query,$params->{using}"; |
|
1411
|
0
|
|
|
|
|
0
|
local @CARP_NOT = ( "Selenium::Remote::Driver", @CARP_NOT ); |
|
1412
|
0
|
|
|
|
|
0
|
croak $@; |
|
1413
|
|
|
|
|
|
|
} |
|
1414
|
|
|
|
|
|
|
else { |
|
1415
|
|
|
|
|
|
|
# re throw if the exception wasn't what we expected |
|
1416
|
0
|
|
|
|
|
0
|
die $@; |
|
1417
|
|
|
|
|
|
|
} |
|
1418
|
|
|
|
|
|
|
} |
|
1419
|
3
|
|
|
|
|
79
|
return $self->webelement_class->new( |
|
1420
|
|
|
|
|
|
|
id => $ret_data, |
|
1421
|
|
|
|
|
|
|
driver => $self |
|
1422
|
|
|
|
|
|
|
); |
|
1423
|
|
|
|
|
|
|
} |
|
1424
|
|
|
|
|
|
|
|
|
1425
|
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
sub find_child_elements { |
|
1427
|
2
|
|
|
2
|
1
|
706
|
my ( $self, $elem, $query, $method ) = @_; |
|
1428
|
2
|
50
|
33
|
|
|
29
|
if ( ( not defined $elem ) || ( not defined $query ) ) { |
|
1429
|
0
|
|
|
|
|
0
|
croak "Missing parameters"; |
|
1430
|
|
|
|
|
|
|
} |
|
1431
|
|
|
|
|
|
|
|
|
1432
|
2
|
|
|
|
|
10
|
my $res = { 'command' => 'findChildElements', 'id' => $elem->{id} }; |
|
1433
|
2
|
|
|
|
|
9
|
my $params = $self->_build_find_params( $method, $query ); |
|
1434
|
2
|
|
|
|
|
16
|
my $ret_data = eval { $self->_execute_command( $res, $params ); }; |
|
|
2
|
|
|
|
|
50
|
|
|
1435
|
2
|
50
|
|
|
|
8
|
if ($@) { |
|
1436
|
0
|
0
|
|
|
|
0
|
if ( $@ =~ |
|
1437
|
|
|
|
|
|
|
/(An element could not be located on the page using the given search parameters)/ |
|
1438
|
|
|
|
|
|
|
) |
|
1439
|
|
|
|
|
|
|
{ |
|
1440
|
|
|
|
|
|
|
# give details on what element wasn't found |
|
1441
|
0
|
|
|
|
|
0
|
$@ = "$1: $query,$params->{using}"; |
|
1442
|
0
|
|
|
|
|
0
|
local @CARP_NOT = ( "Selenium::Remote::Driver", @CARP_NOT ); |
|
1443
|
0
|
|
|
|
|
0
|
croak $@; |
|
1444
|
|
|
|
|
|
|
} |
|
1445
|
|
|
|
|
|
|
else { |
|
1446
|
|
|
|
|
|
|
# re throw if the exception wasn't what we expected |
|
1447
|
0
|
|
|
|
|
0
|
die $@; |
|
1448
|
|
|
|
|
|
|
} |
|
1449
|
|
|
|
|
|
|
} |
|
1450
|
2
|
|
|
|
|
7
|
my $elem_obj_arr = []; |
|
1451
|
2
|
|
|
|
|
6
|
my $i = 0; |
|
1452
|
2
|
|
|
|
|
7
|
foreach (@$ret_data) { |
|
1453
|
6
|
|
|
|
|
114
|
$elem_obj_arr->[$i] = $self->webelement_class->new( |
|
1454
|
|
|
|
|
|
|
id => $_, |
|
1455
|
|
|
|
|
|
|
driver => $self |
|
1456
|
|
|
|
|
|
|
); |
|
1457
|
6
|
|
|
|
|
38
|
$i++; |
|
1458
|
|
|
|
|
|
|
} |
|
1459
|
2
|
50
|
|
|
|
15
|
return wantarray ? @{$elem_obj_arr} : $elem_obj_arr; |
|
|
0
|
|
|
|
|
0
|
|
|
1460
|
|
|
|
|
|
|
} |
|
1461
|
|
|
|
|
|
|
|
|
1462
|
|
|
|
|
|
|
|
|
1463
|
|
|
|
|
|
|
sub _build_find_params { |
|
1464
|
78
|
|
|
78
|
|
169
|
my ( $self, $method, $query ) = @_; |
|
1465
|
|
|
|
|
|
|
|
|
1466
|
78
|
|
|
|
|
198
|
my $using = $self->_build_using($method); |
|
1467
|
|
|
|
|
|
|
|
|
1468
|
|
|
|
|
|
|
# geckodriver doesn't accept name as a valid selector |
|
1469
|
78
|
50
|
33
|
|
|
587
|
if ( $self->isa('Selenium::Firefox') && $using eq 'name' ) { |
|
1470
|
|
|
|
|
|
|
return { |
|
1471
|
0
|
|
|
|
|
0
|
using => 'css selector', |
|
1472
|
|
|
|
|
|
|
value => qq{[name="$query"]} |
|
1473
|
|
|
|
|
|
|
}; |
|
1474
|
|
|
|
|
|
|
} |
|
1475
|
|
|
|
|
|
|
else { |
|
1476
|
|
|
|
|
|
|
return { |
|
1477
|
78
|
|
|
|
|
319
|
using => $using, |
|
1478
|
|
|
|
|
|
|
value => $query |
|
1479
|
|
|
|
|
|
|
}; |
|
1480
|
|
|
|
|
|
|
} |
|
1481
|
|
|
|
|
|
|
} |
|
1482
|
|
|
|
|
|
|
|
|
1483
|
|
|
|
|
|
|
sub _build_using { |
|
1484
|
78
|
|
|
78
|
|
162
|
my ( $self, $method ) = @_; |
|
1485
|
|
|
|
|
|
|
|
|
1486
|
78
|
100
|
|
|
|
185
|
if ($method) { |
|
1487
|
69
|
50
|
|
|
|
253
|
if ( $self->FINDERS->{$method} ) { |
|
1488
|
69
|
|
|
|
|
240
|
return $self->FINDERS->{$method}; |
|
1489
|
|
|
|
|
|
|
} |
|
1490
|
|
|
|
|
|
|
else { |
|
1491
|
|
|
|
|
|
|
croak 'Bad method, expected: ' |
|
1492
|
0
|
|
|
|
|
0
|
. join( ', ', keys %{ $self->FINDERS } ) |
|
|
0
|
|
|
|
|
0
|
|
|
1493
|
|
|
|
|
|
|
. ", got $method"; |
|
1494
|
|
|
|
|
|
|
} |
|
1495
|
|
|
|
|
|
|
} |
|
1496
|
|
|
|
|
|
|
else { |
|
1497
|
9
|
|
|
|
|
208
|
return $self->default_finder; |
|
1498
|
|
|
|
|
|
|
} |
|
1499
|
|
|
|
|
|
|
} |
|
1500
|
|
|
|
|
|
|
|
|
1501
|
|
|
|
|
|
|
sub get_active_element { |
|
1502
|
1
|
|
|
1
|
1
|
1067
|
my ($self) = @_; |
|
1503
|
1
|
|
|
|
|
4
|
my $res = { 'command' => 'getActiveElement' }; |
|
1504
|
1
|
|
|
|
|
2
|
my $ret_data = eval { $self->_execute_command($res) }; |
|
|
1
|
|
|
|
|
28
|
|
|
1505
|
1
|
50
|
|
|
|
12
|
if ($@) { |
|
1506
|
0
|
|
|
|
|
0
|
croak $@; |
|
1507
|
|
|
|
|
|
|
} |
|
1508
|
|
|
|
|
|
|
else { |
|
1509
|
1
|
|
|
|
|
23
|
return $self->webelement_class->new( |
|
1510
|
|
|
|
|
|
|
id => $ret_data, |
|
1511
|
|
|
|
|
|
|
driver => $self |
|
1512
|
|
|
|
|
|
|
); |
|
1513
|
|
|
|
|
|
|
} |
|
1514
|
|
|
|
|
|
|
} |
|
1515
|
|
|
|
|
|
|
|
|
1516
|
|
|
|
|
|
|
|
|
1517
|
|
|
|
|
|
|
sub cache_status { |
|
1518
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_; |
|
1519
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'cacheStatus' }; |
|
1520
|
0
|
|
|
|
|
0
|
return $self->_execute_command($res); |
|
1521
|
|
|
|
|
|
|
} |
|
1522
|
|
|
|
|
|
|
|
|
1523
|
|
|
|
|
|
|
|
|
1524
|
|
|
|
|
|
|
sub set_geolocation { |
|
1525
|
1
|
|
|
1
|
1
|
791
|
my ( $self, %params ) = @_; |
|
1526
|
1
|
|
|
|
|
6
|
my $res = { 'command' => 'setGeolocation' }; |
|
1527
|
1
|
|
|
|
|
29
|
return $self->_execute_command( $res, \%params ); |
|
1528
|
|
|
|
|
|
|
} |
|
1529
|
|
|
|
|
|
|
|
|
1530
|
|
|
|
|
|
|
|
|
1531
|
|
|
|
|
|
|
sub get_geolocation { |
|
1532
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_; |
|
1533
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'getGeolocation' }; |
|
1534
|
0
|
|
|
|
|
0
|
return $self->_execute_command($res); |
|
1535
|
|
|
|
|
|
|
} |
|
1536
|
|
|
|
|
|
|
|
|
1537
|
|
|
|
|
|
|
|
|
1538
|
|
|
|
|
|
|
sub get_log { |
|
1539
|
4
|
|
|
4
|
1
|
1463
|
my ( $self, $type ) = @_; |
|
1540
|
4
|
|
|
|
|
13
|
my $res = { 'command' => 'getLog' }; |
|
1541
|
4
|
|
|
|
|
115
|
return $self->_execute_command( $res, { type => $type } ); |
|
1542
|
|
|
|
|
|
|
} |
|
1543
|
|
|
|
|
|
|
|
|
1544
|
|
|
|
|
|
|
|
|
1545
|
|
|
|
|
|
|
sub get_log_types { |
|
1546
|
1
|
|
|
1
|
1
|
8
|
my ($self) = @_; |
|
1547
|
1
|
|
|
|
|
4
|
my $res = { 'command' => 'getLogTypes' }; |
|
1548
|
1
|
|
|
|
|
23
|
return $self->_execute_command($res); |
|
1549
|
|
|
|
|
|
|
} |
|
1550
|
|
|
|
|
|
|
|
|
1551
|
|
|
|
|
|
|
|
|
1552
|
|
|
|
|
|
|
sub set_orientation { |
|
1553
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $orientation ) = @_; |
|
1554
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'setOrientation' }; |
|
1555
|
0
|
|
|
|
|
0
|
return $self->_execute_command( $res, { orientation => $orientation } ); |
|
1556
|
|
|
|
|
|
|
} |
|
1557
|
|
|
|
|
|
|
|
|
1558
|
|
|
|
|
|
|
|
|
1559
|
|
|
|
|
|
|
sub get_orientation { |
|
1560
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_; |
|
1561
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'getOrientation' }; |
|
1562
|
0
|
|
|
|
|
0
|
return $self->_execute_command($res); |
|
1563
|
|
|
|
|
|
|
} |
|
1564
|
|
|
|
|
|
|
|
|
1565
|
|
|
|
|
|
|
|
|
1566
|
|
|
|
|
|
|
sub send_modifier { |
|
1567
|
1
|
|
|
1
|
1
|
22
|
my ( $self, $modifier, $isdown ) = @_; |
|
1568
|
1
|
50
|
|
|
|
11
|
if ( $isdown =~ /(down|up)/ ) { |
|
1569
|
1
|
50
|
|
|
|
6
|
$isdown = $isdown =~ /down/ ? 1 : 0; |
|
1570
|
|
|
|
|
|
|
} |
|
1571
|
|
|
|
|
|
|
|
|
1572
|
1
|
50
|
33
|
|
|
5
|
if ( $self->{is_wd3} |
|
1573
|
0
|
|
|
|
|
0
|
&& !( grep { $self->browser_name eq $_ } qw{MicrosoftEdge} ) ) |
|
1574
|
|
|
|
|
|
|
{ |
|
1575
|
|
|
|
|
|
|
my $acts = [ |
|
1576
|
|
|
|
|
|
|
{ |
|
1577
|
|
|
|
|
|
|
type => $isdown ? 'keyDown' : 'keyUp', |
|
1578
|
0
|
0
|
|
|
|
0
|
value => KEYS->{ lc($modifier) }, |
|
1579
|
|
|
|
|
|
|
}, |
|
1580
|
|
|
|
|
|
|
]; |
|
1581
|
|
|
|
|
|
|
|
|
1582
|
0
|
|
|
|
|
0
|
my $action = { |
|
1583
|
|
|
|
|
|
|
actions => [ |
|
1584
|
|
|
|
|
|
|
{ |
|
1585
|
|
|
|
|
|
|
id => 'key', |
|
1586
|
|
|
|
|
|
|
type => 'key', |
|
1587
|
|
|
|
|
|
|
actions => $acts, |
|
1588
|
|
|
|
|
|
|
} |
|
1589
|
|
|
|
|
|
|
] |
|
1590
|
|
|
|
|
|
|
}; |
|
1591
|
0
|
|
|
|
|
0
|
_queue_action(%$action); |
|
1592
|
0
|
|
|
|
|
0
|
return 1; |
|
1593
|
|
|
|
|
|
|
} |
|
1594
|
|
|
|
|
|
|
|
|
1595
|
1
|
|
|
|
|
4
|
my $res = { 'command' => 'sendModifier' }; |
|
1596
|
1
|
|
|
|
|
5
|
my $params = { |
|
1597
|
|
|
|
|
|
|
value => $modifier, |
|
1598
|
|
|
|
|
|
|
isdown => $isdown |
|
1599
|
|
|
|
|
|
|
}; |
|
1600
|
1
|
|
|
|
|
23
|
return $self->_execute_command( $res, $params ); |
|
1601
|
|
|
|
|
|
|
} |
|
1602
|
|
|
|
|
|
|
|
|
1603
|
|
|
|
|
|
|
|
|
1604
|
|
|
|
|
|
|
sub compare_elements { |
|
1605
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $elem1, $elem2 ) = @_; |
|
1606
|
|
|
|
|
|
|
my $res = { |
|
1607
|
|
|
|
|
|
|
'command' => 'elementEquals', |
|
1608
|
|
|
|
|
|
|
'id' => $elem1->{id}, |
|
1609
|
|
|
|
|
|
|
'other' => $elem2->{id} |
|
1610
|
0
|
|
|
|
|
0
|
}; |
|
1611
|
0
|
|
|
|
|
0
|
return $self->_execute_command($res); |
|
1612
|
|
|
|
|
|
|
} |
|
1613
|
|
|
|
|
|
|
|
|
1614
|
|
|
|
|
|
|
|
|
1615
|
|
|
|
|
|
|
sub click { |
|
1616
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $button, $append ) = @_; |
|
1617
|
0
|
|
|
|
|
0
|
$button = _get_button($button); |
|
1618
|
|
|
|
|
|
|
|
|
1619
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'click' }; |
|
1620
|
0
|
|
|
|
|
0
|
my $params = { 'button' => $button }; |
|
1621
|
|
|
|
|
|
|
|
|
1622
|
0
|
0
|
0
|
|
|
0
|
if ( $self->{is_wd3} |
|
1623
|
0
|
|
|
|
|
0
|
&& !( grep { $self->browser_name eq $_ } qw{MicrosoftEdge} ) ) |
|
1624
|
|
|
|
|
|
|
{ |
|
1625
|
0
|
|
|
|
|
0
|
$params = { |
|
1626
|
|
|
|
|
|
|
actions => [ |
|
1627
|
|
|
|
|
|
|
{ |
|
1628
|
|
|
|
|
|
|
type => "pointer", |
|
1629
|
|
|
|
|
|
|
id => 'mouse', |
|
1630
|
|
|
|
|
|
|
parameters => { "pointerType" => "mouse" }, |
|
1631
|
|
|
|
|
|
|
actions => [ |
|
1632
|
|
|
|
|
|
|
{ |
|
1633
|
|
|
|
|
|
|
type => "pointerDown", |
|
1634
|
|
|
|
|
|
|
duration => 0, |
|
1635
|
|
|
|
|
|
|
button => $button, |
|
1636
|
|
|
|
|
|
|
}, |
|
1637
|
|
|
|
|
|
|
{ |
|
1638
|
|
|
|
|
|
|
type => "pointerUp", |
|
1639
|
|
|
|
|
|
|
duration => 0, |
|
1640
|
|
|
|
|
|
|
button => $button, |
|
1641
|
|
|
|
|
|
|
}, |
|
1642
|
|
|
|
|
|
|
], |
|
1643
|
|
|
|
|
|
|
} |
|
1644
|
|
|
|
|
|
|
], |
|
1645
|
|
|
|
|
|
|
}; |
|
1646
|
0
|
0
|
|
|
|
0
|
if ($append) { |
|
1647
|
0
|
|
|
|
|
0
|
_queue_action(%$params); |
|
1648
|
0
|
|
|
|
|
0
|
return 1; |
|
1649
|
|
|
|
|
|
|
} |
|
1650
|
0
|
|
|
|
|
0
|
return $self->general_action(%$params); |
|
1651
|
|
|
|
|
|
|
} |
|
1652
|
|
|
|
|
|
|
|
|
1653
|
0
|
|
|
|
|
0
|
return $self->_execute_command( $res, $params ); |
|
1654
|
|
|
|
|
|
|
} |
|
1655
|
|
|
|
|
|
|
|
|
1656
|
|
|
|
|
|
|
sub _get_button { |
|
1657
|
0
|
|
|
0
|
|
0
|
my $button = shift; |
|
1658
|
0
|
|
|
|
|
0
|
my $button_enum = { LEFT => 0, MIDDLE => 1, RIGHT => 2 }; |
|
1659
|
0
|
0
|
0
|
|
|
0
|
if ( defined $button && $button =~ /(LEFT|MIDDLE|RIGHT)/i ) { |
|
1660
|
0
|
|
|
|
|
0
|
return $button_enum->{ uc $1 }; |
|
1661
|
|
|
|
|
|
|
} |
|
1662
|
0
|
0
|
0
|
|
|
0
|
if ( defined $button && $button =~ /(0|1|2)/ ) { |
|
1663
|
|
|
|
|
|
|
#Handle user error sending in "1" |
|
1664
|
0
|
|
|
|
|
0
|
return int($1); |
|
1665
|
|
|
|
|
|
|
} |
|
1666
|
0
|
|
|
|
|
0
|
return 0; |
|
1667
|
|
|
|
|
|
|
} |
|
1668
|
|
|
|
|
|
|
|
|
1669
|
|
|
|
|
|
|
|
|
1670
|
|
|
|
|
|
|
sub double_click { |
|
1671
|
0
|
|
|
0
|
1
|
0
|
my ( $self, $button ) = @_; |
|
1672
|
|
|
|
|
|
|
|
|
1673
|
0
|
|
|
|
|
0
|
$button = _get_button($button); |
|
1674
|
|
|
|
|
|
|
|
|
1675
|
0
|
0
|
0
|
|
|
0
|
if ( $self->{is_wd3} |
|
1676
|
0
|
|
|
|
|
0
|
&& !( grep { $self->browser_name eq $_ } qw{MicrosoftEdge} ) ) |
|
1677
|
|
|
|
|
|
|
{ |
|
1678
|
0
|
|
|
|
|
0
|
$self->click( $button, 1 ); |
|
1679
|
0
|
|
|
|
|
0
|
$self->click( $button, 1 ); |
|
1680
|
0
|
|
|
|
|
0
|
return $self->general_action(); |
|
1681
|
|
|
|
|
|
|
} |
|
1682
|
|
|
|
|
|
|
|
|
1683
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'doubleClick' }; |
|
1684
|
0
|
|
|
|
|
0
|
return $self->_execute_command($res); |
|
1685
|
|
|
|
|
|
|
} |
|
1686
|
|
|
|
|
|
|
|
|
1687
|
|
|
|
|
|
|
|
|
1688
|
|
|
|
|
|
|
sub button_down { |
|
1689
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_; |
|
1690
|
|
|
|
|
|
|
|
|
1691
|
0
|
0
|
0
|
|
|
0
|
if ( $self->{is_wd3} |
|
1692
|
0
|
|
|
|
|
0
|
&& !( grep { $self->browser_name eq $_ } qw{MicrosoftEdge} ) ) |
|
1693
|
|
|
|
|
|
|
{ |
|
1694
|
0
|
|
|
|
|
0
|
my $params = { |
|
1695
|
|
|
|
|
|
|
actions => [ |
|
1696
|
|
|
|
|
|
|
{ |
|
1697
|
|
|
|
|
|
|
type => "pointer", |
|
1698
|
|
|
|
|
|
|
id => 'mouse', |
|
1699
|
|
|
|
|
|
|
parameters => { "pointerType" => "mouse" }, |
|
1700
|
|
|
|
|
|
|
actions => [ |
|
1701
|
|
|
|
|
|
|
{ |
|
1702
|
|
|
|
|
|
|
type => "pointerDown", |
|
1703
|
|
|
|
|
|
|
duration => 0, |
|
1704
|
|
|
|
|
|
|
button => 0, |
|
1705
|
|
|
|
|
|
|
}, |
|
1706
|
|
|
|
|
|
|
], |
|
1707
|
|
|
|
|
|
|
} |
|
1708
|
|
|
|
|
|
|
], |
|
1709
|
|
|
|
|
|
|
}; |
|
1710
|
0
|
|
|
|
|
0
|
_queue_action(%$params); |
|
1711
|
0
|
|
|
|
|
0
|
return 1; |
|
1712
|
|
|
|
|
|
|
} |
|
1713
|
|
|
|
|
|
|
|
|
1714
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'buttonDown' }; |
|
1715
|
0
|
|
|
|
|
0
|
return $self->_execute_command($res); |
|
1716
|
|
|
|
|
|
|
} |
|
1717
|
|
|
|
|
|
|
|
|
1718
|
|
|
|
|
|
|
|
|
1719
|
|
|
|
|
|
|
sub button_up { |
|
1720
|
0
|
|
|
0
|
1
|
0
|
my ($self) = @_; |
|
1721
|
|
|
|
|
|
|
|
|
1722
|
0
|
0
|
0
|
|
|
0
|
if ( $self->{is_wd3} |
|
1723
|
0
|
|
|
|
|
0
|
&& !( grep { $self->browser_name eq $_ } qw{MicrosoftEdge} ) ) |
|
1724
|
|
|
|
|
|
|
{ |
|
1725
|
0
|
|
|
|
|
0
|
my $params = { |
|
1726
|
|
|
|
|
|
|
actions => [ |
|
1727
|
|
|
|
|
|
|
{ |
|
1728
|
|
|
|
|
|
|
type => "pointer", |
|
1729
|
|
|
|
|
|
|
id => 'mouse', |
|
1730
|
|
|
|
|
|
|
parameters => { "pointerType" => "mouse" }, |
|
1731
|
|
|
|
|
|
|
actions => [ |
|
1732
|
|
|
|
|
|
|
{ |
|
1733
|
|
|
|
|
|
|
type => "pointerUp", |
|
1734
|
|
|
|
|
|
|
duration => 0, |
|
1735
|
|
|
|
|
|
|
button => 0, |
|
1736
|
|
|
|
|
|
|
}, |
|
1737
|
|
|
|
|
|
|
], |
|
1738
|
|
|
|
|
|
|
} |
|
1739
|
|
|
|
|
|
|
], |
|
1740
|
|
|
|
|
|
|
}; |
|
1741
|
0
|
|
|
|
|
0
|
_queue_action(%$params); |
|
1742
|
0
|
|
|
|
|
0
|
return 1; |
|
1743
|
|
|
|
|
|
|
} |
|
1744
|
|
|
|
|
|
|
|
|
1745
|
0
|
|
|
|
|
0
|
my $res = { 'command' => 'buttonUp' }; |
|
1746
|
0
|
|
|
|
|
0
|
return $self->_execute_command($res); |
|
1747
|
|
|
|
|
|
|
} |
|
1748
|
|
|
|
|
|
|
|
|
1749
|
|
|
|
|
|
|
|
|
1750
|
|
|
|
|
|
|
# this method duplicates upload() method in the |
|
1751
|
|
|
|
|
|
|
# org.openqa.selenium.remote.RemoteWebElement java class. |
|
1752
|
|
|
|
|
|
|
|
|
1753
|
|
|
|
|
|
|
sub upload_file { |
|
1754
|
5
|
|
|
5
|
1
|
1142
|
my ( $self, $filename, $raw_content ) = @_; |
|
1755
|
|
|
|
|
|
|
|
|
1756
|
5
|
|
|
|
|
10
|
my $params; |
|
1757
|
5
|
100
|
|
|
|
15
|
if ( defined $raw_content ) { |
|
1758
|
|
|
|
|
|
|
|
|
1759
|
|
|
|
|
|
|
#If no processing is passed, send the argument raw |
|
1760
|
1
|
|
|
|
|
6
|
$params = { file => $raw_content }; |
|
1761
|
|
|
|
|
|
|
} |
|
1762
|
|
|
|
|
|
|
else { |
|
1763
|
|
|
|
|
|
|
#Otherwise, zip/base64 it. |
|
1764
|
4
|
|
|
|
|
11
|
$params = $self->_prepare_file($filename); |
|
1765
|
|
|
|
|
|
|
} |
|
1766
|
|
|
|
|
|
|
|
|
1767
|
4
|
|
|
|
|
87
|
my $res = { 'command' => 'uploadFile' }; # /session/:SessionId/file |
|
1768
|
4
|
|
|
|
|
115
|
my $ret = $self->_execute_command( $res, $params ); |
|
1769
|
|
|
|
|
|
|
|
|
1770
|
3
|
|
|
|
|
51
|
return $ret; |
|
1771
|
|
|
|
|
|
|
} |
|
1772
|
|
|
|
|
|
|
|
|
1773
|
|
|
|
|
|
|
sub _prepare_file { |
|
1774
|
4
|
|
|
4
|
|
11
|
my ( $self, $filename ) = @_; |
|
1775
|
|
|
|
|
|
|
|
|
1776
|
4
|
100
|
|
|
|
189
|
if ( not -r $filename ) { croak "upload_file: no such file: $filename"; } |
|
|
1
|
|
|
|
|
184
|
|
|
1777
|
3
|
|
|
|
|
11
|
my $string = ""; # buffer |
|
1778
|
3
|
|
|
|
|
21
|
my $zip = Archive::Zip->new(); |
|
1779
|
3
|
|
|
|
|
293
|
$zip->addFile( $filename, basename($filename) ); |
|
1780
|
3
|
50
|
|
|
|
1273
|
if ( $zip->writeToFileHandle( IO::String->new($string) ) != AZ_OK ) { |
|
1781
|
0
|
|
|
|
|
0
|
die 'zip failed'; |
|
1782
|
|
|
|
|
|
|
} |
|
1783
|
|
|
|
|
|
|
|
|
1784
|
3
|
|
|
|
|
6416
|
return { file => MIME::Base64::encode_base64( $string, '' ) }; |
|
1785
|
|
|
|
|
|
|
} |
|
1786
|
|
|
|
|
|
|
|
|
1787
|
|
|
|
|
|
|
|
|
1788
|
|
|
|
|
|
|
sub get_text { |
|
1789
|
14
|
|
|
14
|
1
|
25
|
my $self = shift; |
|
1790
|
14
|
|
|
|
|
46
|
return $self->find_element(@_)->get_text(); |
|
1791
|
|
|
|
|
|
|
} |
|
1792
|
|
|
|
|
|
|
|
|
1793
|
|
|
|
|
|
|
|
|
1794
|
|
|
|
|
|
|
sub get_body { |
|
1795
|
13
|
|
|
13
|
1
|
29
|
my $self = shift; |
|
1796
|
13
|
|
|
|
|
40
|
return $self->get_text( '//body', 'xpath' ); |
|
1797
|
|
|
|
|
|
|
} |
|
1798
|
|
|
|
|
|
|
|
|
1799
|
|
|
|
|
|
|
|
|
1800
|
|
|
|
|
|
|
sub get_path { |
|
1801
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1802
|
0
|
|
|
|
|
0
|
my $location = $self->get_current_url; |
|
1803
|
0
|
|
|
|
|
0
|
$location =~ s/\?.*//; # strip of query params |
|
1804
|
0
|
|
|
|
|
0
|
$location =~ s/#.*//; # strip of anchors |
|
1805
|
0
|
|
|
|
|
0
|
$location =~ s#^https?://[^/]+##; # strip off host |
|
1806
|
0
|
|
|
|
|
0
|
return $location; |
|
1807
|
|
|
|
|
|
|
} |
|
1808
|
|
|
|
|
|
|
|
|
1809
|
|
|
|
|
|
|
|
|
1810
|
|
|
|
|
|
|
sub get_user_agent { |
|
1811
|
1
|
|
|
1
|
1
|
81
|
my $self = shift; |
|
1812
|
1
|
|
|
|
|
7
|
return $self->execute_script('return window.navigator.userAgent;'); |
|
1813
|
|
|
|
|
|
|
} |
|
1814
|
|
|
|
|
|
|
|
|
1815
|
|
|
|
|
|
|
|
|
1816
|
|
|
|
|
|
|
sub set_inner_window_size { |
|
1817
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
|
1818
|
0
|
|
|
|
|
0
|
my $height = shift; |
|
1819
|
0
|
|
|
|
|
0
|
my $width = shift; |
|
1820
|
0
|
|
|
|
|
0
|
my $location = $self->get_current_url; |
|
1821
|
|
|
|
|
|
|
|
|
1822
|
0
|
|
|
|
|
0
|
$self->execute_script( 'window.open("' . $location . '", "_blank")' ); |
|
1823
|
0
|
|
|
|
|
0
|
$self->close; |
|
1824
|
0
|
|
|
|
|
0
|
my @handles = @{ $self->get_window_handles }; |
|
|
0
|
|
|
|
|
0
|
|
|
1825
|
0
|
|
|
|
|
0
|
$self->switch_to_window( pop @handles ); |
|
1826
|
|
|
|
|
|
|
|
|
1827
|
0
|
|
|
|
|
0
|
my @resize = ( |
|
1828
|
|
|
|
|
|
|
'window.innerHeight = ' . $height, |
|
1829
|
|
|
|
|
|
|
'window.innerWidth = ' . $width, |
|
1830
|
|
|
|
|
|
|
'return 1' |
|
1831
|
|
|
|
|
|
|
); |
|
1832
|
|
|
|
|
|
|
|
|
1833
|
0
|
0
|
|
|
|
0
|
return $self->execute_script( join( ';', @resize ) ) ? 1 : 0; |
|
1834
|
|
|
|
|
|
|
} |
|
1835
|
|
|
|
|
|
|
|
|
1836
|
|
|
|
|
|
|
|
|
1837
|
|
|
|
|
|
|
sub get_local_storage_item { |
|
1838
|
2
|
|
|
2
|
1
|
13
|
my ( $self, $key ) = @_; |
|
1839
|
2
|
|
|
|
|
6
|
my $res = { 'command' => 'getLocalStorageItem' }; |
|
1840
|
2
|
|
|
|
|
8
|
my $params = { 'key' => $key }; |
|
1841
|
2
|
|
|
|
|
51
|
return $self->_execute_command( $res, $params ); |
|
1842
|
|
|
|
|
|
|
} |
|
1843
|
|
|
|
|
|
|
|
|
1844
|
|
|
|
|
|
|
|
|
1845
|
|
|
|
|
|
|
sub delete_local_storage_item { |
|
1846
|
1
|
|
|
1
|
1
|
339
|
my ( $self, $key ) = @_; |
|
1847
|
1
|
|
|
|
|
8
|
my $res = { 'command' => 'deleteLocalStorageItem' }; |
|
1848
|
1
|
|
|
|
|
7
|
my $params = { 'key' => $key }; |
|
1849
|
1
|
|
|
|
|
28
|
return $self->_execute_command( $res, $params ); |
|
1850
|
|
|
|
|
|
|
} |
|
1851
|
|
|
|
|
|
|
|
|
1852
|
|
|
|
|
|
|
sub _coerce_timeout_ms { |
|
1853
|
7
|
|
|
7
|
|
1369
|
my ($ms) = @_; |
|
1854
|
|
|
|
|
|
|
|
|
1855
|
7
|
100
|
|
|
|
21
|
if ( defined $ms ) { |
|
1856
|
6
|
|
|
|
|
17
|
return _coerce_number($ms); |
|
1857
|
|
|
|
|
|
|
} |
|
1858
|
|
|
|
|
|
|
else { |
|
1859
|
1
|
|
|
|
|
91
|
croak 'Expecting a timeout in ms'; |
|
1860
|
|
|
|
|
|
|
} |
|
1861
|
|
|
|
|
|
|
} |
|
1862
|
|
|
|
|
|
|
|
|
1863
|
|
|
|
|
|
|
sub _coerce_number { |
|
1864
|
7
|
|
|
7
|
|
422
|
my ($maybe_number) = @_; |
|
1865
|
|
|
|
|
|
|
|
|
1866
|
7
|
100
|
|
|
|
32
|
if ( Scalar::Util::looks_like_number($maybe_number) ) { |
|
1867
|
2
|
|
|
|
|
7
|
return $maybe_number + 0; |
|
1868
|
|
|
|
|
|
|
} |
|
1869
|
|
|
|
|
|
|
else { |
|
1870
|
5
|
|
|
|
|
514
|
croak "Expecting a number, not: $maybe_number"; |
|
1871
|
|
|
|
|
|
|
} |
|
1872
|
|
|
|
|
|
|
} |
|
1873
|
|
|
|
|
|
|
|
|
1874
|
|
|
|
|
|
|
1; |
|
1875
|
|
|
|
|
|
|
|
|
1876
|
|
|
|
|
|
|
__END__ |