init III
This commit is contained in:
483
Perl OTRS/Kernel/cpan-lib/Selenium/ActionChains.pm
Normal file
483
Perl OTRS/Kernel/cpan-lib/Selenium/ActionChains.pm
Normal file
@@ -0,0 +1,483 @@
|
||||
package Selenium::ActionChains;
|
||||
$Selenium::ActionChains::VERSION = '1.33';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: Action chains for Selenium::Remote::Driver
|
||||
use Moo;
|
||||
|
||||
|
||||
has 'driver' => ( is => 'ro', );
|
||||
|
||||
has 'actions' => (
|
||||
is => 'lazy',
|
||||
builder => sub { [] },
|
||||
clearer => 1,
|
||||
);
|
||||
|
||||
sub perform {
|
||||
my $self = shift;
|
||||
foreach my $action ( @{ $self->actions } ) {
|
||||
$action->();
|
||||
}
|
||||
}
|
||||
|
||||
sub click {
|
||||
my $self = shift;
|
||||
my $element = shift;
|
||||
if ($element) {
|
||||
$self->move_to_element($element);
|
||||
}
|
||||
|
||||
# left click
|
||||
push @{ $self->actions }, sub { $self->driver->click('LEFT') };
|
||||
$self;
|
||||
}
|
||||
|
||||
sub click_and_hold {
|
||||
my $self = shift;
|
||||
my $element = shift;
|
||||
if ($element) {
|
||||
$self->move_to_element($element);
|
||||
}
|
||||
push @{ $self->actions }, sub { $self->driver->button_down };
|
||||
$self;
|
||||
}
|
||||
|
||||
sub context_click {
|
||||
my $self = shift;
|
||||
my $element = shift;
|
||||
if ($element) {
|
||||
$self->move_to_element($element);
|
||||
}
|
||||
|
||||
# right click
|
||||
push @{ $self->actions }, sub { $self->driver->click('RIGHT') };
|
||||
$self;
|
||||
}
|
||||
|
||||
sub double_click {
|
||||
my $self = shift;
|
||||
my $element = shift;
|
||||
if ($element) {
|
||||
$self->move_to_element($element);
|
||||
}
|
||||
push @{ $self->actions }, sub { $self->driver->double_click };
|
||||
$self;
|
||||
}
|
||||
|
||||
sub release {
|
||||
my $self = shift;
|
||||
my $element = shift;
|
||||
if ($element) {
|
||||
$self->move_to_element($element);
|
||||
}
|
||||
push @{ $self->actions }, sub { $self->driver->button_up };
|
||||
$self;
|
||||
}
|
||||
|
||||
sub drag_and_drop {
|
||||
my $self = shift;
|
||||
my ( $source, $target ) = @_;
|
||||
$self->click_and_hold($source);
|
||||
$self->release($target);
|
||||
$self;
|
||||
}
|
||||
|
||||
sub drag_and_drop_by_offset {
|
||||
my $self = shift;
|
||||
my ( $source, $xoffset, $yoffset ) = @_;
|
||||
$self->click_and_hold($source);
|
||||
$self->move_by_offset( $xoffset, $yoffset );
|
||||
$self->release($source);
|
||||
$self;
|
||||
}
|
||||
|
||||
sub move_to_element {
|
||||
my $self = shift;
|
||||
my $element = shift;
|
||||
push @{ $self->actions },
|
||||
sub { $self->driver->move_to( element => $element ) };
|
||||
$self;
|
||||
}
|
||||
|
||||
sub move_by_offset {
|
||||
my $self = shift;
|
||||
my ( $xoffset, $yoffset ) = @_;
|
||||
push @{ $self->actions }, sub {
|
||||
$self->driver->move_to( xoffset => $xoffset, yoffset => $yoffset );
|
||||
};
|
||||
$self;
|
||||
}
|
||||
|
||||
sub move_to_element_with_offset {
|
||||
my $self = shift;
|
||||
my ( $element, $xoffset, $yoffset ) = @_;
|
||||
push @{ $self->actions }, sub {
|
||||
$self->driver->move_to(
|
||||
element => $element,
|
||||
xoffset => $xoffset,
|
||||
yoffset => $yoffset
|
||||
);
|
||||
};
|
||||
$self;
|
||||
}
|
||||
|
||||
sub key_down {
|
||||
my $self = shift;
|
||||
my ( $value, $element ) = @_;
|
||||
if ( defined($element) ) {
|
||||
$self->click($element);
|
||||
}
|
||||
push @{ $self->actions },
|
||||
sub { $self->driver->send_keys_to_active_element(@$value) };
|
||||
$self;
|
||||
}
|
||||
|
||||
sub key_up {
|
||||
my $self = shift;
|
||||
my ( $value, $element ) = @_;
|
||||
if ( defined($element) ) {
|
||||
$self->click($element);
|
||||
}
|
||||
push @{ $self->actions },
|
||||
sub { $self->driver->send_keys_to_active_element(@$value) };
|
||||
$self;
|
||||
}
|
||||
|
||||
sub send_keys {
|
||||
my $self = shift;
|
||||
my $keys = shift;
|
||||
push @{ $self->actions },
|
||||
sub { $self->driver->get_active_element->send_keys($keys) };
|
||||
$self;
|
||||
}
|
||||
|
||||
sub send_keys_to_element {
|
||||
my $self = shift;
|
||||
my ( $element, $keys ) = @_;
|
||||
push @{ $self->actions }, sub { $element->send_keys($keys) };
|
||||
$self;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::ActionChains - Action chains for Selenium::Remote::Driver
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Selenium::Remote::Driver;
|
||||
use Selenium::ActionChains;
|
||||
|
||||
my $driver = Selenium::Remote::Driver->new;
|
||||
my $action_chains = Selenium::ActionChains->new(driver => $driver);
|
||||
|
||||
$driver->get("http://www.some.web/site");
|
||||
my $elt_1 = $driver->find_element("//*[\@id='someid']");
|
||||
my $elt_2 = $driver->find_element("//*[\@id='someotherid']");
|
||||
$action_chains->send_keys_to_element($elt_1)->click($elt_2)->perform;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module implements ActionChains for Selenium, which is a way of automating
|
||||
low level interactions like mouse movements, mouse button actions , key presses and
|
||||
context menu interactions.
|
||||
The code was inspired by the L<Python implementation|http://selenium.googlecode.com/svn/trunk/docs/api/py/_modules/selenium/webdriver/common/action_chains.html#ActionChains>.
|
||||
|
||||
=for Pod::Coverage driver
|
||||
|
||||
=head1 DRAG AND DROP IS NOT WORKING !
|
||||
|
||||
The implementation contains a drag_and_drop function, but due to Selenium limitations, it is L<not working|https://code.google.com/p/selenium/issues/detail?id=3604>.
|
||||
|
||||
Nevertheless, we decided to implement the function, because eventually one day it will work.
|
||||
|
||||
In the meantime, there are workarounds that can be used to simulate drag and drop, like L<this StackOverflow post|http://stackoverflow.com/questions/29381233/how-to-simulate-html5-drag-and-drop-in-selenium-webdriver-in-python>.
|
||||
|
||||
=head1 FUNCTIONS
|
||||
|
||||
=head2 new
|
||||
|
||||
Creates a new ActionChains object. Requires a Selenium::Remote::Driver as a mandatory parameter:
|
||||
|
||||
my $driver = Selenium::Remote::Driver->new;
|
||||
my $action_chains = Selenium::ActionChains->new(driver => $driver);
|
||||
|
||||
=head2 perform
|
||||
|
||||
Performs all the actions stored in the ActionChains object in the order they were called:
|
||||
|
||||
Args: None
|
||||
|
||||
Usage:
|
||||
my $action_chains = Selenium::ActionChains->new(driver => $driver);
|
||||
# assuming that $some_element and $other_element are valid
|
||||
# Selenium::Remote::WebElement objects
|
||||
$action_chains->click($some_element);
|
||||
$action_chains->move_to_element($other_element);
|
||||
$action_chains->click($other_element);
|
||||
# click some_element, move to other_element, then click other_element
|
||||
$action_chains->perform;
|
||||
|
||||
=head2 click
|
||||
|
||||
Clicks an element. If none specified, clicks on current mouse position.
|
||||
|
||||
Args: A Selenium::Remote::WebElement object
|
||||
|
||||
Usage:
|
||||
my $element = $driver->find_element("//div[\@id='some_id']");
|
||||
$action_chains->click($element);
|
||||
|
||||
=head2 click_and_hold
|
||||
|
||||
Holds down the left mouse button on an element. If none specified, clicks on current
|
||||
mouse position.
|
||||
|
||||
Args: A Selenium::Remote::WebElement object
|
||||
|
||||
Usage:
|
||||
my $element = $driver->find_element("//div[\@id='some_id']");
|
||||
$action_chains->click_and_hold($element);
|
||||
|
||||
=head2 context_click
|
||||
|
||||
Right clicks an element. If none specified, right clicks on current mouse
|
||||
position.
|
||||
|
||||
Args: A Selenium::Remote::WebElement object
|
||||
|
||||
Usage:
|
||||
my $element = $driver->find_element("//div[\@id='some_id']");
|
||||
$action_chains->context_click($element);
|
||||
|
||||
=head2 double_click
|
||||
|
||||
Double clicks an element. If none specified, double clicks on current mouse
|
||||
position.
|
||||
|
||||
Args: A Selenium::Remote::WebElement object
|
||||
|
||||
Usage:
|
||||
my $element = $driver->find_element("//div[\@id='some_id']");
|
||||
$action_chains->double_click($element);
|
||||
|
||||
=head2 drag_and_drop - NOT WORKING
|
||||
|
||||
Holds down the left mouse button on the source element, then moves to the target
|
||||
element and releases the mouse button. IT IS NOT WORKING DUE TO CURRENT SELENIUM
|
||||
LIMITATIONS.
|
||||
|
||||
Args:
|
||||
A source Selenium::Remote::WebElement object
|
||||
A target Selenium::Remote::WebElement object
|
||||
|
||||
Usage:
|
||||
my $src_element = $driver->find_element("//*[\@class='foo']");
|
||||
my $tgt_element = $driver->find_element("//*[\@class='bar']");
|
||||
$action_chains->drag_and_drop($src_element,$tgt_element);
|
||||
|
||||
=head2 drag_and_drop_by_offset - NOT WORKING
|
||||
|
||||
Holds down the left mouse button on the source element, then moves to the offset
|
||||
specified and releases the mouse button. IT IS NOT WORKING DUE TO CURRENT SELENIUM
|
||||
LIMITATIONS.
|
||||
|
||||
Args:
|
||||
A source Selenium::Remote::WebElement object
|
||||
An integer X offset
|
||||
An integer Y offset
|
||||
|
||||
Usage:
|
||||
my $src_element = $driver->find_element("//*[\@class='foo']");
|
||||
my $xoffset = 10;
|
||||
my $yoffset = 10;
|
||||
$action_chains->drag_and_drop($src_element,$xoffset,$yoffset);
|
||||
|
||||
=head2 key_down
|
||||
|
||||
Sends key presses only, without releasing them.
|
||||
Should be used only with modifier keys (Control, Alt, Shift)
|
||||
|
||||
Args:
|
||||
An array ref to keys to send. Use the KEY constant from Selenium::Remote::WDKeys
|
||||
The element to send keys to. If none, sends keys to the current focused element
|
||||
|
||||
Usage:
|
||||
use Selenium::Remote::WDKeys 'KEYS';
|
||||
$action_chains->key_down( [ KEYS->{'alt'} ] );
|
||||
|
||||
=head2 key_up
|
||||
|
||||
Releases a mofifier key.
|
||||
|
||||
Args:
|
||||
An array ref to keys to send. Use the KEY constant from Selenium::Remote::WDKeys
|
||||
The element to send keys to. If none, sends keys to the current focused element
|
||||
|
||||
Usage:
|
||||
use Selenium::Remote::WDKeys 'KEYS';
|
||||
my $element = $driver->find_element('foo','id');
|
||||
$action_chains->key_up( [ KEYS->{'alt'} ],$element);
|
||||
|
||||
=head2 move_by_offset
|
||||
|
||||
Moves the mouse to an offset from current mouse position.
|
||||
|
||||
Args:
|
||||
An integer X offset
|
||||
An integer Y offset
|
||||
|
||||
Usage:
|
||||
$action_chains->move_by_offset(10,100);
|
||||
|
||||
=head2 move_to_element
|
||||
|
||||
Moves the mouse to the middle of an element
|
||||
|
||||
Args:
|
||||
A Selenium::Remote::WebElement to move to
|
||||
|
||||
Usage:
|
||||
my $element = $driver->find_element('foo','id');
|
||||
$action_chains->move_to_element($element);
|
||||
|
||||
=head2 move_to_element_with_offset
|
||||
|
||||
Moves the mouse by an offset of the specified element.
|
||||
Offsets are relative to the top-left corner of the element
|
||||
|
||||
Args:
|
||||
A Selenium::Remote::WebElement
|
||||
An integer X offset
|
||||
An integer Y offset
|
||||
|
||||
Usage:
|
||||
my $element = $driver->find_element('foo','id');
|
||||
$action_chains->move_to_element_with_offset($element,10,10);
|
||||
|
||||
=head2 release
|
||||
|
||||
Releases a held mouse_button
|
||||
|
||||
Args:
|
||||
A Selenium::Remote::WebElement, the element to mouse up
|
||||
|
||||
Usage:
|
||||
my $element = $driver->find_element('foo','id');
|
||||
$action_chains->release($element);
|
||||
|
||||
=head2 send_keys
|
||||
|
||||
Sends keys to the currently focused element
|
||||
|
||||
Args:
|
||||
The keys to send
|
||||
|
||||
Usage:
|
||||
$action_chains->send_keys('abcd');
|
||||
|
||||
=head2 send_keys_to_element
|
||||
|
||||
Sends keys to an element
|
||||
|
||||
Args:
|
||||
A Selenium::Remote::WebElement
|
||||
The keys to send
|
||||
|
||||
Usage:
|
||||
my $element = $driver->find_element('foo','id');
|
||||
$action_chains->send_keys_to_element($element,'abcd');
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
628
Perl OTRS/Kernel/cpan-lib/Selenium/CanStartBinary.pm
Normal file
628
Perl OTRS/Kernel/cpan-lib/Selenium/CanStartBinary.pm
Normal file
@@ -0,0 +1,628 @@
|
||||
package Selenium::CanStartBinary;
|
||||
$Selenium::CanStartBinary::VERSION = '1.33';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: Teach a WebDriver how to start its own binary aka no JRE!
|
||||
use File::Spec;
|
||||
use Selenium::CanStartBinary::ProbePort
|
||||
qw/find_open_port_above find_open_port probe_port/;
|
||||
use Selenium::Firefox::Binary qw/setup_firefox_binary_env/;
|
||||
use Selenium::Waiter qw/wait_until/;
|
||||
use Moo::Role;
|
||||
|
||||
use constant IS_WIN => $^O eq 'MSWin32';
|
||||
|
||||
|
||||
requires 'binary';
|
||||
|
||||
|
||||
requires 'binary_port';
|
||||
|
||||
|
||||
requires '_binary_args';
|
||||
|
||||
|
||||
has '_real_binary' => (
|
||||
is => 'lazy',
|
||||
builder => sub {
|
||||
my ($self) = @_;
|
||||
|
||||
if ( $self->_is_old_ff ) {
|
||||
return $self->firefox_binary;
|
||||
}
|
||||
else {
|
||||
return $self->binary;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
has '_is_old_ff' => (
|
||||
is => 'lazy',
|
||||
builder => sub {
|
||||
my ($self) = @_;
|
||||
|
||||
return $self->isa('Selenium::Firefox') && !$self->marionette_enabled;
|
||||
}
|
||||
);
|
||||
|
||||
has '+port' => (
|
||||
is => 'lazy',
|
||||
builder => sub {
|
||||
my ($self) = @_;
|
||||
|
||||
if ( $self->_real_binary ) {
|
||||
if ( $self->fixed_ports ) {
|
||||
return find_open_port( $self->binary_port );
|
||||
}
|
||||
else {
|
||||
return find_open_port_above( $self->binary_port );
|
||||
}
|
||||
}
|
||||
else {
|
||||
return 4444;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
has 'fixed_ports' => (
|
||||
is => 'lazy',
|
||||
default => sub { 0 }
|
||||
);
|
||||
|
||||
|
||||
has custom_args => (
|
||||
is => 'lazy',
|
||||
predicate => 1,
|
||||
default => sub { '' }
|
||||
);
|
||||
|
||||
has 'marionette_port' => (
|
||||
is => 'lazy',
|
||||
builder => sub {
|
||||
my ($self) = @_;
|
||||
|
||||
if ( $self->_is_old_ff ) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
if ( $self->fixed_ports ) {
|
||||
return find_open_port( $self->marionette_binary_port );
|
||||
}
|
||||
else {
|
||||
return find_open_port_above( $self->marionette_binary_port );
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
has startup_timeout => (
|
||||
is => 'lazy',
|
||||
default => sub { 10 }
|
||||
);
|
||||
|
||||
|
||||
has 'binary_mode' => (
|
||||
is => 'lazy',
|
||||
init_arg => undef,
|
||||
builder => 1,
|
||||
predicate => 1
|
||||
);
|
||||
|
||||
has 'try_binary' => (
|
||||
is => 'lazy',
|
||||
default => sub { 0 },
|
||||
trigger => sub {
|
||||
my ($self) = @_;
|
||||
$self->binary_mode if $self->try_binary;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
has 'window_title' => (
|
||||
is => 'lazy',
|
||||
init_arg => undef,
|
||||
builder => sub {
|
||||
my ($self) = @_;
|
||||
my ( undef, undef, $file ) =
|
||||
File::Spec->splitpath( $self->_real_binary );
|
||||
my $port = $self->port;
|
||||
|
||||
return $file . ':' . $port;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
has '_command' => (
|
||||
is => 'lazy',
|
||||
init_arg => undef,
|
||||
builder => sub {
|
||||
my ($self) = @_;
|
||||
return $self->_construct_command;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
has 'logfile' => (
|
||||
is => 'lazy',
|
||||
default => sub {
|
||||
return '/nul' if IS_WIN;
|
||||
return '/dev/null';
|
||||
}
|
||||
);
|
||||
|
||||
sub BUILDARGS {
|
||||
|
||||
# There's a bit of finagling to do to since we can't ensure the
|
||||
# attribute instantiation order. To decide whether we're going into
|
||||
# binary mode, we need the remote_server_addr and port. But, they're
|
||||
# both lazy and only instantiated immediately before S:R:D's
|
||||
# remote_conn attribute. Once remote_conn is set, we can't change it,
|
||||
# so we need the following order:
|
||||
#
|
||||
# parent: remote_server_addr, port
|
||||
# role: binary_mode (aka _build_binary_mode)
|
||||
# parent: remote_conn
|
||||
#
|
||||
# Since we can't force an order, we introduced try_binary which gets
|
||||
# decided during BUILDARGS to tip us off as to whether we should try
|
||||
# binary mode or not.
|
||||
my ( undef, %args ) = @_;
|
||||
|
||||
if ( !exists $args{remote_server_addr} && !exists $args{port} ) {
|
||||
$args{try_binary} = 1;
|
||||
|
||||
# Windows may throw a fit about invalid pointers if we try to
|
||||
# connect to localhost instead of 127.1
|
||||
$args{remote_server_addr} = '127.0.0.1';
|
||||
}
|
||||
else {
|
||||
$args{try_binary} = 0;
|
||||
$args{binary_mode} = 0;
|
||||
}
|
||||
|
||||
return {%args};
|
||||
}
|
||||
|
||||
sub _build_binary_mode {
|
||||
my ($self) = @_;
|
||||
|
||||
# We don't know what to do without a binary driver to start up
|
||||
return unless $self->_real_binary;
|
||||
|
||||
# Either the user asked for 4444, or we couldn't find an open port
|
||||
my $port = $self->port + 0;
|
||||
return if $port == 4444;
|
||||
if ( $self->fixed_ports && $port == 0 ) {
|
||||
die 'port '
|
||||
. $self->binary_port
|
||||
. ' is not free and have requested fixed ports';
|
||||
}
|
||||
|
||||
$self->_handle_firefox_setup($port);
|
||||
|
||||
system( $self->_command );
|
||||
|
||||
my $success =
|
||||
wait_until { probe_port($port) } timeout => $self->startup_timeout;
|
||||
if ($success) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
die 'Unable to connect to the '
|
||||
. $self->_real_binary
|
||||
. ' binary on port '
|
||||
. $port;
|
||||
}
|
||||
}
|
||||
|
||||
sub _handle_firefox_setup {
|
||||
my ( $self, $port ) = @_;
|
||||
|
||||
# This is a no-op for other browsers
|
||||
return unless $self->isa('Selenium::Firefox');
|
||||
|
||||
my $user_profile =
|
||||
$self->has_firefox_profile
|
||||
? $self->firefox_profile
|
||||
: 0;
|
||||
|
||||
my $profile =
|
||||
setup_firefox_binary_env( $port, $self->marionette_port, $user_profile );
|
||||
|
||||
if ( $self->_is_old_ff ) {
|
||||
|
||||
# For non-geckodriver/non-marionette, we want to get rid of
|
||||
# the profile so that we don't accidentally zip it and encode
|
||||
# it down the line while Firefox is trying to read from it.
|
||||
$self->clear_firefox_profile if $self->has_firefox_profile;
|
||||
}
|
||||
else {
|
||||
# For geckodriver/marionette, we keep the enhanced profile around because
|
||||
# we need to send it to geckodriver as a zipped b64-encoded
|
||||
# directory.
|
||||
$self->firefox_profile($profile);
|
||||
}
|
||||
}
|
||||
|
||||
sub shutdown_binary {
|
||||
my ($self) = @_;
|
||||
|
||||
if ( $self->auto_close && defined $self->session_id ) {
|
||||
$self->quit();
|
||||
}
|
||||
|
||||
if ( $self->has_binary_mode && $self->binary_mode ) {
|
||||
|
||||
# Tell the binary itself to shutdown
|
||||
my $port = $self->port;
|
||||
my $ua = $self->ua;
|
||||
$ua->get( 'http://127.0.0.1:' . $port . '/wd/hub/shutdown' );
|
||||
|
||||
# Close the orphaned command windows on windows
|
||||
$self->shutdown_windows_binary;
|
||||
$self->shutdown_unix_binary;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub shutdown_unix_binary {
|
||||
my ($self) = @_;
|
||||
if (!IS_WIN) {
|
||||
my $cmd = "lsof -t -i :".$self->port();
|
||||
my ( $pid ) = grep { $_ && $_ ne $$ } split( /\s+/, scalar `$cmd` );
|
||||
if ($pid) {
|
||||
print "Killing Driver PID $pid listening on port "
|
||||
. $self->port . "...\n";
|
||||
eval { kill 'KILL', $pid };
|
||||
warn
|
||||
"Could not kill driver process! you may have to clean up manually."
|
||||
if $@;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub shutdown_windows_binary {
|
||||
my ($self) = @_;
|
||||
|
||||
if (IS_WIN) {
|
||||
if ( $self->_is_old_ff ) {
|
||||
|
||||
# FIXME: Blech, handle a race condition that kills the
|
||||
# driver before it's finished cleaning up its sessions. In
|
||||
# particular, when the perl process ends, it wants to
|
||||
# clean up the temp directory it created for the Firefox
|
||||
# profile. But, if the Firefox process is still running,
|
||||
# it will have a lock on the temp profile directory, and
|
||||
# perl will get upset. This "solution" is _very_ bad.
|
||||
sleep(2);
|
||||
|
||||
# Firefox doesn't have a Driver/Session architecture - the
|
||||
# only thing running is Firefox itself, so there's no
|
||||
# other task to kill.
|
||||
return;
|
||||
}
|
||||
system( 'taskkill /FI "WINDOWTITLE eq '
|
||||
. $self->window_title
|
||||
. '" > nul 2>&1' );
|
||||
}
|
||||
}
|
||||
|
||||
sub DEMOLISH {
|
||||
my ( $self, $in_gd ) = @_;
|
||||
|
||||
# if we're in global destruction, all bets are off.
|
||||
return if $in_gd;
|
||||
$self->shutdown_binary;
|
||||
}
|
||||
|
||||
sub _construct_command {
|
||||
my ($self) = @_;
|
||||
my $executable = $self->_real_binary;
|
||||
|
||||
# Executable path names may have spaces
|
||||
$executable = '"' . $executable . '"';
|
||||
|
||||
# The different binaries take different arguments for proper setup
|
||||
$executable .= $self->_binary_args;
|
||||
if ( $self->has_custom_args ) {
|
||||
$executable .= ' ' . $self->custom_args;
|
||||
}
|
||||
|
||||
# Handle Windows vs Unix discrepancies for invoking shell commands
|
||||
my ( $prefix, $suffix ) = ( $self->_cmd_prefix, $self->_cmd_suffix );
|
||||
return join( ' ', ( $prefix, $executable, $suffix ) );
|
||||
}
|
||||
|
||||
sub _cmd_prefix {
|
||||
my ($self) = @_;
|
||||
|
||||
my $prefix = '';
|
||||
if (IS_WIN) {
|
||||
$prefix = 'start "' . $self->window_title . '"';
|
||||
|
||||
if ( $self->_is_old_ff ) {
|
||||
|
||||
# For older versions of Firefox that run without
|
||||
# marionette, the command we're running actually starts up
|
||||
# the browser itself, so we don't want to minimize it.
|
||||
return $prefix;
|
||||
}
|
||||
else {
|
||||
# If we're firefox with marionette, or any other browser,
|
||||
# the command we're running is the driver, and we don't
|
||||
# need want the command window in the foreground.
|
||||
return $prefix . ' /MIN ';
|
||||
}
|
||||
}
|
||||
return $prefix;
|
||||
}
|
||||
|
||||
sub _cmd_suffix {
|
||||
my ($self) = @_;
|
||||
return " > " . $self->logfile . " 2>&1 " if IS_WIN;
|
||||
return " > " . $self->logfile . " 2>&1 &";
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::CanStartBinary - Teach a WebDriver how to start its own binary aka no JRE!
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This role takes care of the details for starting up a Webdriver
|
||||
instance. It does not do any downloading or installation of any sort -
|
||||
you're still responsible for obtaining and installing the necessary
|
||||
binaries into your C<$PATH> for this role to find. You may be
|
||||
interested in L<Selenium::Chrome>, L<Selenium::Firefox>, or
|
||||
L<Selenium::PhantomJS> if you're looking for classes that already
|
||||
consume this role.
|
||||
|
||||
The role determines whether or not it should try to do its own magic
|
||||
based on whether the consuming class is instantiated with a
|
||||
C<remote_server_addr> and/or C<port>.
|
||||
|
||||
# We'll start up the Chrome binary for you
|
||||
my $chrome_via_binary = Selenium::Chrome->new;
|
||||
|
||||
# Look for a selenium server running on 4444.
|
||||
my $chrome_via_server = Selenium::Chrome->new( port => 4444 );
|
||||
|
||||
If they're missing, we assume the user wants to use a webdriver
|
||||
directly and act accordingly. We handle finding the proper associated
|
||||
binary (or you can specify it with L</binary>), figuring out what
|
||||
arguments it wants, setting up any necessary environments, and
|
||||
starting up the binary.
|
||||
|
||||
There's a number of TODOs left over - namely Windows support is
|
||||
severely lacking, and we're pretty naive when we attempt to locate the
|
||||
executables on our own.
|
||||
|
||||
In the following documentation, C<required> refers to when you're
|
||||
consuming the role, not the C<required> when you're instantiating a
|
||||
class that has already consumed the role.
|
||||
|
||||
=head1 ATTRIBUTES
|
||||
|
||||
=head2 binary
|
||||
|
||||
Required: Specify the path to the executable in question, or the name
|
||||
of the executable for us to find via L<File::Which/which>.
|
||||
|
||||
=head2 binary_port
|
||||
|
||||
Required: Specify a default port that for the webdriver binary to try
|
||||
to bind to. If that port is unavailable, we'll probe above that port
|
||||
until we find a valid one.
|
||||
|
||||
=head2 _binary_args
|
||||
|
||||
Required: Specify the arguments that the particular binary needs in
|
||||
order to start up correctly. In particular, you may need to tell the
|
||||
binary about the proper port when we start it up, or that it should
|
||||
use a particular prefix to match up with the behavior of the Remote
|
||||
Driver server.
|
||||
|
||||
If your binary doesn't need any arguments, just have the default be an
|
||||
empty string.
|
||||
|
||||
=head2 port
|
||||
|
||||
The role will attempt to determine the proper port for us. Consuming
|
||||
roles should set a default port in L</binary_port> at which we will
|
||||
begin searching for an open port.
|
||||
|
||||
Note that if we cannot locate a suitable L</binary>, port will be set
|
||||
to 4444 so we can attempt to look for a Selenium server at
|
||||
C<127.0.0.1:4444>.
|
||||
|
||||
=head2 fixed_ports
|
||||
|
||||
Optional: By default, if binary_port and marionette_port are not free
|
||||
a higher free port is probed and acquired if possible, until a free one
|
||||
if found or a timeout is exceeded.
|
||||
|
||||
my $driver1 = Selenium::Chrome->new;
|
||||
my $driver2 = Selenium::Chrome->new( port => 1234 );
|
||||
|
||||
The default behavior can be overridden. In this case, only the default
|
||||
or given binary_port and marionette_port are probed, without probing
|
||||
higher ports. This ensures that either the default or given port will be
|
||||
assigned, or no port will be assigned at all.
|
||||
|
||||
my $driver1 = Selenium::Chrome->new( fixed_ports => 1 );
|
||||
my $driver2 = Selenium::Chrome->new( port => 1234, fixed_ports => 1);
|
||||
|
||||
=head2 custom_args
|
||||
|
||||
Optional: If you want to pass additional options to the binary when it
|
||||
starts up, you can add that here. For example, if your binary accepts
|
||||
an argument on the command line like C<--log-path=/path/to/log>, and
|
||||
you'd like to specify that the binary uses that option, you could do:
|
||||
|
||||
my $chrome = Selenium::Chrome->new(
|
||||
custom_args => '--log-path=/path/to/log'
|
||||
);
|
||||
|
||||
To specify multiple arguments, just include them all in the string.
|
||||
|
||||
=head2 startup_timeout
|
||||
|
||||
Optional: you can modify how long we will wait for the binary to start
|
||||
up. By default, we will start the binary and check the intended
|
||||
destination port for 10 seconds before giving up. If the machine
|
||||
you're using to run your browsers is slower or smaller, you may need
|
||||
to increase this timeout.
|
||||
|
||||
The following:
|
||||
|
||||
my $f = Selenium::Firefox->new(
|
||||
startup_timeout => 60
|
||||
);
|
||||
|
||||
will wait up to 60 seconds for the firefox binary to respond on the
|
||||
proper port. To use this constructor option, you should specify a time
|
||||
in seconds as an integer, and it will be passed to the arguments
|
||||
section of a L<Selenium::Waiter/wait_until> subroutine call.
|
||||
|
||||
=head2 binary_mode
|
||||
|
||||
Mostly intended for internal use, its builder coordinates all the side
|
||||
effects of interacting with the binary: locating the executable,
|
||||
finding an open port, setting up the environment, shelling out to
|
||||
start the binary, and ensuring that the webdriver is listening on the
|
||||
correct port.
|
||||
|
||||
If all of the above steps pass, it will return truthy after
|
||||
instantiation. If any of them fail, it should return falsy and the
|
||||
class should attempt normal L<Selenium::Remote::Driver> behavior.
|
||||
|
||||
=head2 window_title
|
||||
|
||||
Intended for internal use: this will build us a unique title for the
|
||||
background binary process of the Webdriver. Then, when we're cleaning
|
||||
up, we know what the window title is that we're going to C<taskkill>.
|
||||
|
||||
=head2 command
|
||||
|
||||
Intended for internal use: this read-only attribute is built by us,
|
||||
but it can be useful after instantiation to see exactly what command
|
||||
was run to start the webdriver server.
|
||||
|
||||
my $f = Selenium::Firefox->new;
|
||||
say $f->_command;
|
||||
|
||||
=head2 logfile
|
||||
|
||||
Normally we log what occurs in the driver to /dev/null (or /nul on windows).
|
||||
Setting this will redirect it to the provided file.
|
||||
|
||||
=for Pod::Coverage *EVERYTHING*
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Chrome|Selenium::Chrome>
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Firefox|Selenium::Firefox>
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::PhantomJS|Selenium::PhantomJS>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
172
Perl OTRS/Kernel/cpan-lib/Selenium/CanStartBinary/FindBinary.pm
Normal file
172
Perl OTRS/Kernel/cpan-lib/Selenium/CanStartBinary/FindBinary.pm
Normal file
@@ -0,0 +1,172 @@
|
||||
package Selenium::CanStartBinary::FindBinary;
|
||||
$Selenium::CanStartBinary::FindBinary::VERSION = '1.33';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: Coercions for finding webdriver binaries on your system
|
||||
use Cwd qw/abs_path/;
|
||||
use File::Which qw/which/;
|
||||
use IO::Socket::INET;
|
||||
use Selenium::Firefox::Binary qw/firefox_path/;
|
||||
|
||||
require Exporter;
|
||||
our @ISA = qw/Exporter/;
|
||||
our @EXPORT_OK = qw/coerce_simple_binary coerce_firefox_binary/;
|
||||
|
||||
use constant IS_WIN => $^O eq 'MSWin32';
|
||||
|
||||
|
||||
sub coerce_simple_binary {
|
||||
my ($executable) = @_;
|
||||
|
||||
my $manual_binary = _validate_manual_binary($executable);
|
||||
if ($manual_binary) {
|
||||
return $manual_binary;
|
||||
}
|
||||
else {
|
||||
return _naive_find_binary($executable);
|
||||
}
|
||||
}
|
||||
|
||||
sub coerce_firefox_binary {
|
||||
my ($executable) = @_;
|
||||
|
||||
my $manual_binary = _validate_manual_binary($executable);
|
||||
if ($manual_binary) {
|
||||
return $manual_binary;
|
||||
}
|
||||
else {
|
||||
return firefox_path();
|
||||
}
|
||||
}
|
||||
|
||||
sub _validate_manual_binary {
|
||||
my ($executable) = @_;
|
||||
|
||||
my $abs_executable = eval {
|
||||
my $path = abs_path($executable);
|
||||
die unless -e $path;
|
||||
$path;
|
||||
};
|
||||
|
||||
if ($abs_executable) {
|
||||
if ( -x $abs_executable || IS_WIN ) {
|
||||
return $abs_executable;
|
||||
}
|
||||
else {
|
||||
die 'The binary at '
|
||||
. $executable
|
||||
. ' is not executable. Choose the correct file or chmod +x it as needed.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub _naive_find_binary {
|
||||
my ($executable) = @_;
|
||||
|
||||
my $naive_binary = which($executable);
|
||||
if ( defined $naive_binary ) {
|
||||
return $naive_binary;
|
||||
}
|
||||
else {
|
||||
warn qq(Unable to find the $executable binary in your \$PATH.);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::CanStartBinary::FindBinary - Coercions for finding webdriver binaries on your system
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=for Pod::Coverage *EVERYTHING*
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
143
Perl OTRS/Kernel/cpan-lib/Selenium/CanStartBinary/ProbePort.pm
Normal file
143
Perl OTRS/Kernel/cpan-lib/Selenium/CanStartBinary/ProbePort.pm
Normal file
@@ -0,0 +1,143 @@
|
||||
package Selenium::CanStartBinary::ProbePort;
|
||||
$Selenium::CanStartBinary::ProbePort::VERSION = '1.33';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: Utility functions for finding open ports to eventually bind to
|
||||
|
||||
use IO::Socket::INET;
|
||||
use Selenium::Waiter qw/wait_until/;
|
||||
|
||||
require Exporter;
|
||||
our @ISA = qw/Exporter/;
|
||||
our @EXPORT_OK = qw/find_open_port_above find_open_port probe_port/;
|
||||
|
||||
|
||||
sub find_open_port_above {
|
||||
my ($port) = @_;
|
||||
|
||||
my $free_port = wait_until {
|
||||
if ( probe_port($port) ) {
|
||||
$port++;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return $port;
|
||||
}
|
||||
};
|
||||
|
||||
return $free_port;
|
||||
}
|
||||
|
||||
sub find_open_port {
|
||||
my ($port) = @_;
|
||||
|
||||
probe_port($port) ? return 0 : return $port;
|
||||
}
|
||||
|
||||
sub probe_port {
|
||||
my ($port) = @_;
|
||||
|
||||
return IO::Socket::INET->new(
|
||||
PeerAddr => '127.0.0.1',
|
||||
PeerPort => $port,
|
||||
Timeout => 3
|
||||
);
|
||||
}
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::CanStartBinary::ProbePort - Utility functions for finding open ports to eventually bind to
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=for Pod::Coverage *EVERYTHING*
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
228
Perl OTRS/Kernel/cpan-lib/Selenium/Chrome.pm
Normal file
228
Perl OTRS/Kernel/cpan-lib/Selenium/Chrome.pm
Normal file
@@ -0,0 +1,228 @@
|
||||
package Selenium::Chrome;
|
||||
$Selenium::Chrome::VERSION = '1.33';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: Use ChromeDriver without a Selenium server
|
||||
use Moo;
|
||||
use Selenium::CanStartBinary::FindBinary qw/coerce_simple_binary/;
|
||||
extends 'Selenium::Remote::Driver';
|
||||
|
||||
|
||||
has '+browser_name' => (
|
||||
is => 'ro',
|
||||
default => sub { 'chrome' }
|
||||
);
|
||||
|
||||
|
||||
has 'binary' => (
|
||||
is => 'lazy',
|
||||
coerce => \&coerce_simple_binary,
|
||||
default => sub { 'chromedriver' },
|
||||
predicate => 1
|
||||
);
|
||||
|
||||
|
||||
has 'binary_port' => (
|
||||
is => 'lazy',
|
||||
default => sub { 9515 }
|
||||
);
|
||||
|
||||
has '_binary_args' => (
|
||||
is => 'lazy',
|
||||
builder => sub {
|
||||
my ($self) = @_;
|
||||
|
||||
my $context = $self->wd_context_prefix;
|
||||
$context =~ s{^/}{};
|
||||
|
||||
return ' --port=' . $self->port . ' --url-base=' . $context . ' ';
|
||||
}
|
||||
);
|
||||
|
||||
with 'Selenium::CanStartBinary';
|
||||
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Chrome - Use ChromeDriver without a Selenium server
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
my $driver = Selenium::Chrome->new;
|
||||
# when you're done
|
||||
$driver->shutdown_binary;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This class allows you to use the ChromeDriver without needing the JRE
|
||||
or a selenium server running. When you refrain from passing the
|
||||
C<remote_server_addr> and C<port> arguments, we will search for the
|
||||
chromedriver executable binary in your $PATH. We'll try to start the
|
||||
binary connect to it, shutting it down at the end of the test.
|
||||
|
||||
If the chromedriver binary is not found, we'll fall back to the
|
||||
default L<Selenium::Remote::Driver> behavior of assuming defaults of
|
||||
127.0.0.1:4444 after waiting a few seconds.
|
||||
|
||||
If you specify a remote server address, or a port, we'll assume you
|
||||
know what you're doing and take no additional behavior.
|
||||
|
||||
If you're curious whether your Selenium::Chrome instance is using a
|
||||
separate ChromeDriver binary, or through the selenium server, you can
|
||||
check the C<binary_mode> attr after instantiation.
|
||||
|
||||
=head1 ATTRIBUTES
|
||||
|
||||
=head2 binary
|
||||
|
||||
Optional: specify the path to your binary. If you don't specify
|
||||
anything, we'll try to find it on our own via L<File::Which/which>.
|
||||
|
||||
=head2 binary_port
|
||||
|
||||
Optional: specify the port that we should bind to. If you don't
|
||||
specify anything, we'll default to the driver's default port. Since
|
||||
there's no a priori guarantee that this will be an open port, this is
|
||||
_not_ necessarily the port that we end up using - if the port here is
|
||||
already bound, we'll search above it until we find an open one.
|
||||
|
||||
See L<Selenium::CanStartBinary/port> for more details, and
|
||||
L<Selenium::Remote::Driver/port> after instantiation to see what the
|
||||
actual port turned out to be.
|
||||
|
||||
=head2 custom_args
|
||||
|
||||
Optional: specify any additional command line arguments you'd like
|
||||
invoked during the binary startup. See
|
||||
L<Selenium::CanStartBinary/custom_args> for more information.
|
||||
|
||||
=head2 startup_timeout
|
||||
|
||||
Optional: specify how long to wait for the binary to start itself and
|
||||
listen on its port. The default duration is arbitrarily 10 seconds. It
|
||||
accepts an integer number of seconds to wait: the following will wait
|
||||
up to 20 seconds:
|
||||
|
||||
Selenium::Chrome->new( startup_timeout => 20 );
|
||||
|
||||
See L<Selenium::CanStartBinary/startup_timeout> for more information.
|
||||
|
||||
=head2 fixed_ports
|
||||
|
||||
Optional: Throw instead of searching for additional ports; see
|
||||
L<Selenium::CanStartBinary/fixed_ports> for more info.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 shutdown_binary
|
||||
|
||||
Call this method instead of L<Selenium::Remote::Driver/quit> to ensure
|
||||
that the binary executable is also closed, instead of simply closing
|
||||
the browser itself. If the browser is still around, it will call
|
||||
C<quit> for you. After that, it will try to shutdown the browser
|
||||
binary by making a GET to /shutdown and on Windows, it will attempt to
|
||||
do a C<taskkill> on the binary CMD window.
|
||||
|
||||
$self->shutdown_binary;
|
||||
|
||||
It doesn't take any arguments, and it doesn't return anything.
|
||||
|
||||
We do our best to call this when the C<$driver> option goes out of
|
||||
scope, but if that happens during global destruction, there's nothing
|
||||
we can do.
|
||||
|
||||
=for Pod::Coverage has_binary
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
228
Perl OTRS/Kernel/cpan-lib/Selenium/Edge.pm
Normal file
228
Perl OTRS/Kernel/cpan-lib/Selenium/Edge.pm
Normal file
@@ -0,0 +1,228 @@
|
||||
package Selenium::Edge;
|
||||
$Selenium::Edge::VERSION = '1.33';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: Use EdgeDriver without a Selenium server
|
||||
use Moo;
|
||||
use Selenium::CanStartBinary::FindBinary qw/coerce_simple_binary/;
|
||||
extends 'Selenium::Remote::Driver';
|
||||
|
||||
|
||||
has '+browser_name' => (
|
||||
is => 'ro',
|
||||
default => sub { 'MicrosoftEdge' }
|
||||
);
|
||||
|
||||
|
||||
has 'binary' => (
|
||||
is => 'lazy',
|
||||
coerce => \&coerce_simple_binary,
|
||||
default => sub { 'MicrosoftWebDriver.exe' },
|
||||
predicate => 1
|
||||
);
|
||||
|
||||
|
||||
has 'binary_port' => (
|
||||
is => 'lazy',
|
||||
default => sub { 17556 }
|
||||
);
|
||||
|
||||
has '_binary_args' => (
|
||||
is => 'lazy',
|
||||
builder => sub {
|
||||
my ($self) = @_;
|
||||
|
||||
my $context = $self->wd_context_prefix;
|
||||
$context =~ s{^/}{};
|
||||
|
||||
return ' --port=' . $self->port . ' --url-base=' . $context . ' ';
|
||||
}
|
||||
);
|
||||
|
||||
with 'Selenium::CanStartBinary';
|
||||
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Edge - Use EdgeDriver without a Selenium server
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
my $driver = Selenium::Edge->new;
|
||||
# when you're done
|
||||
$driver->shutdown_binary;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This class allows you to use the EdgeDriver without needing the JRE
|
||||
or a selenium server running. When you refrain from passing the
|
||||
C<remote_server_addr> and C<port> arguments, we will search for the
|
||||
edgedriver executable binary in your $PATH. We'll try to start the
|
||||
binary connect to it, shutting it down at the end of the test.
|
||||
|
||||
If the MicrosoftWebDriver binary is not found, we'll fall back to the
|
||||
default L<Selenium::Remote::Driver> behavior of assuming defaults of
|
||||
127.0.0.1:4444 after waiting a few seconds.
|
||||
|
||||
If you specify a remote server address, or a port, we'll assume you
|
||||
know what you're doing and take no additional behavior.
|
||||
|
||||
If you're curious whether your Selenium::Edge instance is using a
|
||||
separate MicrosoftWebDriver binary, or through the selenium server, you can
|
||||
check the C<binary_mode> attr after instantiation.
|
||||
|
||||
=head1 ATTRIBUTES
|
||||
|
||||
=head2 binary
|
||||
|
||||
Optional: specify the path to your binary. If you don't specify
|
||||
anything, we'll try to find it on our own via L<File::Which/which>.
|
||||
|
||||
=head2 binary_port
|
||||
|
||||
Optional: specify the port that we should bind to. If you don't
|
||||
specify anything, we'll default to the driver's default port. Since
|
||||
there's no a priori guarantee that this will be an open port, this is
|
||||
_not_ necessarily the port that we end up using - if the port here is
|
||||
already bound, we'll search above it until we find an open one.
|
||||
|
||||
See L<Selenium::CanStartBinary/port> for more details, and
|
||||
L<Selenium::Remote::Driver/port> after instantiation to see what the
|
||||
actual port turned out to be.
|
||||
|
||||
=head2 custom_args
|
||||
|
||||
Optional: specify any additional command line arguments you'd like
|
||||
invoked during the binary startup. See
|
||||
L<Selenium::CanStartBinary/custom_args> for more information.
|
||||
|
||||
=head2 startup_timeout
|
||||
|
||||
Optional: specify how long to wait for the binary to start itself and
|
||||
listen on its port. The default duration is arbitrarily 10 seconds. It
|
||||
accepts an integer number of seconds to wait: the following will wait
|
||||
up to 20 seconds:
|
||||
|
||||
Selenium::Edge->new( startup_timeout => 20 );
|
||||
|
||||
See L<Selenium::CanStartBinary/startup_timeout> for more information.
|
||||
|
||||
=head2 fixed_ports
|
||||
|
||||
Optional: Throw instead of searching for additional ports; see
|
||||
L<Selenium::CanStartBinary/fixed_ports> for more info.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 shutdown_binary
|
||||
|
||||
Call this method instead of L<Selenium::Remote::Driver/quit> to ensure
|
||||
that the binary executable is also closed, instead of simply closing
|
||||
the browser itself. If the browser is still around, it will call
|
||||
C<quit> for you. After that, it will try to shutdown the browser
|
||||
binary by making a GET to /shutdown and on Windows, it will attempt to
|
||||
do a C<taskkill> on the binary CMD window.
|
||||
|
||||
$self->shutdown_binary;
|
||||
|
||||
It doesn't take any arguments, and it doesn't return anything.
|
||||
|
||||
We do our best to call this when the C<$driver> option goes out of
|
||||
scope, but if that happens during global destruction, there's nothing
|
||||
we can do.
|
||||
|
||||
=for Pod::Coverage has_binary
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
464
Perl OTRS/Kernel/cpan-lib/Selenium/Firefox.pm
Normal file
464
Perl OTRS/Kernel/cpan-lib/Selenium/Firefox.pm
Normal file
@@ -0,0 +1,464 @@
|
||||
package Selenium::Firefox;
|
||||
$Selenium::Firefox::VERSION = '1.33';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: Use FirefoxDriver without a Selenium server
|
||||
use Moo;
|
||||
use Carp;
|
||||
use Selenium::Firefox::Binary qw/firefox_path/;
|
||||
use Selenium::CanStartBinary::FindBinary
|
||||
qw/coerce_simple_binary coerce_firefox_binary/;
|
||||
extends 'Selenium::Remote::Driver';
|
||||
|
||||
|
||||
has '+browser_name' => (
|
||||
is => 'ro',
|
||||
default => sub { 'firefox' }
|
||||
);
|
||||
|
||||
|
||||
has 'binary' => (
|
||||
is => 'lazy',
|
||||
coerce => \&coerce_simple_binary,
|
||||
default => sub { 'geckodriver' },
|
||||
predicate => 1
|
||||
);
|
||||
|
||||
|
||||
has 'binary_port' => (
|
||||
is => 'lazy',
|
||||
default => sub { 9090 }
|
||||
);
|
||||
|
||||
|
||||
has '_binary_args' => (
|
||||
is => 'lazy',
|
||||
builder => sub {
|
||||
my ($self) = @_;
|
||||
|
||||
if ( $self->marionette_enabled ) {
|
||||
my $args =
|
||||
' --port '
|
||||
. $self->port
|
||||
. ' --marionette-port '
|
||||
. $self->marionette_binary_port
|
||||
. ' --binary "'
|
||||
. $self->firefox_binary . '"';
|
||||
|
||||
return $args;
|
||||
}
|
||||
else {
|
||||
return ' -no-remote';
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
has '+wd_context_prefix' => (
|
||||
is => 'ro',
|
||||
default => sub {
|
||||
my ($self) = @_;
|
||||
|
||||
if ( $self->marionette_enabled ) {
|
||||
return '';
|
||||
}
|
||||
else {
|
||||
return '/hub';
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
has 'marionette_binary_port' => (
|
||||
is => 'lazy',
|
||||
default => sub { 2828 }
|
||||
);
|
||||
|
||||
|
||||
has 'marionette_enabled' => (
|
||||
is => 'lazy',
|
||||
default => 1
|
||||
);
|
||||
|
||||
|
||||
has 'firefox_binary' => (
|
||||
is => 'lazy',
|
||||
coerce => \&coerce_firefox_binary,
|
||||
predicate => 1,
|
||||
builder => 'firefox_path'
|
||||
);
|
||||
|
||||
has '_execute_script_suffix' => (
|
||||
is => 'lazy',
|
||||
default => 'Gecko'
|
||||
);
|
||||
|
||||
|
||||
sub get_context {
|
||||
my $self = shift;
|
||||
|
||||
if ( $self->_is_old_ff ) {
|
||||
return 0;
|
||||
}
|
||||
my $res = { 'command' => 'getContext' };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub set_context {
|
||||
my ( $self, $context ) = @_;
|
||||
|
||||
if ( $self->_is_old_ff ) {
|
||||
return 0;
|
||||
}
|
||||
if ( not defined $context ) {
|
||||
croak "Expecting context";
|
||||
}
|
||||
if ( $context !~ m/chrome|content/i ) {
|
||||
croak "Expecting context value: chrome or content";
|
||||
}
|
||||
my $res = { 'command' => 'setContext' };
|
||||
return $self->_execute_command( $res, { context => $context } );
|
||||
}
|
||||
|
||||
with 'Selenium::CanStartBinary';
|
||||
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Firefox - Use FirefoxDriver without a Selenium server
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
# These two are the same, and will only work with Firefox 48+
|
||||
my $driver = Selenium::Firefox->new;
|
||||
$driver = Selenium::Firefox->new( marionette_enabled => 1 );
|
||||
|
||||
#Do stuff...
|
||||
|
||||
$driver->shutdown_binary;
|
||||
|
||||
# For Firefox 47 and older, disable marionette:
|
||||
$driver = Selenium::Firefox->new( marionette_enabled => 0 );
|
||||
$driver->shutdown_binary;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
B<Breaking Changes:> There are breaking changes in v1.0+ of this
|
||||
module if you're using it to start FF47; please see L</"BREAKING
|
||||
CHANGES">. You can ignore this if you're using v1.0+ of this module to
|
||||
start FF48.
|
||||
|
||||
This class allows you to use the FirefoxDriver without needing the JRE
|
||||
or a selenium server running. Unlike starting up an instance of
|
||||
S::R::D, do not pass the C<remote_server_addr> and C<port> arguments,
|
||||
and we will search for the Firefox executable in your C<$PATH>. We'll
|
||||
try to start the binary, connect to it, and shut it down at the end of
|
||||
the test.
|
||||
|
||||
If the Firefox application is not found in the expected places, we'll
|
||||
fall back to the default L<Selenium::Remote::Driver> behavior of
|
||||
assuming defaults of 127.0.0.1:4444 after waiting a few seconds.
|
||||
|
||||
If you specify a remote server address, or a port, our assumption is
|
||||
that you are doing standard S::R::D behavior and we will not attempt
|
||||
any binary startup.
|
||||
|
||||
If you're curious whether your Selenium::Firefox instance is using a
|
||||
separate Firefox binary, or through the selenium server, you can check
|
||||
the value of the C<binary_mode> attr after instantiation.
|
||||
|
||||
=head1 ATTRIBUTES
|
||||
|
||||
=head2 binary
|
||||
|
||||
Optional: specify the path to the C<geckodriver> binary - this is NOT
|
||||
the path to the Firefox browser. To specify the path to your Firefox
|
||||
browser binary, see the L</firefox_binary> attr.
|
||||
|
||||
For Firefox 48 and greater, this is the path to your C<geckodriver>
|
||||
executable. If you don't specify anything, we'll search for
|
||||
C<geckodriver> in your C<$PATH>.
|
||||
|
||||
For Firefox 47 and older, this attribute does not apply, because the
|
||||
older FF browsers do not use the separate driver binary startup.
|
||||
|
||||
=head2 binary_port
|
||||
|
||||
Optional: specify the port that we should bind to. If you don't
|
||||
specify anything, we'll default to the driver's default port. Since
|
||||
there's no a priori guarantee that this will be an open port, this is
|
||||
_not_ necessarily the port that we end up using - if the port here is
|
||||
already bound, we'll search above it until we find an open one.
|
||||
|
||||
See L<Selenium::CanStartBinary/port> for more details, and
|
||||
L<Selenium::Remote::Driver/port> after instantiation to see what the
|
||||
actual port turned out to be.
|
||||
|
||||
=head2 firefox_profile
|
||||
|
||||
Optional: Pass in an instance of L<Selenium::Firefox::Profile>
|
||||
pre-configured as you please. The preferences you specify will be
|
||||
merged with the ones necessary for setting up webdriver, and as a
|
||||
result some options may be overwritten or ignored.
|
||||
|
||||
my $profile = Selenium::Firefox::Profile->new;
|
||||
my $firefox = Selenium::Firefox->new(
|
||||
firefox_profile => $profile
|
||||
);
|
||||
|
||||
=head2 marionette_binary_port
|
||||
|
||||
Optional: specify the port that we should bind marionette to. If you don't
|
||||
specify anything, we'll default to the marionette's default port. Since
|
||||
there's no a priori guarantee that this will be an open port, this is
|
||||
_not_ necessarily the port that we end up using - if the port here is
|
||||
already bound, we'll search above it until we find an open one.
|
||||
|
||||
Selenium::Firefox->new(
|
||||
marionette_enabled => 1,
|
||||
marionette_binary_port => 12345,
|
||||
);
|
||||
|
||||
Attempting to specify a C<marionette_binary_port> in conjunction with
|
||||
setting C<marionette_enabled> does not make sense and will most likely
|
||||
not do anything useful.
|
||||
|
||||
=head2 marionette_enabled
|
||||
|
||||
Optional: specify whether
|
||||
L<marionette|https://developer.mozilla.org/en-US/docs/Mozilla/QA/Marionette>
|
||||
should be enabled or not. By default, marionette is enabled, which
|
||||
assumes you are running with Firefox 48 or newer. To use this module to
|
||||
start Firefox 47 or older, you must pass C<< marionette_enabled => 0 >>.
|
||||
|
||||
my $ff48 = Selenium::Firefox->new( marionette_enabled => 1 ); # defaults to 1
|
||||
my $ff47 = Selenium::Firefox->new( marionette_enabled => 0 );
|
||||
|
||||
=head2 firefox_binary
|
||||
|
||||
Optional: specify the path to the Firefox browser executable. Although
|
||||
we will attempt to locate this in your C<$PATH>, you may specify it
|
||||
explicitly here. Note that path here must point to a file that exists
|
||||
and is executable, or we will croak.
|
||||
|
||||
For Firefox 48 and newer, this will be passed to C<geckodriver> such
|
||||
that it will attempt to start up the Firefox at the specified path. If
|
||||
you do not specify anything, we will look for the Firefox browser on
|
||||
our own in the normal places, but if the browser cannot be found,
|
||||
we'll probably C<die> during instantiation.
|
||||
|
||||
For Firefox 47 and older, this browser path should be the file that we
|
||||
directly start up.
|
||||
|
||||
=head2 custom_args
|
||||
|
||||
Optional: specify any additional command line arguments you'd like
|
||||
invoked during the binary startup. See
|
||||
L<Selenium::CanStartBinary/custom_args> for more information.
|
||||
|
||||
For Firefox 48 and newer, these arguments will be passed to
|
||||
geckodriver during start up.
|
||||
|
||||
For Firefox 47 and older, these arguments will be passed to the
|
||||
Firefox browser during start up.
|
||||
|
||||
=head2 startup_timeout
|
||||
|
||||
Optional: specify how long to wait for the binary to start itself and
|
||||
listen on its port. The default duration is arbitrarily 10 seconds. It
|
||||
accepts an integer number of seconds to wait: the following will wait
|
||||
up to 20 seconds:
|
||||
|
||||
Selenium::Firefox->new( startup_timeout => 20 );
|
||||
|
||||
See L<Selenium::CanStartBinary/startup_timeout> for more information.
|
||||
|
||||
=head2 fixed_ports
|
||||
|
||||
Optional: Throw instead of searching for additional ports; see
|
||||
L<Selenium::CanStartBinary/fixed_ports> for more info.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 shutdown_binary
|
||||
|
||||
Call this method instead of L<Selenium::Remote::Driver/quit> to ensure
|
||||
that the binary executable is also closed, instead of simply closing
|
||||
the browser itself. If the browser is still around, it will call
|
||||
C<quit> for you. After that, it will try to shutdown the browser
|
||||
binary by making a GET to /shutdown and on Windows, it will attempt to
|
||||
do a C<taskkill> on the binary CMD window.
|
||||
|
||||
$self->shutdown_binary;
|
||||
|
||||
It doesn't take any arguments, and it doesn't return anything.
|
||||
|
||||
We do our best to call this when the C<$driver> option goes out of
|
||||
scope, but if that happens during global destruction, there's nothing
|
||||
we can do.
|
||||
|
||||
=for Pod::Coverage has_binary
|
||||
|
||||
=for Pod::Coverage has_firefox_binary
|
||||
|
||||
=head1 BREAKING CHANGES
|
||||
|
||||
In version v1.0+ and newer, the default behavior is to enable
|
||||
marionette & geckodriver mode. This means that an existing script that
|
||||
works with v0.2701 and Firefox v47 will require modification if you
|
||||
upgrade Selenium::Firefox to v1.0+. That is,
|
||||
|
||||
# v0.2701 of Selenium::Firefox works with FF47 like such; this will not
|
||||
# work for FF47 after upgrade:
|
||||
my $fx47_old = Selenium::Firefox->new;
|
||||
...
|
||||
$fx47_old->shutdown_binary;
|
||||
|
||||
# v1.0 of Selenium::Firefox works with FF47 like this
|
||||
my $fx47_new = Selenium::Firefox->new( marionette_enabled => 0);
|
||||
...
|
||||
$fx47_new->shutdown_binary;
|
||||
|
||||
We default to assuming FF48 and geckodriver mode because all
|
||||
forthcoming versions of the Firefox browser will be using the
|
||||
geckodriver architecture, and also because that's consistent with the
|
||||
rest of the driver setups, which all have separate driver binaries
|
||||
apart from the browser itself. This means that:
|
||||
|
||||
# v0.2701 of Selenium::Firefox cannot start up FF48 at all
|
||||
|
||||
# v1.0+ of Selenium::Firefox works with FF48+ like this:
|
||||
my $fx48 = Selenium::Firefox->new;
|
||||
|
||||
As with the other drivers, Selenium::Firefox in marionette/geckodriver
|
||||
mode requires a C<geckodriver> executable in the path or provided
|
||||
during startup, and it will also attempt to find the path to your
|
||||
Firefox browser. During testing, we found that it was necessary for us
|
||||
to pass the Firefox browser file path to the C<geckodriver> executable
|
||||
during start up, or else C<geckodriver> would have trouble finding
|
||||
Firefox.
|
||||
|
||||
=head2 get_context
|
||||
|
||||
Description:
|
||||
Firefox extension: Retrieve browser's scope (chrome or content).
|
||||
Chrome is a privileged scope where you can access things like the
|
||||
Firefox UI itself. Content scope is where things like webpages live.
|
||||
|
||||
Output:
|
||||
STRING - context {CHROME|CONTENT}
|
||||
|
||||
Usage:
|
||||
print $firefox_driver->get_context();
|
||||
|
||||
=head2 set_context
|
||||
|
||||
Description:
|
||||
Firefox extension: Set browser's scope (chrome or content).
|
||||
Chrome is a privileged scope where you can access things like the
|
||||
Firefox UI itself. Content scope is where things like webpages live.
|
||||
|
||||
Input:
|
||||
Required:
|
||||
<STRING> - context {CHROME|CONTENT}
|
||||
|
||||
Usage:
|
||||
$firefox_driver->set_context( $context );
|
||||
|
||||
Output:
|
||||
BOOLEAN - success or failure
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
220
Perl OTRS/Kernel/cpan-lib/Selenium/Firefox/Binary.pm
Normal file
220
Perl OTRS/Kernel/cpan-lib/Selenium/Firefox/Binary.pm
Normal file
@@ -0,0 +1,220 @@
|
||||
package Selenium::Firefox::Binary;
|
||||
$Selenium::Firefox::Binary::VERSION = '1.33';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: Subroutines for locating and properly initializing the Firefox Binary
|
||||
use File::Which qw/which/;
|
||||
use Selenium::Firefox::Profile;
|
||||
|
||||
require Exporter;
|
||||
our @ISA = qw/Exporter/;
|
||||
our @EXPORT_OK = qw/firefox_path setup_firefox_binary_env/;
|
||||
|
||||
sub _firefox_windows_path {
|
||||
|
||||
# TODO: make this slightly less dumb
|
||||
my @program_files = (
|
||||
$ENV{PROGRAMFILES} // 'C:\Program Files',
|
||||
$ENV{'PROGRAMFILES(X86)'} // 'C:\Program Files (x86)',
|
||||
);
|
||||
|
||||
foreach (@program_files) {
|
||||
my $binary_path = $_ . '\Mozilla Firefox\firefox.exe';
|
||||
return $binary_path if -x $binary_path;
|
||||
}
|
||||
|
||||
# Fall back to a completely naive strategy
|
||||
warn
|
||||
q/We couldn't find a viable firefox.EXE; you may want to specify it via the binary attribute./;
|
||||
return which('firefox');
|
||||
}
|
||||
|
||||
sub _firefox_darwin_path {
|
||||
my $default_firefox =
|
||||
'/Applications/Firefox.app/Contents/MacOS/firefox-bin';
|
||||
|
||||
if ( -e $default_firefox && -x $default_firefox ) {
|
||||
return $default_firefox;
|
||||
}
|
||||
else {
|
||||
return which('firefox-bin');
|
||||
}
|
||||
}
|
||||
|
||||
sub _firefox_unix_path {
|
||||
|
||||
# TODO: maybe which('firefox3'), which('firefox2') ?
|
||||
return which('firefox') || '/usr/bin/firefox';
|
||||
}
|
||||
|
||||
|
||||
sub firefox_path {
|
||||
my $path;
|
||||
if ( $^O eq 'MSWin32' ) {
|
||||
$path = _firefox_windows_path();
|
||||
}
|
||||
elsif ( $^O eq 'darwin' ) {
|
||||
$path = _firefox_darwin_path();
|
||||
}
|
||||
else {
|
||||
$path = _firefox_unix_path;
|
||||
}
|
||||
|
||||
if ( not -x $path ) {
|
||||
die $path . ' is not an executable file.';
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
|
||||
# We want the profile to persist to the end of the session, not just
|
||||
# the end of this function.
|
||||
my $profile;
|
||||
|
||||
sub setup_firefox_binary_env {
|
||||
my ( $port, $marionette_port, $caller_profile ) = @_;
|
||||
|
||||
$profile = $caller_profile || Selenium::Firefox::Profile->new;
|
||||
$profile->add_webdriver( $port, $marionette_port );
|
||||
$profile->add_marionette($marionette_port);
|
||||
|
||||
# For non-geckodriver/marionette startup, we instruct Firefox to
|
||||
# use the profile by specifying the appropriate environment
|
||||
# variables for it to hook onto.
|
||||
if ( !$marionette_port ) {
|
||||
$ENV{'XRE_PROFILE_PATH'} = $profile->_layout_on_disk;
|
||||
$ENV{'MOZ_NO_REMOTE'} = '1'; # able to launch multiple instances
|
||||
$ENV{'MOZ_CRASHREPORTER_DISABLE'} = '1'; # disable breakpad
|
||||
$ENV{'NO_EM_RESTART'} =
|
||||
'1'; # prevent the binary from detaching from the console.log
|
||||
}
|
||||
else {
|
||||
# In case the user created an old Firefox, which would've set
|
||||
# those ENV variables, and then wanted to create a new Firefox
|
||||
# afterwards, the env variables would still be around, and the
|
||||
# new Firefox would respect the XRE_PROFILE_PATH and try to
|
||||
# load it in the new geckodriver Firefox, which would cause an
|
||||
# extension compatibility check
|
||||
my @env_vars = qw/
|
||||
XRE_PROFILE_PATH
|
||||
MOZ_NO_REMOTE
|
||||
MOZ_CRASHREPORTER_DISABLE
|
||||
NO_EM_RESTART
|
||||
/;
|
||||
|
||||
foreach (@env_vars) {
|
||||
delete $ENV{$_};
|
||||
}
|
||||
}
|
||||
|
||||
return $profile;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Firefox::Binary - Subroutines for locating and properly initializing the Firefox Binary
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
=head2 firefox_path
|
||||
|
||||
Return the path to the firefox binary on your system.
|
||||
|
||||
=head2 setup_firefox_binary_env
|
||||
|
||||
Sets various environment variables to make firefox work correctly with webDriver.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
470
Perl OTRS/Kernel/cpan-lib/Selenium/Firefox/Profile.pm
Normal file
470
Perl OTRS/Kernel/cpan-lib/Selenium/Firefox/Profile.pm
Normal file
@@ -0,0 +1,470 @@
|
||||
package Selenium::Firefox::Profile;
|
||||
$Selenium::Firefox::Profile::VERSION = '1.33';
|
||||
# ABSTRACT: Use custom profiles with Selenium::Remote::Driver
|
||||
# TODO: convert this to Moo!
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Archive::Zip qw( :ERROR_CODES );
|
||||
use Carp qw(croak);
|
||||
use Cwd qw(abs_path);
|
||||
use File::Copy qw(copy);
|
||||
use File::Temp;
|
||||
use File::Basename qw(dirname);
|
||||
use IO::Uncompress::Unzip 2.030 qw($UnzipError);
|
||||
use JSON qw(decode_json);
|
||||
use MIME::Base64;
|
||||
use Scalar::Util qw(blessed looks_like_number);
|
||||
use XML::Simple;
|
||||
|
||||
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my %args = @_;
|
||||
|
||||
my $profile_dir;
|
||||
if ( $args{profile_dir} && -d $args{profile_dir} ) {
|
||||
$profile_dir = $args{profile_dir};
|
||||
}
|
||||
else {
|
||||
$profile_dir = File::Temp->newdir();
|
||||
}
|
||||
|
||||
# TODO: accept user prefs, boolean prefs, and extensions in
|
||||
# constructor
|
||||
my $self = {
|
||||
profile_dir => $profile_dir,
|
||||
user_prefs => {},
|
||||
extensions => []
|
||||
};
|
||||
bless $self, $class or die "Can't bless $class: $!";
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
|
||||
sub set_preference {
|
||||
my ( $self, %prefs ) = @_;
|
||||
|
||||
foreach ( keys %prefs ) {
|
||||
my $value = $prefs{$_};
|
||||
my $clean_value = '';
|
||||
|
||||
if ( JSON::is_bool($value) ) {
|
||||
$self->set_boolean_preference( $_, $value );
|
||||
next;
|
||||
}
|
||||
elsif ( $value =~ /^(['"]).*\1$/ or looks_like_number($value) ) {
|
||||
|
||||
# plain integers: 0, 1, 32768, or integers wrapped in strings:
|
||||
# "0", "1", "20140204". in either case, there's nothing for us
|
||||
# to do.
|
||||
$clean_value = $value;
|
||||
}
|
||||
else {
|
||||
# otherwise it's hopefully a string that we'll need to
|
||||
# quote on our own
|
||||
$clean_value = '"' . $value . '"';
|
||||
}
|
||||
|
||||
$self->{user_prefs}->{$_} = $clean_value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub set_boolean_preference {
|
||||
my ( $self, %prefs ) = @_;
|
||||
|
||||
foreach ( keys %prefs ) {
|
||||
my $value = $prefs{$_};
|
||||
|
||||
$self->{user_prefs}->{$_} = $value ? 'true' : 'false';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub get_preference {
|
||||
my ( $self, $pref ) = @_;
|
||||
|
||||
return $self->{user_prefs}->{$pref};
|
||||
}
|
||||
|
||||
|
||||
sub add_extension {
|
||||
my ( $self, $xpi ) = @_;
|
||||
|
||||
croak 'File not found: ' . $xpi unless -e $xpi;
|
||||
my $xpi_abs_path = abs_path($xpi);
|
||||
croak '$xpi_abs_path: extensions must be in .xpi format'
|
||||
unless $xpi_abs_path =~ /\.xpi$/;
|
||||
|
||||
push( @{ $self->{extensions} }, $xpi_abs_path );
|
||||
}
|
||||
|
||||
|
||||
sub add_webdriver {
|
||||
my ( $self, $port, $is_marionette ) = @_;
|
||||
|
||||
my $prefs = $self->_load_prefs;
|
||||
my $current_user_prefs = $self->{user_prefs};
|
||||
|
||||
$self->set_preference(
|
||||
%{ $prefs->{mutable} },
|
||||
|
||||
# having the user prefs here allows them to overwrite the
|
||||
# mutable loaded prefs
|
||||
%{$current_user_prefs},
|
||||
|
||||
# but the frozen ones cannot be overwritten
|
||||
%{ $prefs->{frozen} },
|
||||
'webdriver_firefox_port' => $port
|
||||
);
|
||||
|
||||
if ( !$is_marionette ) {
|
||||
$self->_add_webdriver_xpi;
|
||||
}
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub _load_prefs {
|
||||
|
||||
# The appropriate webdriver preferences are stored in an adjacent
|
||||
# JSON file; it's useful things like disabling default browser
|
||||
# checks and setting an empty single page as the start up tab
|
||||
# configuration. Unfortunately, these change with each version of
|
||||
# webdriver.
|
||||
|
||||
my $this_dir = dirname( abs_path(__FILE__) );
|
||||
my $default_prefs_filename = $this_dir . '/webdriver_prefs.json';
|
||||
|
||||
my $json;
|
||||
{
|
||||
local $/;
|
||||
open( my $fh, '<', $default_prefs_filename );
|
||||
$json = <$fh>;
|
||||
close($fh);
|
||||
}
|
||||
|
||||
my $prefs = decode_json($json);
|
||||
|
||||
return $prefs;
|
||||
}
|
||||
|
||||
|
||||
sub _add_webdriver_xpi {
|
||||
my ($self) = @_;
|
||||
|
||||
my $this_dir = dirname( abs_path(__FILE__) );
|
||||
my $webdriver_extension = $this_dir . '/webdriver.xpi';
|
||||
|
||||
$self->add_extension($webdriver_extension);
|
||||
}
|
||||
|
||||
|
||||
sub add_marionette {
|
||||
my ( $self, $port ) = @_;
|
||||
return if !$port;
|
||||
$self->set_preference( 'marionette.defaultPrefs.port', $port );
|
||||
}
|
||||
|
||||
sub _encode {
|
||||
my $self = shift;
|
||||
|
||||
# The remote webdriver accepts the Firefox profile as a base64
|
||||
# encoded zip file
|
||||
$self->_layout_on_disk();
|
||||
|
||||
my $zip = Archive::Zip->new();
|
||||
$zip->addTree( $self->{profile_dir} );
|
||||
|
||||
my $string = "";
|
||||
open( my $fh, ">", \$string );
|
||||
binmode($fh);
|
||||
unless ( $zip->writeToFileHandle($fh) == AZ_OK ) {
|
||||
die 'write error';
|
||||
}
|
||||
|
||||
return encode_base64( $string, '' );
|
||||
}
|
||||
|
||||
sub _layout_on_disk {
|
||||
my $self = shift;
|
||||
|
||||
$self->_write_preferences();
|
||||
$self->_install_extensions();
|
||||
|
||||
return $self->{profile_dir};
|
||||
}
|
||||
|
||||
sub _write_preferences {
|
||||
my $self = shift;
|
||||
|
||||
my $userjs = $self->{profile_dir} . "/user.js";
|
||||
open( my $fh, ">>", $userjs )
|
||||
or die "Cannot open $userjs for writing preferences: $!";
|
||||
|
||||
foreach ( keys %{ $self->{user_prefs} } ) {
|
||||
print $fh 'user_pref("'
|
||||
. $_ . '", '
|
||||
. $self->get_preference($_) . ');' . "\n";
|
||||
}
|
||||
close($fh);
|
||||
}
|
||||
|
||||
sub _install_extensions {
|
||||
my $self = shift;
|
||||
my $extension_dir = $self->{profile_dir} . "/extensions/";
|
||||
mkdir $extension_dir unless -d $extension_dir;
|
||||
|
||||
# TODO: handle extensions that need to be unpacked
|
||||
foreach my $xpi ( @{ $self->{extensions} } ) {
|
||||
|
||||
# For Firefox to recognize the extension, we have to put the
|
||||
# .xpi in the /extensions/ folder and change the filename to
|
||||
# its id, which is found in the install.rdf in the root of the
|
||||
# zip.
|
||||
|
||||
my $rdf_string = $self->_extract_install_rdf($xpi);
|
||||
my $rdf = XMLin($rdf_string);
|
||||
my $name = $rdf->{Description}->{'em:id'};
|
||||
|
||||
my $xpi_dest = $extension_dir . $name . ".xpi";
|
||||
copy( $xpi, $xpi_dest )
|
||||
or croak "Error copying $_ to $xpi_dest : $!";
|
||||
}
|
||||
}
|
||||
|
||||
sub _extract_install_rdf {
|
||||
my ( $self, $xpi ) = @_;
|
||||
|
||||
my $unzipped = IO::Uncompress::Unzip->new($xpi)
|
||||
or die "Cannot unzip $xpi: $UnzipError";
|
||||
|
||||
my $install_rdf = '';
|
||||
while ( $unzipped->nextStream ) {
|
||||
my $filename = $unzipped->getHeaderInfo->{Name};
|
||||
if ( $filename eq 'install.rdf' ) {
|
||||
my $buffer;
|
||||
while ( ( my $status = $unzipped->read($buffer) ) > 0 ) {
|
||||
$install_rdf .= $buffer;
|
||||
}
|
||||
return $install_rdf;
|
||||
}
|
||||
}
|
||||
|
||||
croak
|
||||
'Invalid Firefox extension: could not find install.rdf in the .XPI at: '
|
||||
. $xpi;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Firefox::Profile - Use custom profiles with Selenium::Remote::Driver
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
You can use this module to create a custom Firefox Profile for your
|
||||
Selenium tests. Currently, you can set browser preferences and add
|
||||
extensions to the profile before passing it in the constructor for a
|
||||
new L<Selenium::Remote::Driver> or L<Selenium::Firefox>.
|
||||
|
||||
=head1 SYNPOSIS
|
||||
|
||||
use Selenium::Remote::Driver;
|
||||
use Selenium::Firefox::Profile;
|
||||
|
||||
my $profile = Selenium::Firefox::Profile->new;
|
||||
$profile->set_preference(
|
||||
'browser.startup.homepage' => 'http://www.google.com',
|
||||
'browser.cache.disk.capacity' => 358400
|
||||
);
|
||||
|
||||
$profile->set_boolean_preference(
|
||||
'browser.shell.checkDefaultBrowser' => 0
|
||||
);
|
||||
|
||||
$profile->add_extension('t/www/redisplay.xpi');
|
||||
|
||||
my $driver = Selenium::Remote::Driver->new(
|
||||
'firefox_profile' => $profile
|
||||
);
|
||||
|
||||
$driver->get('http://www.google.com');
|
||||
print $driver->get_title();
|
||||
|
||||
=head1 CONSTRUCTOR
|
||||
|
||||
=head2 new (%args)
|
||||
|
||||
profile_dir - <string> directory to look for the firefox profile. Defaults to a Tempdir.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 set_preference
|
||||
|
||||
Set string and integer preferences on the profile object. You can set
|
||||
multiple preferences at once. If you need to set a boolean preference,
|
||||
either use JSON::true/JSON::false, or see C<set_boolean_preference()>.
|
||||
|
||||
$profile->set_preference("quoted.integer.pref" => '"20140314220517"');
|
||||
# user_pref("quoted.integer.pref", "20140314220517");
|
||||
|
||||
$profile->set_preference("plain.integer.pref" => 9005);
|
||||
# user_pref("plain.integer.pref", 9005);
|
||||
|
||||
$profile->set_preference("string.pref" => "sample string value");
|
||||
# user_pref("string.pref", "sample string value");
|
||||
|
||||
=head2 set_boolean_preference
|
||||
|
||||
Set preferences that require boolean values of 'true' or 'false'. You
|
||||
can set multiple preferences at once. For string or integer
|
||||
preferences, use C<set_preference()>.
|
||||
|
||||
$profile->set_boolean_preference("false.pref" => 0);
|
||||
# user_pref("false.pref", false);
|
||||
|
||||
$profile->set_boolean_preference("true.pref" => 1);
|
||||
# user_pref("true.pref", true);
|
||||
|
||||
=head2 get_preference
|
||||
|
||||
Retrieve the computed value of a preference. Strings will be double
|
||||
quoted and boolean values will be single quoted as "true" or "false"
|
||||
accordingly.
|
||||
|
||||
$profile->set_boolean_preference("true.pref" => 1);
|
||||
print $profile->get_preference("true.pref") # true
|
||||
|
||||
$profile->set_preference("string.pref" => "an extra set of quotes");
|
||||
print $profile->get_preference("string.pref") # "an extra set of quotes"
|
||||
|
||||
=head2 add_extension
|
||||
|
||||
Add an existing C<.xpi> to the profile by providing its path. This
|
||||
only works with packaged C<.xpi> files, not plain/un-packed extension
|
||||
directories.
|
||||
|
||||
$profile->add_extension('t/www/redisplay.xpi');
|
||||
|
||||
=head2 add_webdriver
|
||||
|
||||
Primarily for internal use, we set the appropriate firefox preferences
|
||||
for a new geckodriver session.
|
||||
|
||||
=head2 add_webdriver_xpi
|
||||
|
||||
Primarily for internal use. This adds the fxgoogle .xpi that is used
|
||||
for webdriver communication in FF47 and older. For FF48 and newer, the
|
||||
old method using an extension to orchestrate the webdriver
|
||||
communication with the Firefox browser has been obsoleted by the
|
||||
introduction of C<geckodriver>.
|
||||
|
||||
=head2 add_marionette
|
||||
|
||||
Primarily for internal use, configure Marionette to the
|
||||
current Firefox profile.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=item *
|
||||
|
||||
L<http://kb.mozillazine.org/About:config_entries|http://kb.mozillazine.org/About:config_entries>
|
||||
|
||||
=item *
|
||||
|
||||
L<https://developer.mozilla.org/en-US/docs/Mozilla/Preferences/A_brief_guide_to_Mozilla_preferences|https://developer.mozilla.org/en-US/docs/Mozilla/Preferences/A_brief_guide_to_Mozilla_preferences>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
BIN
Perl OTRS/Kernel/cpan-lib/Selenium/Firefox/webdriver.xpi
Normal file
BIN
Perl OTRS/Kernel/cpan-lib/Selenium/Firefox/webdriver.xpi
Normal file
Binary file not shown.
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"frozen": {
|
||||
"app.update.auto": false,
|
||||
"app.update.enabled": false,
|
||||
"browser.displayedE10SNotice": 4,
|
||||
"browser.download.manager.showWhenStarting": false,
|
||||
"browser.EULA.override": true,
|
||||
"browser.EULA.3.accepted": true,
|
||||
"browser.link.open_external": 2,
|
||||
"browser.link.open_newwindow": 2,
|
||||
"browser.offline": false,
|
||||
"browser.reader.detectedFirstArticle": true,
|
||||
"browser.safebrowsing.enabled": false,
|
||||
"browser.safebrowsing.malware.enabled": false,
|
||||
"browser.search.update": false,
|
||||
"browser.selfsupport.url" : "",
|
||||
"browser.sessionstore.resume_from_crash": false,
|
||||
"browser.shell.checkDefaultBrowser": false,
|
||||
"browser.tabs.warnOnClose": false,
|
||||
"browser.tabs.warnOnOpen": false,
|
||||
"datareporting.healthreport.service.enabled": false,
|
||||
"datareporting.healthreport.uploadEnabled": false,
|
||||
"datareporting.healthreport.service.firstRun": false,
|
||||
"datareporting.healthreport.logging.consoleEnabled": false,
|
||||
"datareporting.policy.dataSubmissionEnabled": false,
|
||||
"datareporting.policy.dataSubmissionPolicyAccepted": false,
|
||||
"devtools.errorconsole.enabled": true,
|
||||
"dom.disable_open_during_load": false,
|
||||
"extensions.autoDisableScopes": 10,
|
||||
"extensions.blocklist.enabled": false,
|
||||
"extensions.checkCompatibility.nightly": false,
|
||||
"extensions.logging.enabled": true,
|
||||
"extensions.update.enabled": false,
|
||||
"extensions.update.notifyUser": false,
|
||||
"javascript.enabled": true,
|
||||
"network.manage-offline-status": false,
|
||||
"network.http.phishy-userpass-length": 255,
|
||||
"offline-apps.allow_by_default": true,
|
||||
"prompts.tab_modal.enabled": false,
|
||||
"security.csp.enable": false,
|
||||
"security.fileuri.origin_policy": 3,
|
||||
"security.fileuri.strict_origin_policy": false,
|
||||
"signon.rememberSignons": false,
|
||||
"toolkit.networkmanager.disable": true,
|
||||
"toolkit.telemetry.prompted": 2,
|
||||
"toolkit.telemetry.enabled": false,
|
||||
"toolkit.telemetry.rejected": true,
|
||||
"xpinstall.signatures.required": false,
|
||||
"xpinstall.whitelist.required": false
|
||||
},
|
||||
"mutable": {
|
||||
"browser.dom.window.dump.enabled": true,
|
||||
"browser.laterrun.enabled": false,
|
||||
"browser.newtab.url": "about:blank",
|
||||
"browser.newtabpage.enabled": false,
|
||||
"browser.startup.page": 0,
|
||||
"browser.startup.homepage": "about:blank",
|
||||
"browser.startup.homepage_override.mstone": "ignore",
|
||||
"browser.usedOnWindows10.introURL": "about:blank",
|
||||
"dom.max_chrome_script_run_time": 30,
|
||||
"dom.max_script_run_time": 30,
|
||||
"dom.report_all_js_exceptions": true,
|
||||
"javascript.options.showInConsole": true,
|
||||
"startup.homepage_welcome_url": "about:blank",
|
||||
"startup.homepage_welcome_url.additional": "about:blank",
|
||||
"webdriver_accept_untrusted_certs": true,
|
||||
"webdriver_assume_untrusted_issuer": true
|
||||
}
|
||||
}
|
||||
142
Perl OTRS/Kernel/cpan-lib/Selenium/InternetExplorer.pm
Normal file
142
Perl OTRS/Kernel/cpan-lib/Selenium/InternetExplorer.pm
Normal file
@@ -0,0 +1,142 @@
|
||||
package Selenium::InternetExplorer;
|
||||
$Selenium::InternetExplorer::VERSION = '1.33';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: A convenience package for creating a IE instance
|
||||
use Moo;
|
||||
extends 'Selenium::Remote::Driver';
|
||||
|
||||
|
||||
has '+browser_name' => (
|
||||
is => 'ro',
|
||||
default => sub { 'internet_explorer' }
|
||||
);
|
||||
|
||||
has '+platform' => (
|
||||
is => 'ro',
|
||||
default => sub { 'WINDOWS' }
|
||||
);
|
||||
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::InternetExplorer - A convenience package for creating a IE instance
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
my $driver = Selenium::InternetExplorer->new;
|
||||
# when you're done
|
||||
$driver->shutdown_binary;
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 shutdown_binary
|
||||
|
||||
Call this method instead of L<Selenium::Remote::Driver/quit> to ensure
|
||||
that the binary executable is also closed, instead of simply closing
|
||||
the browser itself. If the browser is still around, it will call
|
||||
C<quit> for you. After that, it will try to shutdown the browser
|
||||
binary by making a GET to /shutdown and on Windows, it will attempt to
|
||||
do a C<taskkill> on the binary CMD window.
|
||||
|
||||
$self->shutdown_binary;
|
||||
|
||||
It doesn't take any arguments, and it doesn't return anything.
|
||||
|
||||
We do our best to call this when the C<$driver> option goes out of
|
||||
scope, but if that happens during global destruction, there's nothing
|
||||
we can do.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
233
Perl OTRS/Kernel/cpan-lib/Selenium/PhantomJS.pm
Normal file
233
Perl OTRS/Kernel/cpan-lib/Selenium/PhantomJS.pm
Normal file
@@ -0,0 +1,233 @@
|
||||
package Selenium::PhantomJS;
|
||||
$Selenium::PhantomJS::VERSION = '1.33';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: Use GhostDriver without a Selenium server
|
||||
use Moo;
|
||||
use Selenium::CanStartBinary::FindBinary qw/coerce_simple_binary/;
|
||||
extends 'Selenium::Remote::Driver';
|
||||
|
||||
|
||||
has '+browser_name' => (
|
||||
is => 'ro',
|
||||
default => sub { 'phantomjs' }
|
||||
);
|
||||
|
||||
|
||||
has 'binary' => (
|
||||
is => 'lazy',
|
||||
coerce => \&coerce_simple_binary,
|
||||
default => sub { 'phantomjs' },
|
||||
predicate => 1
|
||||
);
|
||||
|
||||
|
||||
has 'binary_port' => (
|
||||
is => 'lazy',
|
||||
default => sub { 8910 }
|
||||
);
|
||||
|
||||
has '_binary_args' => (
|
||||
is => 'lazy',
|
||||
builder => sub {
|
||||
my ($self) = @_;
|
||||
|
||||
return ' --webdriver=127.0.0.1:' . $self->port;
|
||||
}
|
||||
);
|
||||
|
||||
with 'Selenium::CanStartBinary';
|
||||
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::PhantomJS - Use GhostDriver without a Selenium server
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
my $driver = Selenium::PhantomJS->new;
|
||||
# when you're done
|
||||
$driver->shutdown_binary;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This class allows you to use PhantomJS via Ghostdriver without needing
|
||||
the JRE or a selenium server running. When you refrain from passing
|
||||
the C<remote_server_addr> and C<port> arguments, we will search for
|
||||
the phantomjs executable binary in your $PATH. We'll try to start the
|
||||
binary connect to it, shutting it down at the end of the test.
|
||||
|
||||
If the binary is not found, we'll fall back to the default
|
||||
L<Selenium::Remote::Driver> behavior of assuming defaults of
|
||||
127.0.0.1:4444 after waiting a few seconds.
|
||||
|
||||
If you specify a remote server address, or a port, we'll assume you
|
||||
know what you're doing and take no additional behavior.
|
||||
|
||||
If you're curious whether your Selenium::PhantomJS instance is using a
|
||||
separate PhantomJS binary, or through the selenium server, you can check
|
||||
the C<binary_mode> attr after instantiation.
|
||||
|
||||
my $driver = Selenium::PhantomJS->new;
|
||||
print $driver->binary_mode;
|
||||
|
||||
N.B. - if you're using Windows and you installed C<phantomjs> via
|
||||
C<npm install -g phantomjs>, there is a very high probability that we
|
||||
will _not_ close down your phantomjs binary correctly after your
|
||||
test. You will be able to tell if we leave around empty command
|
||||
windows that you didn't start yourself. The easiest way to fix this is
|
||||
to download PhantomJS manually from their
|
||||
L<website|http://phantomjs.org/download.html> and put it in your
|
||||
C<%PATH%>. If this is a blocking issue for you, let us know in
|
||||
L<Github|https://github.com/gempesaw/Selenium-Remote-Driver>; thanks!
|
||||
|
||||
=head1 ATTRIBUTES
|
||||
|
||||
=head2 binary
|
||||
|
||||
Optional: specify the path to your binary. If you don't specify
|
||||
anything, we'll try to find it on our own via L<File::Which/which>.
|
||||
|
||||
=head2 binary_port
|
||||
|
||||
Optional: specify the port that we should bind to. If you don't
|
||||
specify anything, we'll default to the driver's default port. Since
|
||||
there's no a priori guarantee that this will be an open port, this is
|
||||
_not_ necessarily the port that we end up using - if the port here is
|
||||
already bound, we'll search above it until we find an open one.
|
||||
|
||||
See L<Selenium::CanStartBinary/port> for more details, and
|
||||
L<Selenium::Remote::Driver/port> after instantiation to see what the
|
||||
actual port turned out to be.
|
||||
|
||||
=head2 custom_args
|
||||
|
||||
Optional: specify any additional command line arguments you'd like
|
||||
invoked during the binary startup. See
|
||||
L<Selenium::CanStartBinary/custom_args> for more information.
|
||||
|
||||
=head2 startup_timeout
|
||||
|
||||
Optional: specify how long to wait for the binary to start itself and
|
||||
listen on its port. The default duration is arbitrarily 10 seconds. It
|
||||
accepts an integer number of seconds to wait: the following will wait
|
||||
up to 20 seconds:
|
||||
|
||||
Selenium::PhantomJS->new( startup_timeout => 20 );
|
||||
|
||||
See L<Selenium::CanStartBinary/startup_timeout> for more information.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 shutdown_binary
|
||||
|
||||
Call this method instead of L<Selenium::Remote::Driver/quit> to ensure
|
||||
that the binary executable is also closed, instead of simply closing
|
||||
the browser itself. If the browser is still around, it will call
|
||||
C<quit> for you. After that, it will try to shutdown the browser
|
||||
binary by making a GET to /shutdown and on Windows, it will attempt to
|
||||
do a C<taskkill> on the binary CMD window.
|
||||
|
||||
$self->shutdown_binary;
|
||||
|
||||
It doesn't take any arguments, and it doesn't return anything.
|
||||
|
||||
We do our best to call this when the C<$driver> option goes out of
|
||||
scope, but if that happens during global destruction, there's nothing
|
||||
we can do.
|
||||
|
||||
=for Pod::Coverage has_binary
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
614
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/Commands.pm
Normal file
614
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/Commands.pm
Normal file
@@ -0,0 +1,614 @@
|
||||
package Selenium::Remote::Commands;
|
||||
$Selenium::Remote::Commands::VERSION = '1.33';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Carp qw{croak};
|
||||
|
||||
# ABSTRACT: Implement commands for Selenium::Remote::Driver for use with webdriver 2
|
||||
|
||||
|
||||
use Moo;
|
||||
|
||||
has '_cmds' => (
|
||||
is => 'lazy',
|
||||
reader => 'get_cmds',
|
||||
builder => sub {
|
||||
return {
|
||||
'status' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'status',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'newSession' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'getSessions' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'sessions',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'getCapabilities' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'setTimeout' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/timeouts',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'setAsyncScriptTimeout' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/timeouts/async_script',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'setImplicitWaitTimeout' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/timeouts/implicit_wait',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'quit' => {
|
||||
'method' => 'DELETE',
|
||||
'url' => 'session/:sessionId',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'getCurrentWindowHandle' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/window_handle',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'getWindowHandles' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/window_handles',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'getWindowSize' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/window/:windowHandle/size',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'getWindowPosition' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/window/:windowHandle/position',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'maximizeWindow' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/window/:windowHandle/maximize',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'setWindowSize' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/window/:windowHandle/size',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'setWindowPosition' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/window/:windowHandle/position',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'getCurrentUrl' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/url',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'get' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/url',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'goForward' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/forward',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'goBack' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/back',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'refresh' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/refresh',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'executeScript' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/execute',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'executeAsyncScript' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/execute_async',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'screenshot' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/screenshot',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'availableEngines' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/ime/available_engines',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'switchToFrame' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/frame',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'switchToWindow' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/window',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'getAllCookies' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/cookie',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'addCookie' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/cookie',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'deleteAllCookies' => {
|
||||
'method' => 'DELETE',
|
||||
'url' => 'session/:sessionId/cookie',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'deleteCookieNamed' => {
|
||||
'method' => 'DELETE',
|
||||
'url' => 'session/:sessionId/cookie/:name',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'getPageSource' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/source',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'getTitle' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/title',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'findElement' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/element',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'findElements' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/elements',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'getActiveElement' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/element/active',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'describeElement' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/element/:id',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'findChildElement' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/element/:id/element',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'findChildElements' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/element/:id/elements',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'clickElement' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/element/:id/click',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'submitElement' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/element/:id/submit',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'sendKeysToElement' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/element/:id/value',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'sendKeysToActiveElement' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/keys',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'sendModifier' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/modifier',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'isElementSelected' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/element/:id/selected',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'setElementSelected' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/element/:id/selected',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'toggleElement' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/element/:id/toggle',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'isElementEnabled' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/element/:id/enabled',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'getElementLocation' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/element/:id/location',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'getElementLocationInView' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/element/:id/location_in_view',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'getElementTagName' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/element/:id/name',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'clearElement' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/element/:id/clear',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'getElementAttribute' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/element/:id/attribute/:name',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'elementEquals' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/element/:id/equals/:other',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'isElementDisplayed' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/element/:id/displayed',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'close' => {
|
||||
'method' => 'DELETE',
|
||||
'url' => 'session/:sessionId/window',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'getElementSize' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/element/:id/size',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'getElementText' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/element/:id/text',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'getElementValueOfCssProperty' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/element/:id/css/:propertyName',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'mouseMoveToLocation' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/moveto',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'getAlertText' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/alert_text',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'sendKeysToPrompt' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/alert_text',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'acceptAlert' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/accept_alert',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'dismissAlert' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/dismiss_alert',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'click' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/click',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'doubleClick' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/doubleclick',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'buttonDown' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/buttondown',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'buttonUp' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/buttonup',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'uploadFile' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/file',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'getLocalStorageItem' => {
|
||||
'method' => 'GET',
|
||||
'url' => '/session/:sessionId/local_storage/key/:key',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'deleteLocalStorageItem' => {
|
||||
'method' => 'DELETE',
|
||||
'url' => '/session/:sessionId/local_storage/key/:key',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'cacheStatus' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/application_cache/status',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'setGeolocation' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/location',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'getGeolocation' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/location',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'getLog' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/log',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'getLogTypes' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/log/types',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'setOrientation' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/orientation',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'getOrientation' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/orientation',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
|
||||
# firefox extension
|
||||
'setContext' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/moz/context',
|
||||
'no_content_success' => 1
|
||||
},
|
||||
'getContext' => {
|
||||
'method' => 'GET',
|
||||
'url' => 'session/:sessionId/moz/context',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
|
||||
# geckodriver workarounds
|
||||
'executeScriptGecko' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/execute/sync',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
'executeAsyncScriptGecko' => {
|
||||
'method' => 'POST',
|
||||
'url' => 'session/:sessionId/execute/async',
|
||||
'no_content_success' => 0
|
||||
},
|
||||
|
||||
# /session/:sessionId/local_storage
|
||||
# /session/:sessionId/local_storage/key/:key
|
||||
# /session/:sessionId/local_storage/size
|
||||
# /session/:sessionId/session_storage
|
||||
# /session/:sessionId/session_storage/key/:key
|
||||
# /session/:sessionId/session_storage/size
|
||||
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
# helper methods to manipulate the _cmds hash
|
||||
sub get_url {
|
||||
my ( $self, $command ) = @_;
|
||||
return $self->get_cmds->{$command}->{url};
|
||||
}
|
||||
|
||||
sub get_method {
|
||||
my ( $self, $command ) = @_;
|
||||
return $self->get_cmds->{$command}->{method};
|
||||
}
|
||||
|
||||
sub get_no_content_success {
|
||||
my ( $self, $command ) = @_;
|
||||
return $self->get_cmds->{$command}->{no_content_success};
|
||||
}
|
||||
|
||||
# This method will replace the template & return
|
||||
sub get_params {
|
||||
my ( $self, $args ) = @_;
|
||||
if ( !( defined $args->{'session_id'} ) ) {
|
||||
return;
|
||||
}
|
||||
my $data = {};
|
||||
my $command = $args->{'command'};
|
||||
|
||||
#Allow fall-back in the event the command passed doesn't exist
|
||||
return unless $self->get_cmds()->{$command};
|
||||
|
||||
my $url = $self->get_url($command);
|
||||
|
||||
# Do the var substitutions.
|
||||
$url =~ s/:sessionId/$args->{'session_id'}/;
|
||||
$url =~ s/:id/$args->{'id'}/;
|
||||
$url =~ s/:name/$args->{'name'}/;
|
||||
$url =~ s/:propertyName/$args->{'property_name'}/;
|
||||
$url =~ s/:other/$args->{'other'}/;
|
||||
$url =~ s/:windowHandle/$args->{'window_handle'}/;
|
||||
|
||||
$data->{'method'} = $self->get_method($command);
|
||||
$data->{'no_content_success'} = $self->get_no_content_success($command);
|
||||
$data->{'url'} = $url;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
sub parse_response {
|
||||
my ( $self, $res, $resp ) = @_;
|
||||
if ( ref($resp) eq 'HASH' ) {
|
||||
if ( $resp->{cmd_status} && $resp->{cmd_status} eq 'OK' ) {
|
||||
return $resp->{cmd_return};
|
||||
}
|
||||
my $msg = "Error while executing command";
|
||||
$msg .= ": $resp->{cmd_error}" if $resp->{cmd_error};
|
||||
if ( $resp->{cmd_return} ) {
|
||||
if ( ref( $resp->{cmd_return} ) eq 'HASH' ) {
|
||||
$msg .= ": $res->{command}"
|
||||
if $res->{command};
|
||||
$msg .= ": $resp->{cmd_return}->{error}->{msg}"
|
||||
if $resp->{cmd_return}->{error}->{msg};
|
||||
$msg .= ": $resp->{cmd_return}->{message}"
|
||||
if $resp->{cmd_return}->{message};
|
||||
}
|
||||
else {
|
||||
$msg .= ": $resp->{cmd_return}";
|
||||
}
|
||||
}
|
||||
croak $msg;
|
||||
}
|
||||
return $resp;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Remote::Commands - Implement commands for Selenium::Remote::Driver for use with webdriver 2
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Defines all the HTTP endpoints available to execute on a selenium v2 server.
|
||||
|
||||
If you have either a customized Selenium Server, or want new features
|
||||
you should update the _cmds hash.
|
||||
|
||||
=for Pod::Coverage *EVERYTHING*
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
3916
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/Driver.pm
Normal file
3916
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/Driver.pm
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,124 @@
|
||||
package Selenium::Remote::Driver::CanSetWebdriverContext;
|
||||
$Selenium::Remote::Driver::CanSetWebdriverContext::VERSION = '1.33';
|
||||
# ABSTRACT: Customize the webdriver context prefix for various drivers
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Moo::Role;
|
||||
|
||||
|
||||
has 'wd_context_prefix' => (
|
||||
is => 'lazy',
|
||||
default => sub { '/wd/hub' }
|
||||
);
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Remote::Driver::CanSetWebdriverContext - Customize the webdriver context prefix for various drivers
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Some drivers don't use the typical C</wd/hub> context prefix for the
|
||||
webdriver HTTP communication. For example, the newer versions of the
|
||||
Firefox driver extension use the context C</hub> instead. This role
|
||||
just has the one attribute with a default webdriver context prefix,
|
||||
and is consumed in L<Selenium::Remote::Driver> and
|
||||
L<Selenium::Remote::RemoteConnection>.
|
||||
|
||||
If you're new to webdriver, you probably want to head over to
|
||||
L<Selenium::Remote::Driver>'s docs; this package is more of an
|
||||
internal-facing concern.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
@@ -0,0 +1,127 @@
|
||||
package Selenium::Remote::Driver::Firefox::Profile;
|
||||
$Selenium::Remote::Driver::Firefox::Profile::VERSION = '1.33';
|
||||
# ABSTRACT: Use custom profiles with Selenium::Remote::Driver
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Selenium::Firefox::Profile;
|
||||
|
||||
BEGIN {
|
||||
push our @ISA, 'Selenium::Firefox::Profile';
|
||||
}
|
||||
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Remote::Driver::Firefox::Profile - Use custom profiles with Selenium::Remote::Driver
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
We've renamed this class to the slightly less wordy
|
||||
L<Selenium::Firefox::Profile>. This is only around as an alias to
|
||||
hopefully prevent old code from breaking.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Firefox::Profile|Selenium::Firefox::Profile>
|
||||
|
||||
=item *
|
||||
|
||||
L<http://kb.mozillazine.org/About:config_entries|http://kb.mozillazine.org/About:config_entries>
|
||||
|
||||
=item *
|
||||
|
||||
L<https://developer.mozilla.org/en-US/docs/Mozilla/Preferences/A_brief_guide_to_Mozilla_preferences|https://developer.mozilla.org/en-US/docs/Mozilla/Preferences/A_brief_guide_to_Mozilla_preferences>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
247
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/ErrorHandler.pm
Normal file
247
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/ErrorHandler.pm
Normal file
@@ -0,0 +1,247 @@
|
||||
package Selenium::Remote::ErrorHandler;
|
||||
$Selenium::Remote::ErrorHandler::VERSION = '1.33';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: Error handler for Selenium::Remote::Driver
|
||||
|
||||
use Moo;
|
||||
use Carp qw(croak);
|
||||
|
||||
# We're going to handle only codes that are errors.
|
||||
# http://code.google.com/p/selenium/wiki/JsonWireProtocol
|
||||
has STATUS_CODE => (
|
||||
is => 'lazy',
|
||||
builder => sub {
|
||||
return {
|
||||
7 => {
|
||||
'code' => 'NO_SUCH_ELEMENT',
|
||||
'msg' =>
|
||||
'An element could not be located on the page using the given search parameters.',
|
||||
},
|
||||
8 => {
|
||||
'code' => 'NO_SUCH_FRAME',
|
||||
'msg' =>
|
||||
'A request to switch to a frame could not be satisfied because the frame could not be found.',
|
||||
},
|
||||
9 => {
|
||||
'code' => 'UNKNOWN_COMMAND',
|
||||
'msg' =>
|
||||
'The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource.',
|
||||
},
|
||||
10 => {
|
||||
'code' => 'STALE_ELEMENT_REFERENCE',
|
||||
'msg' =>
|
||||
'An element command failed because the referenced element is no longer attached to the DOM.',
|
||||
},
|
||||
11 => {
|
||||
'code' => 'ELEMENT_NOT_VISIBLE',
|
||||
'msg' =>
|
||||
'An element command could not be completed because the element is not visible on the page.',
|
||||
},
|
||||
12 => {
|
||||
'code' => 'INVALID_ELEMENT_STATE',
|
||||
'msg' =>
|
||||
'An element command could not be completed because the element is in an invalid state (e.g. attempting to click a disabled element).',
|
||||
},
|
||||
13 => {
|
||||
'code' => 'UNKNOWN_ERROR',
|
||||
'msg' =>
|
||||
'An unknown server-side error occurred while processing the command.',
|
||||
},
|
||||
15 => {
|
||||
'code' => 'ELEMENT_IS_NOT_SELECTABLE',
|
||||
'msg' =>
|
||||
'An attempt was made to select an element that cannot be selected.',
|
||||
},
|
||||
19 => {
|
||||
'code' => 'XPATH_LOOKUP_ERROR',
|
||||
'msg' =>
|
||||
'An error occurred while searching for an element by XPath.',
|
||||
},
|
||||
21 => {
|
||||
'code' => 'Timeout',
|
||||
'msg' =>
|
||||
'An operation did not complete before its timeout expired.',
|
||||
},
|
||||
23 => {
|
||||
'code' => 'NO_SUCH_WINDOW',
|
||||
'msg' =>
|
||||
'A request to switch to a different window could not be satisfied because the window could not be found.',
|
||||
},
|
||||
24 => {
|
||||
'code' => 'INVALID_COOKIE_DOMAIN',
|
||||
'msg' =>
|
||||
'An illegal attempt was made to set a cookie under a different domain than the current page.',
|
||||
},
|
||||
25 => {
|
||||
'code' => 'UNABLE_TO_SET_COOKIE',
|
||||
'msg' =>
|
||||
'A request to set a cookie\'s value could not be satisfied.',
|
||||
},
|
||||
26 => {
|
||||
'code' => 'UNEXPECTED_ALERT_OPEN',
|
||||
'msg' => 'A modal dialog was open, blocking this operation',
|
||||
},
|
||||
27 => {
|
||||
'code' => 'NO_ALERT_OPEN_ERROR',
|
||||
'msg' =>
|
||||
'An attempt was made to operate on a modal dialog when one was not open.',
|
||||
},
|
||||
28 => {
|
||||
'code' => 'SCRIPT_TIMEOUT',
|
||||
'msg' =>
|
||||
'A script did not complete before its timeout expired.',
|
||||
},
|
||||
29 => {
|
||||
'code' => 'INVALID_ELEMENT_COORDINATES',
|
||||
'msg' =>
|
||||
'The coordinates provided to an interactions operation are invalid.',
|
||||
},
|
||||
30 => {
|
||||
'code' => 'IME_NOT_AVAILABLE',
|
||||
'msg' => 'IME was not available.',
|
||||
},
|
||||
31 => {
|
||||
'code' => 'IME_ENGINE_ACTIVATION_FAILED',
|
||||
'msg' => 'An IME engine could not be started.',
|
||||
},
|
||||
32 => {
|
||||
'code' => 'INVALID_SELECTOR',
|
||||
'msg' => 'Argument was an invalid selector (e.g. XPath/CSS).',
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
sub process_error {
|
||||
my ( $self, $resp ) = @_;
|
||||
|
||||
# TODO: Handle screen if it sent back with the response. Either we could
|
||||
# let the end user handle it or we can save it an image file at a temp
|
||||
# location & return the path.
|
||||
|
||||
# handle stacktrace-only responses by assuming unknown error
|
||||
my $is_stacktrace = !$resp->{status};
|
||||
$resp->{status} = 13 unless $resp->{status};
|
||||
|
||||
my $ret;
|
||||
|
||||
#XXX capitalization is inconsistent among geckodriver versions
|
||||
$ret->{'stackTrace'} = $resp->{'value'}->{'stacktrace'}
|
||||
// $resp->{'value'}->{'stackTrace'};
|
||||
$ret->{'error'} =
|
||||
$is_stacktrace
|
||||
? $resp->{value}->{error}
|
||||
: $self->STATUS_CODE->{ $resp->{'status'} };
|
||||
$ret->{'message'} = $resp->{'value'}->{'message'};
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Remote::ErrorHandler - Error handler for Selenium::Remote::Driver
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
=head2 process_error (Selenium::Remote::Driver $driver, HTTP::Response $response)
|
||||
|
||||
Instead of just returning the end user a server returned error code, this returns a more human readable & usable error message.
|
||||
|
||||
Used internally in Selenium::Remote::Driver, but overriding this might be useful in some situations.
|
||||
You could additionally alter the STATUS_CODE parameter of this module to add extra handlers if the situation warrants it.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
133
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/Finders.pm
Normal file
133
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/Finders.pm
Normal file
@@ -0,0 +1,133 @@
|
||||
package Selenium::Remote::Finders;
|
||||
$Selenium::Remote::Finders::VERSION = '1.33';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: Handle construction of generic parameter finders
|
||||
use Try::Tiny;
|
||||
use Carp qw/carp/;
|
||||
use Moo::Role;
|
||||
use namespace::clean;
|
||||
|
||||
|
||||
sub _build_find_by {
|
||||
my ( $self, $by ) = @_;
|
||||
|
||||
return sub {
|
||||
my ( $driver, $locator ) = @_;
|
||||
my $strategy = $by;
|
||||
|
||||
return try {
|
||||
return $driver->find_element( $locator, $strategy );
|
||||
}
|
||||
catch {
|
||||
carp $_;
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Remote::Finders - Handle construction of generic parameter finders
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This package just takes care of setting up parameter finders - that
|
||||
is, the C<find_element_by_.*> versions of the find element
|
||||
functions. You probably don't need to do anything with this package;
|
||||
instead, see L<Selenium::Remote::Driver/find_element> documentation
|
||||
for the specific finder functions.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
143
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/Mock/Commands.pm
Normal file
143
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/Mock/Commands.pm
Normal file
@@ -0,0 +1,143 @@
|
||||
package Selenium::Remote::Mock::Commands;
|
||||
$Selenium::Remote::Mock::Commands::VERSION = '1.33';
|
||||
# ABSTRACT: utility class to mock Selenium::Remote::Commands
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Moo;
|
||||
extends 'Selenium::Remote::Commands';
|
||||
|
||||
|
||||
# override get_params so we do not rewrite the parameters
|
||||
|
||||
sub get_params {
|
||||
my $self = shift;
|
||||
my $args = shift;
|
||||
my $data = {};
|
||||
my $command = delete $args->{command};
|
||||
$data->{'url'} = $self->get_url($command);
|
||||
$data->{'method'} = $self->get_method($command);
|
||||
$data->{'no_content_success'} = $self->get_no_content_success($command);
|
||||
$data->{'url_params'} = $args;
|
||||
return $data;
|
||||
}
|
||||
|
||||
sub get_method_name_from_parameters {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
my $method_name = '';
|
||||
my $cmds = $self->get_cmds();
|
||||
foreach my $cmd ( keys %{$cmds} ) {
|
||||
if ( ( $cmds->{$cmd}->{method} eq $params->{method} )
|
||||
&& ( $cmds->{$cmd}->{url} eq $params->{url} ) )
|
||||
{
|
||||
$method_name = $cmd;
|
||||
last;
|
||||
}
|
||||
}
|
||||
return $method_name;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Remote::Mock::Commands - utility class to mock Selenium::Remote::Commands
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Utility class to be for testing purposes, with L<Selenium::Remote::Mock::RemoteConnection> only.
|
||||
|
||||
=for Pod::Coverage *EVERYTHING*
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
@@ -0,0 +1,417 @@
|
||||
package Selenium::Remote::Mock::RemoteConnection;
|
||||
$Selenium::Remote::Mock::RemoteConnection::VERSION = '1.33';
|
||||
# ABSTRACT: utility class to mock the responses from Selenium server
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Moo;
|
||||
use JSON;
|
||||
use Carp;
|
||||
use Try::Tiny;
|
||||
use HTTP::Response;
|
||||
use Data::Dumper;
|
||||
|
||||
extends 'Selenium::Remote::RemoteConnection';
|
||||
|
||||
has 'spec' => (
|
||||
is => 'ro',
|
||||
default => sub { {} },
|
||||
);
|
||||
|
||||
has 'mock_cmds' => ( is => 'ro', );
|
||||
|
||||
has 'fake_session_id' => (
|
||||
is => 'lazy',
|
||||
builder => sub {
|
||||
my $id = join '',
|
||||
map +( 0 .. 9, 'a' .. 'z', 'A' .. 'Z' )[ rand( 10 + 26 * 2 ) ],
|
||||
1 .. 50;
|
||||
return $id;
|
||||
},
|
||||
);
|
||||
|
||||
has 'record' => (
|
||||
is => 'ro',
|
||||
default => sub { 0 }
|
||||
);
|
||||
|
||||
has 'replay' => ( is => 'ro', );
|
||||
|
||||
has 'replay_file' => ( is => 'ro', );
|
||||
|
||||
has 'session_store' => (
|
||||
is => 'rw',
|
||||
default => sub { {} }
|
||||
);
|
||||
|
||||
has 'session_id' => (
|
||||
is => 'rw',
|
||||
default => sub { undef },
|
||||
);
|
||||
|
||||
has 'remote_server_addr' => (
|
||||
is => 'lazy',
|
||||
default => sub { 'localhost' }
|
||||
);
|
||||
|
||||
|
||||
sub BUILD {
|
||||
my $self = shift;
|
||||
croak 'Cannot define replay and record attributes at the same time'
|
||||
if ( ( $self->replay ) && ( $self->record ) );
|
||||
croak 'replay_file attribute needs to be defined'
|
||||
if ( ( $self->replay ) && !( $self->replay_file ) );
|
||||
croak 'replay attribute needs to be defined'
|
||||
if ( !( $self->replay ) && ( $self->replay_file ) );
|
||||
$self->port('4444');
|
||||
if ( $self->replay ) {
|
||||
$self->load_session_store( $self->replay_file );
|
||||
}
|
||||
}
|
||||
|
||||
sub check_status {
|
||||
return;
|
||||
}
|
||||
|
||||
sub load_session_store {
|
||||
my $self = shift;
|
||||
my $file = shift;
|
||||
croak "'$file' is not a valid file" unless ( -f $file );
|
||||
open( my $fh, '<', $file ) or croak "Opening '$file' failed";
|
||||
|
||||
# here we use a fake session id since we have no way of figuring out
|
||||
# which session is good or not
|
||||
local $/ = undef;
|
||||
|
||||
my $json = JSON->new;
|
||||
$json->allow_blessed;
|
||||
my $decoded_json = $json->allow_nonref(1)->utf8(1)->decode(<$fh>);
|
||||
close($fh);
|
||||
$self->session_store($decoded_json);
|
||||
}
|
||||
|
||||
sub dump_session_store {
|
||||
my $self = shift;
|
||||
my ($file) = @_;
|
||||
open( my $fh, '>', $file ) or croak "Opening '$file' failed";
|
||||
my $session_store = $self->session_store;
|
||||
my $dump = {};
|
||||
foreach my $path ( keys %{$session_store} ) {
|
||||
$dump->{$path} = $session_store->{$path};
|
||||
}
|
||||
my $json = JSON->new;
|
||||
$json->allow_blessed;
|
||||
my $json_session = $json->allow_nonref->utf8->pretty->encode($dump);
|
||||
print $fh $json_session;
|
||||
close($fh);
|
||||
}
|
||||
|
||||
sub request {
|
||||
my $self = shift;
|
||||
my ( $resource, $params ) = @_;
|
||||
my $method = $resource->{method};
|
||||
my $url = $resource->{url};
|
||||
my $no_content_success = $resource->{no_content_success} // 0;
|
||||
my $content = '';
|
||||
my $json = JSON->new;
|
||||
$json->allow_blessed;
|
||||
|
||||
if ($params) {
|
||||
$content = $json->allow_nonref->utf8->canonical(1)->encode($params);
|
||||
}
|
||||
my $url_params = $resource->{url_params};
|
||||
|
||||
print "REQ: $method, $url, $content\n" if $self->debug;
|
||||
|
||||
if ( $self->record ) {
|
||||
my $response = $self->SUPER::request( $resource, $params, 1 );
|
||||
push @{ $self->session_store->{"$method $url $content"} },
|
||||
$response->as_string;
|
||||
return $self->_process_response( $response, $no_content_success );
|
||||
}
|
||||
if ( $self->replay ) {
|
||||
my $resp;
|
||||
my $arr_of_resps = $self->session_store->{"$method $url $content"}
|
||||
// [];
|
||||
if ( scalar(@$arr_of_resps) ) {
|
||||
$resp = shift @$arr_of_resps;
|
||||
$resp = HTTP::Response->parse($resp);
|
||||
}
|
||||
else {
|
||||
$resp = HTTP::Response->new( '501', "Failed to find a response" );
|
||||
}
|
||||
return $self->_process_response( $resp, $no_content_success );
|
||||
}
|
||||
my $mock_cmds = $self->mock_cmds;
|
||||
my $spec = $self->spec;
|
||||
my $cmd = $mock_cmds->get_method_name_from_parameters(
|
||||
{ method => $method, url => $url } );
|
||||
my $ret = { cmd_status => 'OK', cmd_return => 1 };
|
||||
if ( defined( $spec->{$cmd} ) ) {
|
||||
my $return_sub = $spec->{$cmd};
|
||||
my $mock_return = $return_sub->( $url_params, $params );
|
||||
if ( ref($mock_return) eq 'HASH' ) {
|
||||
$ret->{cmd_status} = $mock_return->{status};
|
||||
$ret->{cmd_return} = $mock_return->{return};
|
||||
$ret->{cmd_error} = $mock_return->{error} // '';
|
||||
}
|
||||
else {
|
||||
$ret = $mock_return;
|
||||
}
|
||||
$ret->{session_id} = $self->fake_session_id if ( ref($ret) eq 'HASH' );
|
||||
}
|
||||
else {
|
||||
$ret->{sessionId} = $self->fake_session_id;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Remote::Mock::RemoteConnection - utility class to mock the responses from Selenium server
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
=head2 Record interactions
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Selenium::Remote::Driver;
|
||||
use Selenium::Remote::Mock::RemoteConnection;
|
||||
|
||||
# create a new Mock object to record the interactions with Selenium
|
||||
# Server
|
||||
my $mock_connection = Selenium::Remote::Mock::RemoteConnection->new( record => 1 );
|
||||
|
||||
# the Mock object is passed to the driver in place of what would be
|
||||
# a regular Selenium::Remote::RemoteConnection object
|
||||
my $driver = Selenium::Remote::Driver->new( remote_conn => $mock_connection );
|
||||
|
||||
# always store the session id, as it will become undef once
|
||||
# $driver->quit is called
|
||||
my $session_id = $driver->session_id;
|
||||
|
||||
# do all the selenium things and quit
|
||||
$driver->get('http://www.google.com');
|
||||
$driver->get('http://www.wikipedia.com');
|
||||
$driver->quit;
|
||||
|
||||
# dump the session to a file
|
||||
$mock_connection->dump_session_store( 'my_record.json' );
|
||||
|
||||
This code, above doing some basic Selenium interactions, will end up generating a JSON file containing all the requests and their responses for your Selenium session.
|
||||
The JSON file looks like this :
|
||||
|
||||
'{
|
||||
"HTTP_REQUEST_URL {request_parameters}":[response1,response2,...],
|
||||
...
|
||||
}'
|
||||
|
||||
The reason why we store array of responses is that the exact same request can be made more than once during a session, so we have to store every response to the same requests.
|
||||
|
||||
=head2 Replay interactions
|
||||
|
||||
#!perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More;
|
||||
use Test::Selenium::Remote::Driver;
|
||||
use Selenium::Remote::Mock::RemoteConnection;
|
||||
my $mock_connection_2 =
|
||||
Selenium::Remote::Mock::RemoteConnection->new( replay => 1,
|
||||
replay_file => 'my_record.json' );
|
||||
# javascript + version parameters added or else it will not work
|
||||
my $driver =
|
||||
Test::Selenium::Remote::Driver->new( remote_conn => $mock_connection_2, javascript => 1, version => '' );
|
||||
$driver->get_ok('http://www.google.com');
|
||||
$driver->get_ok('http://www.wikipedia.com');
|
||||
$driver->quit;
|
||||
done_testing;
|
||||
|
||||
Using the file generated with the recording snippet from the section before, we are able to mock the responses.
|
||||
|
||||
Note that there is one small limitation (that I hope to remove in future versions), is that a record generated with L<Selenium::Remote::Driver> is not directly useable with L<Test::Selenium::Remote::Driver>.
|
||||
This is mainly because the way the two instances are created are a bit different, which leads to different requests made, for creating a session for instance.
|
||||
For now, what works for sure is recording and replaying from the same class.
|
||||
|
||||
=head2 Mock responses
|
||||
|
||||
#!perl
|
||||
use Test::More;
|
||||
use Test::Selenium::Remote::Driver;
|
||||
use Selenium::Remote::WebElement;
|
||||
use Selenium::Remote::Mock::Commands;
|
||||
use Selenium::Remote::Mock::RemoteConnection;
|
||||
|
||||
my $spec = {
|
||||
findElement => sub {
|
||||
my (undef,$searched_item) = @_;
|
||||
return { status => 'OK', return => { ELEMENT => '123456' } }
|
||||
if ( $searched_item->{value} eq 'q' );
|
||||
return { status => 'NOK', return => 0, error => 'element not found' };
|
||||
},
|
||||
getPageSource => sub { return 'this output matches regex'},
|
||||
};
|
||||
my $mock_commands = Selenium::Remote::Mock::Commands->new;
|
||||
|
||||
my $successful_driver =
|
||||
Test::Selenium::Remote::Driver->new(
|
||||
remote_conn => Selenium::Remote::Mock::RemoteConnection->new( spec => $spec, mock_cmds => $mock_commands ),
|
||||
commands => $mock_commands,
|
||||
);
|
||||
$successful_driver->find_element_ok('q','find_element_ok works');
|
||||
dies_ok { $successful_driver->find_element_ok('notq') } 'find_element_ok dies if element not found';
|
||||
$successful_driver->find_no_element_ok('notq','find_no_element_ok works');
|
||||
$successful_driver->content_like( qr/matches/, 'content_like works');
|
||||
$successful_driver->content_unlike( qr/nomatch/, 'content_unlike works');
|
||||
|
||||
done_testing();
|
||||
|
||||
Mocking responses by hand requires a more advanced knowledge of the underlying implementation of L<Selenium::Remote::Driver>.
|
||||
What we mock here is the processed response that will be returned by L<Selenium::Remote::RemoteConnection> to '_execute_command' call.
|
||||
To accomplish this we need :
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
a spec: a HASHREF which keys are the name of the methods we want to mock. Note that those keys should also be valid keys from the _cmds attribute in L<Selenium::Remote::Commands>.
|
||||
The value of each key is a sub which will be given two parameters:
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
$url_params : the values that should have been replaced in the URL
|
||||
For instance, on the example above, it would have been:
|
||||
{ session_id => 'some_session_id'}
|
||||
|
||||
=item *
|
||||
$params : the original parameters of the request.
|
||||
On the example above it would have been:
|
||||
{ value => 'q', using => 'xpath'}
|
||||
|
||||
=back
|
||||
|
||||
The sub used as a value in the spec is not expected to return anything, so you have to craft very carefully what you return so that it will produce the expected result.
|
||||
|
||||
=item *
|
||||
a mock_cmd: a L<Selenium::Remote::Mock::Commands> object. This is used mainly to hijack the normal commands so that placeholders do not get replaced in the URLs.
|
||||
|
||||
=back
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Selenium::Remote::Mock::RemoteConnection is a class to act as a short-circuit or a pass through to the connection to a Selenium Server.
|
||||
Using this class in place of L<Selenium::Remote::RemoteConnection> allows to:
|
||||
|
||||
=over
|
||||
|
||||
=item *
|
||||
record interactions with the Selenium Server into a JSON file
|
||||
|
||||
=item *
|
||||
replay recorded interactions from a JSON file to mock answers from the Selenium Server
|
||||
|
||||
=item *
|
||||
mock responses to specific functions
|
||||
|
||||
=back
|
||||
|
||||
=for Pod::Coverage *EVERYTHING*
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
This code is really early alpha, so its API might change. Use with caution !
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
361
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/RemoteConnection.pm
Normal file
361
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/RemoteConnection.pm
Normal file
@@ -0,0 +1,361 @@
|
||||
package Selenium::Remote::RemoteConnection;
|
||||
$Selenium::Remote::RemoteConnection::VERSION = '1.33';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
#ABSTRACT: Connect to a selenium server
|
||||
|
||||
use Moo;
|
||||
use Try::Tiny;
|
||||
use LWP::UserAgent;
|
||||
use HTTP::Headers;
|
||||
use HTTP::Request;
|
||||
use Carp qw(croak);
|
||||
use JSON;
|
||||
use Data::Dumper;
|
||||
use Selenium::Remote::ErrorHandler;
|
||||
use Scalar::Util qw{looks_like_number};
|
||||
|
||||
has 'remote_server_addr' => ( is => 'rw', );
|
||||
|
||||
has 'port' => ( is => 'rw', );
|
||||
|
||||
has 'debug' => (
|
||||
is => 'rw',
|
||||
default => sub { 0 }
|
||||
);
|
||||
|
||||
has 'ua' => (
|
||||
is => 'lazy',
|
||||
builder => sub { return LWP::UserAgent->new; }
|
||||
);
|
||||
|
||||
has 'error_handler' => (
|
||||
is => 'lazy',
|
||||
builder => sub { return Selenium::Remote::ErrorHandler->new; }
|
||||
);
|
||||
|
||||
with 'Selenium::Remote::Driver::CanSetWebdriverContext';
|
||||
|
||||
|
||||
sub check_status {
|
||||
my $self = shift;
|
||||
my $status;
|
||||
|
||||
try {
|
||||
$status = $self->request( { method => 'GET', url => 'status' } );
|
||||
}
|
||||
catch {
|
||||
croak "Could not connect to SeleniumWebDriver: $_";
|
||||
};
|
||||
|
||||
if ( $status->{cmd_status} ne 'OK' ) {
|
||||
|
||||
# Could be grid, see if we can talk to it
|
||||
$status = undef;
|
||||
$status =
|
||||
$self->request( { method => 'GET', url => 'grid/api/hub/status' } );
|
||||
}
|
||||
|
||||
unless ( $status->{cmd_status} eq 'OK' ) {
|
||||
croak "Selenium server did not return proper status";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sub request {
|
||||
my ( $self, $resource, $params, $dont_process_response ) = @_;
|
||||
my $method = $resource->{method};
|
||||
my $url = $resource->{url};
|
||||
my $no_content_success = $resource->{no_content_success} // 0;
|
||||
|
||||
my $content = '';
|
||||
my $fullurl = '';
|
||||
|
||||
# Construct full url.
|
||||
if ( $url =~ m/^http/g ) {
|
||||
$fullurl = $url;
|
||||
}
|
||||
elsif ( $url =~ m/^\// ) {
|
||||
|
||||
# This is used when we get a 302 Redirect with a Location header.
|
||||
$fullurl =
|
||||
"http://" . $self->remote_server_addr . ":" . $self->port . $url;
|
||||
}
|
||||
elsif ( $url =~ m/grid/g ) {
|
||||
$fullurl =
|
||||
"http://" . $self->remote_server_addr . ":" . $self->port . "/$url";
|
||||
}
|
||||
else {
|
||||
$fullurl =
|
||||
"http://"
|
||||
. $self->remote_server_addr . ":"
|
||||
. $self->port
|
||||
. $self->wd_context_prefix . "/$url";
|
||||
}
|
||||
|
||||
if ( ( defined $params ) && $params ne '' ) {
|
||||
|
||||
#WebDriver 3 shims
|
||||
if ( $resource->{payload} ) {
|
||||
foreach my $key ( keys( %{ $resource->{payload} } ) ) {
|
||||
$params->{$key} = $resource->{payload}->{$key};
|
||||
}
|
||||
}
|
||||
|
||||
my $json = JSON->new;
|
||||
$json->allow_blessed;
|
||||
$content = $json->allow_nonref->utf8->encode($params);
|
||||
}
|
||||
|
||||
print "REQ: $method, $fullurl, $content\n" if $self->debug;
|
||||
|
||||
# HTTP request
|
||||
my $header =
|
||||
HTTP::Headers->new( Content_Type => 'application/json; charset=utf-8' );
|
||||
$header->header( 'Accept' => 'application/json' );
|
||||
my $request = HTTP::Request->new( $method, $fullurl, $header, $content );
|
||||
my $response = $self->ua->request($request);
|
||||
if ($dont_process_response) {
|
||||
return $response;
|
||||
}
|
||||
return $self->_process_response( $response, $no_content_success );
|
||||
}
|
||||
|
||||
sub _process_response {
|
||||
my ( $self, $response, $no_content_success ) = @_;
|
||||
my $data; # server response 'value' that'll be returned to the user
|
||||
my $json = JSON->new;
|
||||
|
||||
if ( $response->is_redirect ) {
|
||||
my $redirect = {
|
||||
method => 'GET',
|
||||
url => $response->header('location')
|
||||
};
|
||||
return $self->request($redirect);
|
||||
}
|
||||
else {
|
||||
my $decoded_json = undef;
|
||||
print "RES: " . $response->decoded_content . "\n\n" if $self->debug;
|
||||
|
||||
if ( ( $response->message ne 'No Content' )
|
||||
&& ( $response->content ne '' ) )
|
||||
{
|
||||
if ( $response->content_type !~ m/json/i ) {
|
||||
$data->{'cmd_status'} = 'NOTOK';
|
||||
$data->{'cmd_return'}->{message} =
|
||||
'Server returned error message '
|
||||
. $response->content
|
||||
. ' instead of data';
|
||||
return $data;
|
||||
}
|
||||
$decoded_json =
|
||||
$json->allow_nonref(1)->utf8(1)->decode( $response->content );
|
||||
$data->{'sessionId'} = $decoded_json->{'sessionId'};
|
||||
}
|
||||
|
||||
if ( $response->is_error ) {
|
||||
$data->{'cmd_status'} = 'NOTOK';
|
||||
if ( defined $decoded_json ) {
|
||||
$data->{'cmd_return'} =
|
||||
$self->error_handler->process_error($decoded_json);
|
||||
}
|
||||
else {
|
||||
$data->{'cmd_return'} =
|
||||
'Server returned error code '
|
||||
. $response->code
|
||||
. ' and no data';
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
elsif ( $response->is_success ) {
|
||||
$data->{'cmd_status'} = 'OK';
|
||||
if ( defined $decoded_json ) {
|
||||
|
||||
#XXX MS edge doesn't follow spec here either
|
||||
if ( looks_like_number( $decoded_json->{status} )
|
||||
&& $decoded_json->{status} > 0
|
||||
&& $decoded_json->{value}{message} )
|
||||
{
|
||||
$data->{cmd_status} = 'NOT OK';
|
||||
$data->{cmd_return} = $decoded_json->{value};
|
||||
return $data;
|
||||
}
|
||||
|
||||
#XXX shockingly, neither does InternetExplorerDriver
|
||||
if ( ref $decoded_json eq 'HASH' && $decoded_json->{error} ) {
|
||||
$data->{cmd_status} = 'NOT OK';
|
||||
$data->{cmd_return} = $decoded_json;
|
||||
return $data;
|
||||
}
|
||||
|
||||
if ($no_content_success) {
|
||||
$data->{'cmd_return'} = 1;
|
||||
}
|
||||
else {
|
||||
$data->{'cmd_return'} = $decoded_json->{'value'};
|
||||
if ( ref( $data->{cmd_return} ) eq 'HASH'
|
||||
&& exists $data->{cmd_return}->{sessionId} )
|
||||
{
|
||||
$data->{sessionId} = $data->{cmd_return}->{sessionId};
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$data->{'cmd_return'} =
|
||||
'Server returned status code '
|
||||
. $response->code
|
||||
. ' but no data';
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
else {
|
||||
# No idea what the server is telling me, must be high
|
||||
$data->{'cmd_status'} = 'NOTOK';
|
||||
$data->{'cmd_return'} =
|
||||
'Server returned status code '
|
||||
. $response->code
|
||||
. ' which I don\'t understand';
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Remote::RemoteConnection - Connect to a selenium server
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
my $driver = Selenium::Remote::Driver->new();
|
||||
eval { $driver->remote_conn->check_status() };
|
||||
die "do something to kick the server" if $@;
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
You shouldn't really need to use this module unless debugging or checking connections when testing dangerous things.
|
||||
|
||||
=head1 CONSTRUCTOR
|
||||
|
||||
=head2 new(%parameters)
|
||||
|
||||
Accepts 5 parameters:
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<remote_server_addr> - address of selenium server
|
||||
|
||||
=item B<port> - port of selenium server
|
||||
|
||||
=item B<ua> - Useful to override with Test::LWP::UserAgent in unit tests
|
||||
|
||||
=item B<debug> - Should be self-explanatory
|
||||
|
||||
=item B<error_handler> - Defaults to Selenium::Remote::ErrorHandler.
|
||||
|
||||
=back
|
||||
|
||||
These can be set any time later by getter/setters with the same name.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 check_status
|
||||
|
||||
Croaks unless the selenium server is responsive. Sometimes is useful to call in-between tests (the server CAN die on you...)
|
||||
|
||||
=head2 request
|
||||
|
||||
Make a request of the Selenium server. Mostly useful for debugging things going wrong with Selenium::Remote::Driver when not in normal operation.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
364
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/Spec.pm
Normal file
364
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/Spec.pm
Normal file
@@ -0,0 +1,364 @@
|
||||
package Selenium::Remote::Spec;
|
||||
$Selenium::Remote::Spec::VERSION = '1.33';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: Implement commands for Selenium::Remote::Driver
|
||||
|
||||
|
||||
use Carp qw{croak};
|
||||
use List::Util qw{any};
|
||||
|
||||
use Moo;
|
||||
extends 'Selenium::Remote::Commands';
|
||||
|
||||
#Ripped from the headlines: https://w3c.github.io/webdriver/webdriver-spec.html
|
||||
#then add 2 params for our use
|
||||
|
||||
#Method URI Template no_content_success internal_name Command
|
||||
our $spec = qq{
|
||||
POST session 0 newSession New Session
|
||||
POST session 0 getCapabilities Get Capabilities (v2->v3 shim)
|
||||
DELETE session/:sessionId 1 quit Delete Session
|
||||
GET status 0 status Status
|
||||
GET session/:sessionId/timeouts 0 getTimeouts Get Timeouts
|
||||
POST session/:sessionId/timeouts 1 setTimeout Set Page Load timeout (v2->v3 shim)
|
||||
POST session/:sessionId/timeouts/async_script 1 setAsyncScriptTimeout Set Async script timeout (v2->v3 shim)
|
||||
POST session/:sessionId/timeouts/implicit_wait 1 setImplicitWaitTimeout Set Implicit wait timeout (v2->v3 shim)
|
||||
POST session/:sessionId/url 1 get Navigate To
|
||||
GET session/:sessionId/url 0 getCurrentUrl Get Current URL
|
||||
POST session/:sessionId/back 1 goBack Back
|
||||
POST session/:sessionId/forward 1 goForward Forward
|
||||
POST session/:sessionId/refresh 1 refresh Refresh
|
||||
GET session/:sessionId/title 0 getTitle Get Title
|
||||
GET session/:sessionId/window 0 getCurrentWindowHandle Get Currently Focused Window Handle
|
||||
DELETE session/:sessionId/window 1 close Close Currently Focused Window
|
||||
POST session/:sessionId/window 1 switchToWindow Switch To Window
|
||||
GET session/:sessionId/window/handles 0 getWindowHandles Get Window Handles
|
||||
POST session/:sessionId/frame 1 switchToFrame Switch To Frame
|
||||
POST session/:sessionId/frame/parent 1 switchToParentFrame Switch To Parent Frame
|
||||
GET session/:sessionId/window/rect 0 getWindowRect Get Window Size/Position (v2->v3 shim)
|
||||
POST session/:sessionId/window/rect 1 setWindowRect Set Window Size/Position (v2->v3 shim)
|
||||
POST session/:sessionId/window/maximize 1 maximizeWindow Maximize Window
|
||||
POST session/:sessionId/window/minimize 1 minimizeWindow Minimize Window
|
||||
POST session/:sessionId/window/fullscreen 1 fullscreenWindow Fullscreen Window
|
||||
GET session/:sessionId/element/active 0 getActiveElement Get Active Element
|
||||
POST session/:sessionId/element 0 findElement Find Element
|
||||
POST session/:sessionId/elements 0 findElements Find Elements
|
||||
POST session/:sessionId/element/:id/element 0 findChildElement Find Element From Element
|
||||
POST session/:sessionId/element/:id/elements 0 findChildElements Find Elements From Element
|
||||
GET session/:sessionId/element/:id/selected 0 isElementSelected Is Element Selected
|
||||
GET session/:sessionId/element/:id/attribute/:name 0 getElementAttribute Get Element Attribute
|
||||
GET session/:sessionId/element/:id/property/:name 0 getElementProperty Get Element Property
|
||||
GET session/:sessionId/element/:id/css/:propertyName 0 getElementValueOfCssProperty Get Element CSS Value
|
||||
GET session/:sessionId/element/:id/text 0 getElementText Get Element Text
|
||||
GET session/:sessionId/element/:id/name 0 getElementTagName Get Element Tag Name
|
||||
GET session/:sessionId/element/:id/rect 0 getElementRect Get Element Rect
|
||||
GET session/:sessionId/element/:id/enabled 0 isElementEnabled Is Element Enabled
|
||||
POST session/:sessionId/element/:id/click 1 clickElement Element Click
|
||||
POST session/:sessionId/element/:id/clear 1 clearElement Element Clear
|
||||
POST session/:sessionId/element/:id/value 1 sendKeysToElement Element Send Keys
|
||||
GET session/:sessionId/source 0 getPageSource Get Page Source
|
||||
POST session/:sessionId/execute/sync 0 executeScript Execute Script
|
||||
POST session/:sessionId/execute/async 0 executeAsyncScript Execute Async Script
|
||||
GET session/:sessionId/cookie 0 getAllCookies Get All Cookies
|
||||
GET session/:sessionId/cookie/:name 0 getCookieNamed Get Named Cookie
|
||||
POST session/:sessionId/cookie 1 addCookie Add Cookie
|
||||
DELETE session/:sessionId/cookie/:name 1 deleteCookieNamed Delete Cookie
|
||||
DELETE session/:sessionId/cookie 1 deleteAllCookies Delete All Cookies
|
||||
POST session/:sessionId/actions 1 generalAction Perform Actions
|
||||
DELETE session/:sessionId/actions 1 releaseGeneralAction Release Actions
|
||||
POST session/:sessionId/alert/dismiss 1 dismissAlert Dismiss Alert
|
||||
POST session/:sessionId/alert/accept 1 acceptAlert Accept Alert
|
||||
GET session/:sessionId/alert/text 0 getAlertText Get Alert Text
|
||||
POST session/:sessionId/alert/text 1 sendKeysToPrompt Send Alert Text
|
||||
GET session/:sessionId/screenshot 0 screenshot Take Screenshot
|
||||
GET session/:sessionId/moz/screenshot/full 0 mozScreenshotFull Take Full Screenshot
|
||||
GET session/:sessionId/element/:id/screenshot 0 elementScreenshot Take Element Screenshot
|
||||
};
|
||||
|
||||
our $spec_parsed;
|
||||
|
||||
sub get_spec {
|
||||
return $spec_parsed if $spec_parsed;
|
||||
my @split = split( /\n/, $spec );
|
||||
foreach my $line (@split) {
|
||||
next unless $line;
|
||||
my ( $method, $uri, $nc_success, $key, @description ) =
|
||||
split( / +/, $line );
|
||||
$spec_parsed->{$key} = {
|
||||
method => $method,
|
||||
url => $uri,
|
||||
no_content_success => int($nc_success)
|
||||
, #XXX this *should* always be 0, but specs lie
|
||||
description => join( ' ', @description ),
|
||||
};
|
||||
}
|
||||
return $spec_parsed;
|
||||
}
|
||||
|
||||
has '_cmds' => (
|
||||
is => 'lazy',
|
||||
reader => 'get_cmds',
|
||||
builder => \&get_spec,
|
||||
);
|
||||
|
||||
|
||||
has '_caps' => (
|
||||
is => 'lazy',
|
||||
reader => 'get_caps',
|
||||
builder => sub {
|
||||
return [
|
||||
'browserName', 'acceptInsecureCerts',
|
||||
'browserVersion', 'platformName',
|
||||
'proxy', 'pageLoadStrategy',
|
||||
'setWindowRect', 'timeouts',
|
||||
'unhandledPromptBehavior', 'moz:firefoxOptions',
|
||||
'chromeOptions',
|
||||
];
|
||||
}
|
||||
);
|
||||
|
||||
has '_caps_map' => (
|
||||
is => 'lazy',
|
||||
reader => 'get_caps_map',
|
||||
builder => sub {
|
||||
return {
|
||||
browserName => 'browserName',
|
||||
acceptSslCerts => 'acceptInsecureCerts',
|
||||
version => 'browserVersion',
|
||||
platform => 'platformName',
|
||||
proxy => 'proxy',
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
sub get_params {
|
||||
my ( $self, $args ) = @_;
|
||||
if ( !( defined $args->{'session_id'} ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
#Allow fall-back in the event the command passed doesn't exist
|
||||
return unless $self->get_cmds()->{ $args->{command} };
|
||||
|
||||
my $url = $self->get_url( $args->{command} );
|
||||
|
||||
my $data = {};
|
||||
|
||||
# Do the var substitutions.
|
||||
$url =~ s/:sessionId/$args->{'session_id'}/;
|
||||
$url =~ s/:id/$args->{'id'}/;
|
||||
$url =~ s/:name/$args->{'name'}/;
|
||||
$url =~ s/:propertyName/$args->{'property_name'}/;
|
||||
$url =~ s/:other/$args->{'other'}/;
|
||||
$url =~ s/:windowHandle/$args->{'window_handle'}/;
|
||||
|
||||
$data->{'method'} = $self->get_method( $args->{command} );
|
||||
$data->{'no_content_success'} =
|
||||
$self->get_no_content_success( $args->{command} );
|
||||
$data->{'url'} = $url;
|
||||
|
||||
#URL & data polyfills for the way selenium2 used to do things, etc
|
||||
$data->{payload} = {};
|
||||
if ( $args->{type} ) {
|
||||
$data->{payload}->{pageLoad} = $args->{ms}
|
||||
if $data->{url} =~ m/timeouts$/ && $args->{type} eq 'page load';
|
||||
$data->{payload}->{script} = $args->{ms}
|
||||
if $data->{url} =~ m/timeouts$/ && $args->{type} eq 'script';
|
||||
$data->{payload}->{implicit} = $args->{ms}
|
||||
if $data->{url} =~ m/timeouts$/ && $args->{type} eq 'implicit';
|
||||
}
|
||||
|
||||
#finder polyfills
|
||||
#orig: class, class_name, css, id, link, link_text, partial_link_text, tag_name, name, xpath
|
||||
#new: "css selector", "link text", "partial link text", "tag name", "xpath"
|
||||
#map: class, class_name, id, name, link = 'css selector'
|
||||
if ( $args->{using} && $args->{value} ) {
|
||||
$data->{payload}->{using} = 'css selector'
|
||||
if grep { $args->{using} eq $_ } ( 'id', 'class name', 'name' );
|
||||
$data->{payload}->{value} = "[id='$args->{value}']"
|
||||
if $args->{using} eq 'id';
|
||||
$data->{payload}->{value} = ".$args->{value}"
|
||||
if $args->{using} eq 'class name';
|
||||
$data->{payload}->{value} = "[name='$args->{value}']"
|
||||
if $args->{using} eq 'name';
|
||||
}
|
||||
if ( $data->{url} =~ s/timeouts\/async_script$/timeouts/g ) {
|
||||
$data->{payload}->{script} = $args->{ms};
|
||||
$data->{payload}->{type} = 'script'; #XXX chrome doesn't follow the spec
|
||||
}
|
||||
if ( $data->{url} =~ s/timeouts\/implicit_wait$/timeouts/g ) {
|
||||
$data->{payload}->{implicit} = $args->{ms};
|
||||
$data->{payload}->{type} =
|
||||
'implicit'; #XXX chrome doesn't follow the spec
|
||||
}
|
||||
$data->{payload}->{value} = $args->{text}
|
||||
if $args->{text} && $args->{command} ne 'sendKeysToElement';
|
||||
$data->{payload}->{handle} = $args->{window_handle}
|
||||
if grep { $args->{command} eq $_ }
|
||||
qw{fullscreenWindow minimizeWindow maximizeWindow};
|
||||
return $data;
|
||||
}
|
||||
|
||||
sub parse_response {
|
||||
my ( $self, undef, $resp ) = @_;
|
||||
|
||||
if ( ref($resp) eq 'HASH' ) {
|
||||
if ( $resp->{cmd_status} && $resp->{cmd_status} eq 'OK' ) {
|
||||
return $resp->{cmd_return};
|
||||
}
|
||||
my $msg = "Error while executing command";
|
||||
if ( ref $resp->{cmd_return} eq 'HASH' ) {
|
||||
$msg .= ": $resp->{cmd_return}{error}"
|
||||
if $resp->{cmd_return}{error};
|
||||
$msg .= ": $resp->{cmd_return}{message}"
|
||||
if $resp->{cmd_return}{message};
|
||||
}
|
||||
else {
|
||||
$msg .= ": $resp->{cmd_return}";
|
||||
}
|
||||
croak $msg;
|
||||
}
|
||||
|
||||
return $resp;
|
||||
}
|
||||
|
||||
#Utility
|
||||
|
||||
sub get_spec_differences {
|
||||
my $v2_spec = Selenium::Remote::Commands->new()->get_cmds();
|
||||
my $v3_spec = Selenium::Remote::Spec->new()->get_cmds();
|
||||
|
||||
foreach my $key ( keys(%$v2_spec) ) {
|
||||
print "v2 $key NOT present in v3 spec!!!\n"
|
||||
unless any { $_ eq $key } keys(%$v3_spec);
|
||||
}
|
||||
foreach my $key ( keys(%$v3_spec) ) {
|
||||
print "v3 $key NOT present in v2 spec!!!\n"
|
||||
unless any { $_ eq $key } keys(%$v2_spec);
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Remote::Spec - Implement commands for Selenium::Remote::Driver
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Defines all the HTTP endpoints available to execute on a selenium server.
|
||||
|
||||
If you have either a customized Selenium Server, or want new features
|
||||
you should update the _cmds hash.
|
||||
|
||||
=for Pod::Coverage *EVERYTHING*
|
||||
|
||||
=head1 Webdriver 3 capabilities
|
||||
|
||||
WD3 giveth and taketh away some caps. Here's all you get:
|
||||
|
||||
Browser name: "browserName" string Identifies the user agent.
|
||||
Browser version: "browserVersion" string Identifies the version of the user agent.
|
||||
Platform name: "platformName" string Identifies the operating system of the endpoint node.
|
||||
Accept insecure TLS certificates: "acceptInsecureCerts" boolean Indicates whether untrusted and self-signed TLS certificates are implicitly trusted on navigation for the duration of the session.
|
||||
Proxy configuration: "proxy" JSON Defines the current session’s proxy configuration.
|
||||
|
||||
New Stuff:
|
||||
|
||||
Page load strategy: "pageLoadStrategy" string Defines the current session’s page load strategy.
|
||||
Window dimensioning/positioning: "setWindowRect" boolean Indicates whether the remote end supports all of the commands in Resizing and Positioning Windows.
|
||||
Session timeouts configuration: "timeouts" JSON Describes the timeouts imposed on certain session operations.
|
||||
Unhandled prompt behavior: "unhandledPromptBehavior" string Describes the current session’s user prompt handler.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
173
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/WDKeys.pm
Normal file
173
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/WDKeys.pm
Normal file
@@ -0,0 +1,173 @@
|
||||
package Selenium::Remote::WDKeys;
|
||||
$Selenium::Remote::WDKeys::VERSION = '1.33';
|
||||
# ABSTRACT: Representation of keystrokes used by Selenium::Remote::WebDriver
|
||||
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'Exporter';
|
||||
|
||||
# http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value
|
||||
use constant KEYS => {
|
||||
'null' => "\N{U+E000}",
|
||||
'cancel' => "\N{U+E001}",
|
||||
'help' => "\N{U+E002}",
|
||||
'backspace' => "\N{U+E003}",
|
||||
'tab' => "\N{U+E004}",
|
||||
'clear' => "\N{U+E005}",
|
||||
'return' => "\N{U+E006}",
|
||||
'enter' => "\N{U+E007}",
|
||||
'shift' => "\N{U+E008}",
|
||||
'control' => "\N{U+E009}",
|
||||
'alt' => "\N{U+E00A}",
|
||||
'pause' => "\N{U+E00B}",
|
||||
'escape' => "\N{U+E00C}",
|
||||
'space' => "\N{U+E00D}",
|
||||
'page_up' => "\N{U+E00E}",
|
||||
'page_down' => "\N{U+E00f}",
|
||||
'end' => "\N{U+E010}",
|
||||
'home' => "\N{U+E011}",
|
||||
'left_arrow' => "\N{U+E012}",
|
||||
'up_arrow' => "\N{U+E013}",
|
||||
'right_arrow' => "\N{U+E014}",
|
||||
'down_arrow' => "\N{U+E015}",
|
||||
'insert' => "\N{U+E016}",
|
||||
'delete' => "\N{U+E017}",
|
||||
'semicolon' => "\N{U+E018}",
|
||||
'equals' => "\N{U+E019}",
|
||||
'numpad_0' => "\N{U+E01A}",
|
||||
'numpad_1' => "\N{U+E01B}",
|
||||
'numpad_2' => "\N{U+E01C}",
|
||||
'numpad_3' => "\N{U+E01D}",
|
||||
'numpad_4' => "\N{U+E01E}",
|
||||
'numpad_5' => "\N{U+E01f}",
|
||||
'numpad_6' => "\N{U+E020}",
|
||||
'numpad_7' => "\N{U+E021}",
|
||||
'numpad_8' => "\N{U+E022}",
|
||||
'numpad_9' => "\N{U+E023}",
|
||||
'multiply' => "\N{U+E024}",
|
||||
'add' => "\N{U+E025}",
|
||||
'separator' => "\N{U+E026}",
|
||||
'subtract' => "\N{U+E027}",
|
||||
'decimal' => "\N{U+E028}",
|
||||
'divide' => "\N{U+E029}",
|
||||
'f1' => "\N{U+E031}",
|
||||
'f2' => "\N{U+E032}",
|
||||
'f3' => "\N{U+E033}",
|
||||
'f4' => "\N{U+E034}",
|
||||
'f5' => "\N{U+E035}",
|
||||
'f6' => "\N{U+E036}",
|
||||
'f7' => "\N{U+E037}",
|
||||
'f8' => "\N{U+E038}",
|
||||
'f9' => "\N{U+E039}",
|
||||
'f10' => "\N{U+E03A}",
|
||||
'f11' => "\N{U+E03B}",
|
||||
'f12' => "\N{U+E03C}",
|
||||
'command_meta' => "\N{U+E03D}",
|
||||
'ZenkakuHankaku' => "\N{U+E040}", #Asian language keys, maybe altGr too?
|
||||
#There are other code points for say, left versus right meta/shift/alt etc, but I don't seriously believe anyone uses that level of sophistication on the web yet.
|
||||
};
|
||||
|
||||
our @EXPORT = ('KEYS');
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Remote::WDKeys - Representation of keystrokes used by Selenium::Remote::WebDriver
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
The constant KEYS is defined here.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
883
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/WebElement.pm
Normal file
883
Perl OTRS/Kernel/cpan-lib/Selenium/Remote/WebElement.pm
Normal file
@@ -0,0 +1,883 @@
|
||||
package Selenium::Remote::WebElement;
|
||||
$Selenium::Remote::WebElement::VERSION = '1.33';
|
||||
# ABSTRACT: Representation of an HTML Element used by Selenium Remote Driver
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Moo;
|
||||
use Carp qw(carp croak);
|
||||
|
||||
|
||||
has 'id' => (
|
||||
is => 'ro',
|
||||
required => 1,
|
||||
coerce => sub {
|
||||
my ($value) = @_;
|
||||
if ( ref($value) eq 'HASH' ) {
|
||||
if ( exists $value->{ELEMENT} ) {
|
||||
|
||||
# The JSONWireProtocol web element object looks like
|
||||
#
|
||||
# { "ELEMENT": $INTEGER_ID }
|
||||
return $value->{ELEMENT};
|
||||
}
|
||||
elsif ( exists $value->{'element-6066-11e4-a52e-4f735466cecf'} ) {
|
||||
|
||||
# but the WebDriver spec web element uses a magic
|
||||
# string. See the spec for more information:
|
||||
#
|
||||
# https://www.w3.org/TR/webdriver/#elements
|
||||
return $value->{'element-6066-11e4-a52e-4f735466cecf'};
|
||||
}
|
||||
else {
|
||||
croak
|
||||
'When passing in an object to the WebElement id attribute, it must have at least one of the ELEMENT or element-6066-11e4-a52e-4f735466cecf keys.';
|
||||
}
|
||||
}
|
||||
else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
has 'driver' => (
|
||||
is => 'ro',
|
||||
required => 1,
|
||||
handles => [qw(_execute_command)],
|
||||
);
|
||||
|
||||
|
||||
sub child {
|
||||
return $_[0]->{driver}->find_child_element(@_);
|
||||
}
|
||||
|
||||
sub children {
|
||||
return $_[0]->{driver}->find_child_elements(@_);
|
||||
}
|
||||
|
||||
|
||||
sub click {
|
||||
my ($self) = @_;
|
||||
my $res = { 'command' => 'clickElement', 'id' => $self->id };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub submit {
|
||||
my ($self) = @_;
|
||||
if (
|
||||
$self->driver->{is_wd3}
|
||||
&& !(
|
||||
grep { $self->driver->browser_name eq $_ } qw{chrome MicrosoftEdge}
|
||||
)
|
||||
)
|
||||
{
|
||||
if ( $self->get_tag_name() ne 'form' ) {
|
||||
return $self->driver->execute_script(
|
||||
"return arguments[0].form.submit();",
|
||||
{ 'element-6066-11e4-a52e-4f735466cecf' => $self->{id} } );
|
||||
}
|
||||
else {
|
||||
return $self->driver->execute_script(
|
||||
"return arguments[0].submit();",
|
||||
{ 'element-6066-11e4-a52e-4f735466cecf' => $self->{id} } );
|
||||
}
|
||||
}
|
||||
my $res = { 'command' => 'submitElement', 'id' => $self->id };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub send_keys {
|
||||
my ( $self, @strings ) = @_;
|
||||
croak "no keys to send" unless scalar @strings >= 1;
|
||||
my $res = { 'command' => 'sendKeysToElement', 'id' => $self->id };
|
||||
|
||||
# We need to send an array of single characters to be WebDriver
|
||||
# spec compatible. That is, for @strings = ('hel', 'lo'), the
|
||||
# corresponding value must be ('h', 'e', 'l', 'l', 'o' ). This
|
||||
# format conforms with the Spec AND works with the Selenium
|
||||
# standalone server.
|
||||
my $strings = join( '', map { $_ . "" } @strings );
|
||||
my $params = {
|
||||
'value' => [ split( '', $strings ) ],
|
||||
text => $strings,
|
||||
};
|
||||
return $self->_execute_command( $res, $params );
|
||||
}
|
||||
|
||||
|
||||
sub is_selected {
|
||||
my ($self) = @_;
|
||||
|
||||
return $self->get_property('checked')
|
||||
if $self->driver->{is_wd3}
|
||||
&& !( grep { $self->driver->browser_name eq $_ }
|
||||
qw{chrome MicrosoftEdge} );
|
||||
my $res = { 'command' => 'isElementSelected', 'id' => $self->id };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub set_selected {
|
||||
my ($self) = @_;
|
||||
if ( $self->driver->{is_wd3} ) {
|
||||
return if $self->is_selected();
|
||||
return $self->click();
|
||||
}
|
||||
my $res = { 'command' => 'setElementSelected', 'id' => $self->id };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub toggle {
|
||||
my ($self) = @_;
|
||||
if ( $self->driver->{is_wd3} ) {
|
||||
return $self->click() unless $self->is_selected();
|
||||
return $self->driver->execute_script(
|
||||
qq/ if (arguments[0].checked) { arguments[0].checked = 0 }; return arguments[0].checked; /,
|
||||
{ 'element-6066-11e4-a52e-4f735466cecf' => $self->{id} }
|
||||
);
|
||||
}
|
||||
my $res = { 'command' => 'toggleElement', 'id' => $self->id };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub is_enabled {
|
||||
my ($self) = @_;
|
||||
if (
|
||||
$self->driver->{is_wd3}
|
||||
&& !(
|
||||
grep { $self->driver->browser_name eq $_ } qw{chrome MicrosoftEdge}
|
||||
)
|
||||
)
|
||||
{
|
||||
return 1 if $self->get_tag_name() ne 'input';
|
||||
return $self->get_property('disabled') ? 0 : 1;
|
||||
}
|
||||
my $res = { 'command' => 'isElementEnabled', 'id' => $self->id };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub get_element_location {
|
||||
my ($self) = @_;
|
||||
if (
|
||||
$self->driver->{is_wd3}
|
||||
&& !(
|
||||
grep { $self->driver->browser_name eq $_ } qw{chrome MicrosoftEdge}
|
||||
)
|
||||
)
|
||||
{
|
||||
my $data = $self->get_element_rect();
|
||||
delete $data->{height};
|
||||
delete $data->{width};
|
||||
return $data;
|
||||
}
|
||||
my $res = { 'command' => 'getElementLocation', 'id' => $self->id };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub get_size {
|
||||
my ($self) = @_;
|
||||
if (
|
||||
$self->driver->{is_wd3}
|
||||
&& !(
|
||||
grep { $self->driver->browser_name eq $_ } qw{chrome MicrosoftEdge}
|
||||
)
|
||||
)
|
||||
{
|
||||
my $data = $self->get_element_rect();
|
||||
delete $data->{x};
|
||||
delete $data->{y};
|
||||
return $data;
|
||||
}
|
||||
my $res = { 'command' => 'getElementSize', 'id' => $self->id };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub get_element_rect {
|
||||
my ($self) = @_;
|
||||
my $res = { 'command' => 'getElementRect', 'id' => $self->id };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub get_element_location_in_view {
|
||||
my ($self) = @_;
|
||||
|
||||
#XXX chrome is dopey here
|
||||
return $self->driver->execute_script(
|
||||
qq{
|
||||
if (typeof(arguments[0]) !== 'undefined' && arguments[0].nodeType === Node.ELEMENT_NODE) {
|
||||
arguments[0].scrollIntoView();
|
||||
var pos = arguments[0].getBoundingClientRect();
|
||||
return {y:pos.top,x:pos.left};
|
||||
}
|
||||
return {};
|
||||
}, { 'element-6066-11e4-a52e-4f735466cecf' => $self->{id} }
|
||||
)
|
||||
if $self->driver->{is_wd3} && grep { $self->driver->browser_name eq $_ }
|
||||
( 'firefox', 'internet explorer' );
|
||||
my $res = { 'command' => 'getElementLocationInView', 'id' => $self->id };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub get_tag_name {
|
||||
my ($self) = @_;
|
||||
my $res = { 'command' => 'getElementTagName', 'id' => $self->id };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub clear {
|
||||
my ($self) = @_;
|
||||
my $res = { 'command' => 'clearElement', 'id' => $self->id };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub get_attribute {
|
||||
my ( $self, $attr_name, $no_i_really_mean_it ) = @_;
|
||||
if ( not defined $attr_name ) {
|
||||
croak 'Attribute name not provided';
|
||||
}
|
||||
|
||||
#Handle global JSONWire emulation flag
|
||||
$no_i_really_mean_it = 1 unless $self->{driver}->{emulate_jsonwire};
|
||||
|
||||
return $self->get_property($attr_name)
|
||||
if $self->driver->{is_wd3}
|
||||
&& !( grep { $self->driver->browser_name eq $_ }
|
||||
qw{chrome MicrosoftEdge} )
|
||||
&& !$no_i_really_mean_it;
|
||||
|
||||
my $res = {
|
||||
'command' => 'getElementAttribute',
|
||||
'id' => $self->id,
|
||||
'name' => $attr_name,
|
||||
};
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub get_property {
|
||||
my ( $self, $prop ) = @_;
|
||||
return $self->get_attribute($prop)
|
||||
if $self->driver->{is_wd3}
|
||||
&& ( grep { $self->driver->browser_name eq $_ }
|
||||
qw{chrome MicrosoftEdge} );
|
||||
my $res =
|
||||
{ 'command' => 'getElementProperty', id => $self->id, name => $prop };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub get_value {
|
||||
my ($self) = @_;
|
||||
return $self->get_attribute('value');
|
||||
}
|
||||
|
||||
|
||||
sub is_displayed {
|
||||
my ($self) = @_;
|
||||
if (
|
||||
$self->driver->{is_wd3}
|
||||
&& !(
|
||||
grep { $self->driver->browser_name eq $_ } qw{chrome MicrosoftEdge}
|
||||
)
|
||||
)
|
||||
{
|
||||
return 0
|
||||
if $self->get_tag_name() eq 'input'
|
||||
&& $self->get_property('type') eq 'hidden'; #hidden type inputs
|
||||
return 0 unless $self->_is_in_viewport();
|
||||
return int( $self->get_css_attribute('display') ne 'none' );
|
||||
}
|
||||
my $res = { 'command' => 'isElementDisplayed', 'id' => $self->id };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
sub _is_in_viewport {
|
||||
my ($self) = @_;
|
||||
return $self->driver->execute_script(
|
||||
qq{
|
||||
var rect = arguments[0].getBoundingClientRect();
|
||||
return (
|
||||
rect.top >= 0 &&
|
||||
rect.left >= 0 &&
|
||||
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
||||
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
|
||||
);
|
||||
}, { 'element-6066-11e4-a52e-4f735466cecf' => $self->{id} }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
sub is_hidden {
|
||||
my ($self) = @_;
|
||||
return !$self->is_displayed();
|
||||
}
|
||||
|
||||
|
||||
sub drag {
|
||||
my ( $self, $target ) = @_;
|
||||
require Selenium::ActionChains;
|
||||
my $chain = Selenium::ActionChains->new( driver => $self->driver );
|
||||
return $chain->drag_and_drop( $self, $target )->perform();
|
||||
}
|
||||
|
||||
|
||||
sub get_text {
|
||||
my ($self) = @_;
|
||||
my $res = { 'command' => 'getElementText', 'id' => $self->id };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub get_css_attribute {
|
||||
my ( $self, $attr_name ) = @_;
|
||||
if ( not defined $attr_name ) {
|
||||
croak 'CSS attribute name not provided';
|
||||
}
|
||||
my $res = {
|
||||
'command' => 'getElementValueOfCssProperty',
|
||||
'id' => $self->id,
|
||||
'property_name' => $attr_name,
|
||||
};
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub describe {
|
||||
my ($self) = @_;
|
||||
my $res = { 'command' => 'describeElement', 'id' => $self->id };
|
||||
return $self->_execute_command($res);
|
||||
}
|
||||
|
||||
|
||||
sub screenshot {
|
||||
my ( $self, $scroll ) = @_;
|
||||
$scroll //= 1;
|
||||
my $res = { 'command' => 'elementScreenshot', id => $self->id };
|
||||
my $input = { scroll => int($scroll) };
|
||||
return $self->_execute_command( $res, $input );
|
||||
}
|
||||
|
||||
|
||||
sub capture_screenshot {
|
||||
my ( $self, $filename, $scroll ) = @_;
|
||||
croak '$filename is required' unless $filename;
|
||||
|
||||
open( my $fh, '>', $filename );
|
||||
binmode $fh;
|
||||
print $fh MIME::Base64::decode_base64( $self->screenshot($scroll) );
|
||||
CORE::close $fh;
|
||||
return 1;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Remote::WebElement - Representation of an HTML Element used by Selenium Remote Driver
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Selenium Webdriver represents all the HTML elements as WebElements.
|
||||
This module provides a mechanism to represent them as objects &
|
||||
perform various actions on the related elements. This module should
|
||||
not be instantiated directly by the end user. Selenium::Remote::Driver
|
||||
instantiates this module when required. Typically, the find_element
|
||||
method in Selenium::Remote::Driver returns this object on which
|
||||
various element related operations can be carried out.
|
||||
|
||||
What is probably most useful on this page is the list of methods below
|
||||
that you can perform on an element once you've found one and S::R::D
|
||||
has made an instance of this for you.
|
||||
|
||||
=head1 CONSTRUCTOR
|
||||
|
||||
=head2 new
|
||||
|
||||
=over 4
|
||||
|
||||
=item B<id>
|
||||
|
||||
Required: Pass in a string representing the ID of the object. The
|
||||
string should be obtained from the response object of making one of
|
||||
the C<find_element> calls from L<Selenium::Remote::Driver>.
|
||||
|
||||
The attribute is also set up to handle spec compliant element response
|
||||
objects via its `coerce` such that any of the following will work and
|
||||
are all equivalent:
|
||||
|
||||
my $old_elem = Selenium::Remote::WebElement->new(
|
||||
id => 1,
|
||||
driver => $driver
|
||||
);
|
||||
|
||||
my $new_remote_elem = Selenium::Remote::WebElement->new(
|
||||
id => { ELEMENT => 1 },
|
||||
driver => $driver
|
||||
);
|
||||
|
||||
my $new_spec_elem = Selenium::Remote::WebElement->new(
|
||||
id => { 'element-6066-11e4-a52e-4f735466cecf' => 1 },
|
||||
driver => $driver
|
||||
);
|
||||
|
||||
and then after instantiation, all three would give the following for
|
||||
`id`:
|
||||
|
||||
print $elem->id; # prints 1
|
||||
|
||||
=item B<driver>
|
||||
|
||||
Required: Pass in a Selenium::Remote::Driver instance or one of its
|
||||
subclasses. The WebElement needs the appropriate Driver session to
|
||||
execute its commands properly.
|
||||
|
||||
=back
|
||||
|
||||
For typical usage of S::R::D and this module, none of this
|
||||
matters and it should Just Work without you having to worry about it
|
||||
at all. For further reading, the L<W3C
|
||||
spec|https://www.w3.org/TR/webdriver/#elements> strictly dictates the
|
||||
exact behavior.
|
||||
|
||||
=head1 FUNCTIONS
|
||||
|
||||
=head2 child(selector, method)
|
||||
|
||||
=head2 children(selector, method)
|
||||
|
||||
Alias to Selenium::Remote::Driver::find_child_element and find_child_elements, respectively.
|
||||
|
||||
=head2 click
|
||||
|
||||
Description:
|
||||
Click the element.
|
||||
|
||||
Usage:
|
||||
$elem->click();
|
||||
|
||||
=head2 submit
|
||||
|
||||
Description:
|
||||
Submit a FORM element. The submit command may also be applied to any element
|
||||
that is a descendant of a FORM element.
|
||||
|
||||
Compatibility:
|
||||
On webdriver3 enabled servers, this uses a JS shim, which WILL NOT submit correctly unless your element is an <input>.
|
||||
Try clicking it if possible instead.
|
||||
|
||||
Usage:
|
||||
$elem->submit();
|
||||
|
||||
=head2 send_keys
|
||||
|
||||
Description:
|
||||
Send a sequence of key strokes to an element. If you want to send specific
|
||||
Keyboard events, then use the WDKeys module along with theis method. See e.g.
|
||||
for reference
|
||||
|
||||
Input: 1
|
||||
Required:
|
||||
{ARRAY | STRING} - Array of strings or a string.
|
||||
|
||||
Usage:
|
||||
$elem->send_keys('abcd', 'efg');
|
||||
$elem->send_keys('hijk');
|
||||
|
||||
or
|
||||
|
||||
# include the WDKeys module
|
||||
use Selenium::Remote::WDKeys;
|
||||
.
|
||||
.
|
||||
$elem->send_keys(KEYS->{'space'}, KEYS->{'enter'});
|
||||
|
||||
=head2 is_selected
|
||||
|
||||
Description:
|
||||
Determine if an OPTION element, or an INPUT element of type checkbox or
|
||||
radiobutton is currently selected.
|
||||
|
||||
Output:
|
||||
BOOLEAN - whether the element is selected
|
||||
|
||||
Usage:
|
||||
$elem->is_selected();
|
||||
|
||||
=head2 set_selected
|
||||
|
||||
Description:
|
||||
Select an OPTION element, or an INPUT element of type checkbox or radiobutton.
|
||||
Forces selected=1 on the element..
|
||||
|
||||
Usage:
|
||||
$elem->set_selected();
|
||||
|
||||
=head2 toggle
|
||||
|
||||
Description:
|
||||
Toggle whether an OPTION element, or an INPUT element of type checkbox or
|
||||
radiobutton is currently selected.
|
||||
|
||||
Output:
|
||||
BOOLEAN - Whether the element is selected after toggling its state.
|
||||
|
||||
Usage:
|
||||
$elem->toggle();
|
||||
|
||||
=head2 is_enabled
|
||||
|
||||
Description:
|
||||
Determine if an element is currently enabled.
|
||||
|
||||
Output:
|
||||
BOOLEAN - Whether the element is enabled.
|
||||
|
||||
Usage:
|
||||
$elem->is_enabled();
|
||||
|
||||
=head2 get_element_location
|
||||
|
||||
Description:
|
||||
Determine an element's location on the page. The point (0, 0) refers to the
|
||||
upper-left corner of the page.
|
||||
|
||||
Compatibility:
|
||||
On WebDriver 3 enabled servers, this is an alias for get_element_rect().
|
||||
|
||||
Output:
|
||||
HASH - The X and Y coordinates for the element on the page.
|
||||
|
||||
Usage:
|
||||
$elem->get_element_location();
|
||||
|
||||
This method is DEPRECATED on webdriver3 enabled servers.
|
||||
|
||||
=head2 get_size
|
||||
|
||||
Description:
|
||||
Determine an element's size in pixels. The size will be returned with width
|
||||
and height properties.
|
||||
|
||||
Compatibility:
|
||||
On WebDriver 3 enabled servers, this is an alias for get_element_rect().
|
||||
|
||||
Output:
|
||||
HASH - The width and height of the element, in pixels.
|
||||
|
||||
Usage:
|
||||
$elem->get_size();
|
||||
|
||||
This method is DEPRECATED on webdriver3 enabled servers.
|
||||
|
||||
=head2 get_element_rect
|
||||
|
||||
Get the element's size AND location in a hash.
|
||||
|
||||
Example Output:
|
||||
|
||||
{ x => 0, y => 0, height => 10, width => 10 }
|
||||
|
||||
=head2 get_element_location_in_view
|
||||
|
||||
Description:
|
||||
Determine an element's location on the screen once it has been scrolled
|
||||
into view.
|
||||
|
||||
Note: This is considered an internal command and should only be used to
|
||||
determine an element's location for correctly generating native events.
|
||||
|
||||
Compatibility:
|
||||
On Webdriver3 servers, we have to implement this with a JS shim.
|
||||
This means in some contexts, you won't get any position returned, as the element isn't considered an element internally.
|
||||
You may have to go up the element stack to find the element that actually has the bounding box.
|
||||
|
||||
Output:
|
||||
{x:number, y:number} The X and Y coordinates for the element on the page.
|
||||
|
||||
Usage:
|
||||
$elem->get_element_location_in_view();
|
||||
|
||||
=head2 get_tag_name
|
||||
|
||||
Description:
|
||||
Query for an element's tag name.
|
||||
|
||||
Output:
|
||||
STRING - The element's tag name, as a lowercase string.
|
||||
|
||||
Usage:
|
||||
$elem->get_tag_name();
|
||||
|
||||
=head2 clear
|
||||
|
||||
Description:
|
||||
Clear a TEXTAREA or text INPUT element's value.
|
||||
|
||||
Usage:
|
||||
$elem->clear();
|
||||
|
||||
=head2 get_attribute
|
||||
|
||||
Description:
|
||||
Get the value of an element's attribute.
|
||||
|
||||
Compatibility:
|
||||
In older webDriver, this actually got the value of an element's property.
|
||||
If you want to get the initial condition (e.g. the values in the tag hardcoded in HTML), pass 1 as the second argument.
|
||||
|
||||
Or, set $driver->{emulate_jsonwire} = 0 to not have to pass the extra arg.
|
||||
|
||||
This can only done on WebDriver 3 enabled servers.
|
||||
|
||||
Input: 2
|
||||
Required:
|
||||
STRING - name of the attribute of the element
|
||||
Optional:
|
||||
BOOLEAN - "I really mean that I want the initial condition, quit being so compatible!!!"
|
||||
|
||||
|
||||
Output:
|
||||
{STRING | NULL} The value of the attribute, or null if it is not set on the element.
|
||||
|
||||
Usage:
|
||||
$elem->get_attribute('name',1);
|
||||
|
||||
=head2 get_property
|
||||
|
||||
Gets the C<Current Value> of an element's attribute.
|
||||
|
||||
Takes a named property as an argument.
|
||||
|
||||
Only available on WebDriver 3 enabled servers.
|
||||
|
||||
=head2 get_value
|
||||
|
||||
Description:
|
||||
Query for the value of an element, as determined by its value attribute.
|
||||
|
||||
Output:
|
||||
{STRING | NULL} The element's value, or null if it doesn't have a value attribute.
|
||||
|
||||
Usage:
|
||||
$elem->get_value();
|
||||
|
||||
=head2 is_displayed
|
||||
|
||||
Description:
|
||||
Determine if an element is currently displayed.
|
||||
Note: This does *not* tell you an element's 'visibility' property; as it still takes up space in the DOM and is therefore considered 'displayed'.
|
||||
|
||||
WC3 Compatibility:
|
||||
On JSONWire this method really only checked to see whether the element's style was display:none, or whether it was a hidden input.
|
||||
This is because "displayedness" was pretty loosely defined until fairly late on into the process, and much grief resulted.
|
||||
In WC3 webdriver, it additionally does a viewport check, to account for the firmer definition of "displayedness":
|
||||
https://w3c.github.io/webdriver/#element-displayedness
|
||||
|
||||
Output:
|
||||
BOOLEAN - Whether the element is displayed.
|
||||
|
||||
Usage:
|
||||
$elem->is_displayed();
|
||||
|
||||
=head2 is_hidden
|
||||
|
||||
Description:
|
||||
Determine if an element is currently hidden.
|
||||
|
||||
Output:
|
||||
BOOLEAN - Whether the element is hidden.
|
||||
|
||||
Usage:
|
||||
$elem->is_hidden();
|
||||
|
||||
=head2 drag
|
||||
|
||||
Alias for Selenium::ActionChains::drag_and_drop().
|
||||
|
||||
Provide element you wish to drag to as argument.
|
||||
|
||||
my $target = $driver->find_element('receptacle','id');
|
||||
my $subject = $driver->find_element('thingy','id');
|
||||
$subject->drag($target);
|
||||
|
||||
=head2 get_text
|
||||
|
||||
Description:
|
||||
Get the innerText of the element.
|
||||
|
||||
Output:
|
||||
STRING - innerText of an element
|
||||
|
||||
Usage:
|
||||
$elem->get_text();
|
||||
|
||||
=head2 get_css_attribute
|
||||
|
||||
Description:
|
||||
Query the value of an element's computed CSS property. The CSS property to
|
||||
query should be specified using the CSS property name, not the JavaScript
|
||||
property name (e.g. background-color instead of backgroundColor).
|
||||
|
||||
Input: 1
|
||||
Required:
|
||||
STRING - name of the css-attribute
|
||||
|
||||
Output:
|
||||
STRING - Value of the css attribute
|
||||
|
||||
Usage:
|
||||
$elem->get_css_attribute('background-color');
|
||||
|
||||
=head2 describe
|
||||
|
||||
Description:
|
||||
Describe the identified element
|
||||
|
||||
Usage:
|
||||
$elem->describe();
|
||||
|
||||
Note: DEPRECATED as of 2.42.2 -- use get_text, get_value, is_displayed, or
|
||||
whatever appropriate WebElement function you need instead
|
||||
|
||||
Entirely unsupported on WebDriver 3 enabled servers.
|
||||
|
||||
=head2 screenshot
|
||||
|
||||
Description:
|
||||
Get a screenshot of the visible region that is a subset of the element's bounding box as a base64 encoded image.
|
||||
|
||||
Compatibility:
|
||||
Only available on Webdriver3 enabled selenium servers.
|
||||
|
||||
Input (optional):
|
||||
$scroll_into_view - BOOLEAN default true. If false, will not scroll the element into the viewport first.
|
||||
Failing to do so may result in an image being cropped partially or entirely.
|
||||
|
||||
Output:
|
||||
STRING - base64 encoded image
|
||||
|
||||
Usage:
|
||||
print $element->screenshot();
|
||||
|
||||
To conveniently write the screenshot to a file, see L</capture_screenshot>.
|
||||
|
||||
=head2 capture_screenshot
|
||||
|
||||
Description:
|
||||
Capture a screenshot of said element and save as a PNG to provided file name.
|
||||
|
||||
Compatibility:
|
||||
Only available on Webdriver3 enabled selenium servers.
|
||||
|
||||
Input (optional):
|
||||
$scroll_into_view - BOOLEAN default true. If false, will not scroll the element into the viewport first.
|
||||
Failing to do so may result in an image being cropped partially or entirely.
|
||||
|
||||
Output:
|
||||
TRUE - (Screenshot is written to file)
|
||||
|
||||
Usage:
|
||||
$element->capture_screenshot($filename);
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
213
Perl OTRS/Kernel/cpan-lib/Selenium/Waiter.pm
Normal file
213
Perl OTRS/Kernel/cpan-lib/Selenium/Waiter.pm
Normal file
@@ -0,0 +1,213 @@
|
||||
package Selenium::Waiter;
|
||||
$Selenium::Waiter::VERSION = '1.33';
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# ABSTRACT: Provides a utility wait_until function
|
||||
use Try::Tiny;
|
||||
require Exporter;
|
||||
our @ISA = qw/Exporter/;
|
||||
our @EXPORT = qw/wait_until/;
|
||||
|
||||
|
||||
sub wait_until (&%) {
|
||||
my $assert = shift;
|
||||
my $args = {
|
||||
timeout => 30,
|
||||
interval => 1,
|
||||
debug => 0,
|
||||
@_
|
||||
};
|
||||
|
||||
my $start = time;
|
||||
my $timeout_not_elapsed = sub {
|
||||
my $elapsed = time - $start;
|
||||
return $elapsed < $args->{timeout};
|
||||
};
|
||||
|
||||
my $exception = '';
|
||||
while ( $timeout_not_elapsed->() ) {
|
||||
my $assert_ret;
|
||||
my $try_ret = try {
|
||||
$assert_ret = $assert->();
|
||||
return $assert_ret if $assert_ret;
|
||||
}
|
||||
catch {
|
||||
$exception = $_;
|
||||
warn $_ if $args->{debug};
|
||||
return '';
|
||||
}
|
||||
finally {
|
||||
if ( !$assert_ret ) {
|
||||
sleep( $args->{interval} );
|
||||
}
|
||||
};
|
||||
|
||||
return $try_ret if $try_ret;
|
||||
}
|
||||
|
||||
# No need to repeat ourselves if we're already debugging.
|
||||
warn $exception if $exception && !$args->{debug};
|
||||
return '';
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=pod
|
||||
|
||||
=encoding UTF-8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Selenium::Waiter - Provides a utility wait_until function
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
version 1.33
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Selenium::Waiter qw/wait_until/;
|
||||
my $d = Selenium::Remote::Driver->new;
|
||||
|
||||
my $div = wait_until { $d->find_element('div', 'css') };
|
||||
|
||||
=head1 FUNCTIONS
|
||||
|
||||
=head2 wait_until
|
||||
|
||||
Exported by default, it takes a BLOCK (required) and optionally a
|
||||
hash of configuration params. It uses a prototype to take its
|
||||
arguments, so usage looks look like:
|
||||
|
||||
use Selenium::Waiter;
|
||||
my $div = wait_until { $driver->find_element('div', 'css') };
|
||||
|
||||
The above snippet will search for C<css=div> for thirty seconds; if it
|
||||
ever finds the element, it will immediately return. More generally,
|
||||
Once the BLOCK returns anything truthy, the C<wait_until> will stop
|
||||
evaluating and the return of the BLOCK will be returned to you. If the
|
||||
BLOCK never returns a truthy value, we'll wait until the elapsed time
|
||||
has increased past the timeout and then return an empty string C<''>.
|
||||
|
||||
B<Achtung!> Please make sure that the BLOCK you pass in can be
|
||||
executed in a timely fashion. For Webdriver, that means that you
|
||||
should set the appropriate C<implicit_wait> timeout low (a second or
|
||||
less!) so that we can rerun the assert sub repeatedly. We don't do
|
||||
anything fancy behind the scenes: we just execute the BLOCK you pass
|
||||
in and sleep between iterations. If your BLOCK actively blocks for
|
||||
thirty seconds, like a C<find_element> would do with an
|
||||
C<implicit_wait> of 30 seconds, we won't be able to help you at all -
|
||||
that blocking behavior is on the webdriver server side, and is out of
|
||||
our control. We'd run one iteration, get blocked for thirty seconds,
|
||||
and return control to you at that point.
|
||||
|
||||
=head4 Dying
|
||||
|
||||
PLEASE check the return value before proceeding, as we unwisely
|
||||
suppress any attempts your BLOCK may make to die or croak. The BLOCK
|
||||
you pass is called in a L<Try::Tiny/try>, and if any of the
|
||||
invocations of your function throw and the BLOCK never becomes true,
|
||||
we'll carp exactly once at the end immediately before returning
|
||||
false. We overwrite the death message from each iteration, so at the
|
||||
end, you'll only see the most recent death message.
|
||||
|
||||
# warns once after thirty seconds: "kept from dying";
|
||||
wait_until { die 'kept from dying' };
|
||||
|
||||
The output of C<die>s from each iteration can be exposed if you wish
|
||||
to see the massacre:
|
||||
|
||||
# carps: "kept from dying" once a second for thirty seconds
|
||||
wait_until { die 'kept from dying' } debug => 1;
|
||||
|
||||
=head4 Timeouts and Intervals
|
||||
|
||||
You can also customize the timeout, and/or the retry interval between
|
||||
iterations.
|
||||
|
||||
# prints hi three four times at 0, 3, 6, and 9 seconds
|
||||
wait_until { print 'hi'; '' } timeout => 10, interval => 3;
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Please see those modules/websites for more information related to this module.
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
|
||||
|
||||
=back
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Please report any bugs or feature requests on the bugtracker website
|
||||
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
|
||||
|
||||
When submitting a bug or request, please include a test-file or a
|
||||
patch to an existing test-file that illustrates the bug or desired
|
||||
feature.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Current Maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Daniel Gempesaw <gempesaw@gmail.com>
|
||||
|
||||
=item *
|
||||
|
||||
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
Previous maintainers:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Luke Closs <cpan@5thplane.com>
|
||||
|
||||
=item *
|
||||
|
||||
Mark Stosberg <mark@stosberg.com>
|
||||
|
||||
=back
|
||||
|
||||
Original authors:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
Aditya Ivaturi <ivaturi@gmail.com>
|
||||
|
||||
=back
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
|
||||
|
||||
Copyright (c) 2014-2017 Daniel Gempesaw
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
=cut
|
||||
Reference in New Issue
Block a user