Parcourir la source

Merge branch 'master' into action-chains-experiment

Emmanuel Peroumalnaik il y a 10 ans
Parent
commit
f362903503
59 fichiers modifiés avec 2760 ajouts et 689 suppressions
  1. 3 0
      .gitignore
  2. 34 0
      Changes
  3. 61 0
      INSTALL.md
  4. 49 67
      README.md
  5. 12 6
      cpanfile
  6. 5 7
      dist.ini
  7. 344 0
      lib/Selenium/CanStartBinary.pm
  8. 70 0
      lib/Selenium/CanStartBinary/FindBinary.pm
  9. 35 0
      lib/Selenium/CanStartBinary/ProbePort.pm
  10. 82 0
      lib/Selenium/Chrome.pm
  11. 84 0
      lib/Selenium/Firefox.pm
  12. 79 0
      lib/Selenium/Firefox/Binary.pm
  13. 286 0
      lib/Selenium/Firefox/Profile.pm
  14. BIN
      lib/Selenium/Firefox/amd64/libibushandler.so
  15. BIN
      lib/Selenium/Firefox/amd64/x_ignore_nofocus.so
  16. BIN
      lib/Selenium/Firefox/webdriver.xpi
  17. 68 0
      lib/Selenium/Firefox/webdriver_prefs.json
  18. BIN
      lib/Selenium/Firefox/x86/libibushandler.so
  19. BIN
      lib/Selenium/Firefox/x86/x_ignore_nofocus.so
  20. 23 0
      lib/Selenium/InternetExplorer.pm
  21. 95 0
      lib/Selenium/PhantomJS.pm
  22. 37 2
      lib/Selenium/Remote/Commands.pm
  23. 320 25
      lib/Selenium/Remote/Driver.pm
  24. 8 236
      lib/Selenium/Remote/Driver/Firefox/Profile.pm
  25. 36 0
      lib/Selenium/Remote/Finders.pm
  26. 8 6
      lib/Selenium/Remote/Mock/RemoteConnection.pm
  27. 111 0
      lib/Selenium/Waiter.pm
  28. 158 120
      lib/Test/Selenium/Remote/Driver.pm
  29. 44 1
      lib/Test/Selenium/Remote/Role/DoesTesting.pm
  30. 92 7
      t/01-driver.t
  31. 3 1
      t/02-webelement.t
  32. 6 8
      t/03-spec-coverage.t
  33. 89 0
      t/CanStartBinary.t
  34. 50 0
      t/Finders.t
  35. 16 15
      t/Firefox-Profile.t
  36. 3 2
      t/Remote-Connection.t
  37. 116 9
      t/Test-Selenium-Remote-Driver.t
  38. 48 0
      t/Waiter.t
  39. 39 0
      t/convenience.t
  40. 26 28
      t/error.t
  41. 25 0
      t/lib/TestHarness.pm
  42. 19 20
      t/mock-recordings/01-driver-mock-MSWin32.json
  43. 3 4
      t/mock-recordings/01-driver-mock-darwin.json
  44. 6 9
      t/mock-recordings/01-driver-mock-linux.json
  45. 2 3
      t/mock-recordings/02-webelement-mock-darwin.json
  46. 13 18
      t/mock-recordings/02-webelement-mock-linux.json
  47. 28 28
      t/mock-recordings/10-switch-to-window-mock-darwin.json
  48. 28 28
      t/mock-recordings/10-switch-to-window-mock-linux.json
  49. 20 0
      t/mock-recordings/convenience-mock-MSWin32.json
  50. 14 0
      t/mock-recordings/convenience-mock-darwin.json
  51. 14 0
      t/mock-recordings/convenience-mock-linux.json
  52. 8 0
      t/mock-recordings/finders-mock-MSWin32.json
  53. 2 0
      t/mock-recordings/finders-mock-darwin.json
  54. 2 0
      t/mock-recordings/finders-mock-linux.json
  55. 6 6
      t/mock-recordings/firefox-profile-mock-darwin.json
  56. 6 9
      t/mock-recordings/firefox-profile-mock-linux.json
  57. 12 12
      t/mock-recordings/test-selenium-remote-driver-google-mock-darwin.json
  58. 12 12
      t/mock-recordings/test-selenium-remote-driver-google-mock-linux.json
  59. 0 0
      t/uploadTest

+ 3 - 0
.gitignore

@@ -10,3 +10,6 @@ cover_db
 Selenium-Remote-Driver-*
 Selenium-Remote-Driver-*
 .prove
 .prove
 _prove
 _prove
+#vim swapfiles
+*.swo
+*.swp

+ 34 - 0
Changes

@@ -1,5 +1,39 @@
 Revision history for Selenium-Remote-Driver
 Revision history for Selenium-Remote-Driver
 
 
+0.25  03-24-2015
+        (selenium version: 2.45.0)
+
+        [NEW FEATURES]
+        - #189 Start webdrivers directly, removing the need for the JRE. See
+           Selenium::Chrome, Selenium::Firefox, Selenium::PhantomJS.
+
+        [BUG FIXES]
+        - #194, #195: @teodesian, @jamadam: fix undocumented upload file behavior
+
+0.24    02-18-2015
+        [NEW FEATURES]
+        - #182 Non-crashing warn-instead-of-die parameter finders: find_element_by_class, etc
+        - #183 Convenience packages for starting up browsers: Selenium::Chrome, Selenium::Firefox, etc
+        - Provide wait_until utility functionality in Selenium::Waiter
+
+        [BUG FIXES]
+        - #184 Fix bug about reverting default finder in T:S:R:D
+
+0.23   01-25-2014
+        [NEW FEATURES]
+        - #178 Fix upload_file's return value & add test suite
+        - #179 Add optional middle finder-strategy argument to T:S:R:D's find_element-like functions
+        - #174 Add new endpoints: cache status, geolocation, log types, orientation, etc
+
+        [BUG FIXES]
+        - #175 Fix find_elements
+        - #180 Stop overwriting body_text_unlike test descriptions
+        - #141 Explicitly cast height and weight to integers for set_window_size
+
+0.2203 01-18-2014
+        [BUG FIXES]
+        - #175 Fix find_elements_ok
+
 0.2202 11-26-2014
 0.2202 11-26-2014
         [BUG FIXES]
         [BUG FIXES]
         - Generalize error message for invalid finder strategies
         - Generalize error message for invalid finder strategies

+ 61 - 0
INSTALL.md

@@ -0,0 +1,61 @@
+## Installation
+
+It's probably easiest to use the `cpanm` or `CPAN` commands:
+
+```bash
+$ cpanm Selenium::Remote::Driver
+```
+
+If you want to install from this repository, you have a few options;
+see the [installation docs][] for more details.
+
+[installation docs]: /install.md
+
+### With Dist::Zilla
+
+If you have Dist::Zilla, it's straightforward:
+
+```bash
+$ dzil listdeps --missing | cpanm
+$ dzil install
+```
+
+### Without Dist::Zilla
+
+We maintain two branches that have `Makefile.PL`:
+[`cpan`][cpan-branch] and [`build/master`][bm-branch]. The `cpan`
+branch is only updated every time we release to the CPAN, and it is
+not kept up to date with master. The `build/master` branch is an
+up-to-date copy of the latest changes in master, and will usually
+contain changes that have not made it to a CPAN release yet.
+
+To get either of these, you can use the following, (replacing
+"build/master" with "cpan" if desired):
+
+```bash
+$ cpanm -v git://github.com/gempesaw/Selenium-Remote-Driver.git@build/master
+```
+
+Or, without `cpanm` and/or without the `git://` protocol:
+
+```bash
+$ git clone https://github.com/gempesaw/Selenium-Remote-Driver --branch build/master --single-branch --depth 1
+$ cd Selenium-Remote-Driver
+$ perl Makefile.PL
+```
+
+Note that due to POD::Weaver, the line numbers between these generated
+branches and the master branch are unfortunately completely
+incompatible.
+
+[cpan-branch]: https://github.com/gempesaw/Selenium-Remote-Driver/tree/cpan
+[bm-branch]: https://github.com/gempesaw/Selenium-Remote-Driver/tree/build/master
+
+### Viewing dependencies
+
+You can also use `cpanm` to help you with dependencies after you've
+cloned the repository:
+
+```bash
+$ cpanm --showdeps .
+```

+ 49 - 67
README.md

@@ -20,72 +20,31 @@ already downloaded and started the
 
 
 ## Installation
 ## Installation
 
 
-It's probably easiest to use cpanm:
+It's probably easiest to use the `cpanm` or `CPAN` commands:
 
 
 ```bash
 ```bash
 $ cpanm Selenium::Remote::Driver
 $ cpanm Selenium::Remote::Driver
 ```
 ```
 
 
-If you want to install from this repository, you have a few options:
+If you want to install from this repository, you have a few options;
+see the [installation docs][] for more details.
 
 
-### With Dist::Zilla
-
-If you have Dist::Zilla, it's straightforward:
-
-```bash
-$ dzil listdeps --missing | cpanm
-$ dzil install
-```
-
-### Without Dist::Zilla
-
-We maintain two branches that have `Makefile.PL`:
-[`cpan`][cpan-branch] and [`build/master`][bm-branch]. The `cpan`
-branch is only updated every time we release to the CPAN, and it is
-not kept up to date with master. The `build/master` branch is an
-up-to-date copy of the latest changes in master, and will usually
-contain changes that have not made it to a CPAN release yet.
-
-To get either of these, you can use the following, (replacing
-"build/master" with "cpan" if desired):
-
-```bash
-$ cpanm -v git://github.com/gempesaw/Selenium-Remote-Driver.git@build/master
-```
-
-Or, without `cpanm` and/or without the `git://` protocol:
-
-```bash
-$ git clone https://github.com/gempesaw/Selenium-Remote-Driver --branch build/master --single-branch --depth 1
-$ cd Selenium-Remote-Driver
-$ perl Makefile.PL
-```
-
-Note that due to POD::Weaver, the line numbers between these generated
-branches and the master branch are unfortunately completely
-incompatible.
-
-[cpan-branch]: https://github.com/gempesaw/Selenium-Remote-Driver/tree/cpan
-[bm-branch]: https://github.com/gempesaw/Selenium-Remote-Driver/tree/build/master
-
-### Viewing dependencies
-
-You can also use `cpanm` to help you with dependencies after you've
-cloned the repository:
-
-```bash
-$ cpanm --showdeps .
-```
+[installation docs]: /INSTALL.md
 
 
 ## Usage
 ## Usage
 
 
-You'll need a Remote WebDriver Server running somewhere. You can
-download a [selenium-standalone-server.jar][standalone] and run one
-locally, or you can point your driver somewhere like [Saucelabs][s].
+You can either use this module with the standalone java server, or use
+it to directly start the webdriver binaries for you. Note that the
+latter option does _not_ require the JRE/JDK to be installed, nor does
+it require the selenium standalone server (despite the name of the
+main module!).
 
 
-[s]: http://saucelabs.com
+### with a standalone server
 
 
-### Locally
+Download the standalone server and have it running on port 4444; then
+the following should start up Firefox for you:
+
+#### Locally
 
 
 ```perl
 ```perl
 #! /usr/bin/perl
 #! /usr/bin/perl
@@ -112,7 +71,7 @@ print $driver->get_title . "\n"; # CPAN Selenium Remote Driver - Google Search
 $driver->quit;
 $driver->quit;
 ```
 ```
 
 
-### Saucelabs
+#### Saucelabs
 
 
 ```perl
 ```perl
 use Selenium::Remote::Driver;
 use Selenium::Remote::Driver;
@@ -141,20 +100,43 @@ useful [example snippets][ex].
 [ie]: https://github.com/gempesaw/Selenium-Remote-Driver/wiki/IE-browser-automation
 [ie]: https://github.com/gempesaw/Selenium-Remote-Driver/wiki/IE-browser-automation
 [chrome]: https://github.com/gempesaw/Selenium-Remote-Driver/wiki/Chrome-browser-automation
 [chrome]: https://github.com/gempesaw/Selenium-Remote-Driver/wiki/Chrome-browser-automation
 [pjs]: https://github.com/gempesaw/Selenium-Remote-Driver/wiki/PhantomJS-Headless-Browser-Automation
 [pjs]: https://github.com/gempesaw/Selenium-Remote-Driver/wiki/PhantomJS-Headless-Browser-Automation
-[ex]: https://github.com/gempesaw/Selenium-Remote-Driver/wiki/Example-Snippets
+[ex]:
+https://github.com/gempesaw/Selenium-Remote-Driver/wiki/Example-Snippets
+
+### no standalone server
+
+- _Firefox_: simply have the browser installed in the normal place
+for your OS.
+
+- _Chrome_: install the Chrome browser, [download Chromedriver][dcd]
+and get it in your `$PATH`:
+
+- _PhantomJS_: install the PhantomJS binary and get it in your `$PATH`
+
+As long as the proper binary is available in your path, you should be
+able to do the following:
 
 
-#### NB: Problems with Webdriver 2.42.x ?
+```perl
+my $firefox = Selenium::Firefox->new;
+$firefox->get('http://www.google.com');
+
+my $chrome = Selenium::Chrome->new;
+$chrome->get('http://www.google.com');
+
+my $ghost = Selenium::PhantomJS->new;
+$ghost->get('http://www.google.com');
+```
+
+Note that you can also pass a `binary` argument to any of the above
+classes to manually specify what binary to start:
+
+```perl
+my $chrome = Selenium::Chrome->new(binary => '~/Downloads/chromedriver');
+```
 
 
-It appears that the standalone webdriver API for no-content successful
-responses changed slightly in 2.42.x versions, breaking things like
-`get_ok` and `set_window_size`. Your options for fixes are:
+See the pod for the different modules for more details.
 
 
-* Upgrade your version of S::R::D via your preferred method! We've
-  released v0.2002 of S::R::D to CPAN, which contains the fixes to
-  address this.
-* Or, stick with v2.41.0 of the Selenium standalone server or lower
-  for your tests. v0.2001 of S::R::D still works with v2.41.0 of the
-  standalone server.
+[dcd]: https://sites.google.com/a/chromium.org/chromedriver/downloads
 
 
 ## Support and Documentation
 ## Support and Documentation
 
 

+ 12 - 6
cpanfile

@@ -1,18 +1,24 @@
-requires "Archive::Extract" => "0";
 requires "Archive::Zip" => "0";
 requires "Archive::Zip" => "0";
 requires "Carp" => "0";
 requires "Carp" => "0";
 requires "Cwd" => "0";
 requires "Cwd" => "0";
 requires "Data::Dumper" => "0";
 requires "Data::Dumper" => "0";
 requires "Exporter" => "0";
 requires "Exporter" => "0";
+requires "File::Basename" => "0";
 requires "File::Copy" => "0";
 requires "File::Copy" => "0";
+requires "File::Spec" => "0";
+requires "File::Spec::Functions" => "0";
 requires "File::Temp" => "0";
 requires "File::Temp" => "0";
+requires "File::Which" => "0";
 requires "HTTP::Headers" => "0";
 requires "HTTP::Headers" => "0";
 requires "HTTP::Request" => "0";
 requires "HTTP::Request" => "0";
 requires "HTTP::Response" => "0";
 requires "HTTP::Response" => "0";
 requires "IO::Compress::Zip" => "0";
 requires "IO::Compress::Zip" => "0";
 requires "IO::Socket" => "0";
 requires "IO::Socket" => "0";
+requires "IO::Socket::INET" => "0";
+requires "IO::Uncompress::Unzip" => "0";
 requires "JSON" => "0";
 requires "JSON" => "0";
 requires "LWP::UserAgent" => "0";
 requires "LWP::UserAgent" => "0";
+requires "List::MoreUtils" => "0";
 requires "MIME::Base64" => "0";
 requires "MIME::Base64" => "0";
 requires "Moo" => "1.005";
 requires "Moo" => "1.005";
 requires "Moo::Role" => "0";
 requires "Moo::Role" => "0";
@@ -22,6 +28,7 @@ requires "Sub::Install" => "0";
 requires "Test::Builder" => "0";
 requires "Test::Builder" => "0";
 requires "Test::LongString" => "0";
 requires "Test::LongString" => "0";
 requires "Try::Tiny" => "0";
 requires "Try::Tiny" => "0";
+requires "XML::Simple" => "0";
 requires "base" => "0";
 requires "base" => "0";
 requires "constant" => "0";
 requires "constant" => "0";
 requires "namespace::clean" => "0";
 requires "namespace::clean" => "0";
@@ -30,19 +37,18 @@ requires "strict" => "0";
 requires "warnings" => "0";
 requires "warnings" => "0";
 
 
 on 'test' => sub {
 on 'test' => sub {
-  requires "File::Basename" => "0";
   requires "File::stat" => "0";
   requires "File::stat" => "0";
   requires "FindBin" => "0";
   requires "FindBin" => "0";
-  requires "IO::Socket::INET" => "0";
-  requires "LWP::Simple" => "0";
-  requires "Test::Exception" => "0";
+  requires "Test::Fatal" => "0";
   requires "Test::LWP::UserAgent" => "0";
   requires "Test::LWP::UserAgent" => "0";
   requires "Test::More" => "0";
   requires "Test::More" => "0";
+  requires "Test::Warn" => "0";
+  requires "Time::Mock" => "0";
   requires "lib" => "0";
   requires "lib" => "0";
 };
 };
 
 
 on 'configure' => sub {
 on 'configure' => sub {
-  requires "ExtUtils::MakeMaker" => "6.30";
+  requires "ExtUtils::MakeMaker" => "0";
 };
 };
 
 
 on 'develop' => sub {
 on 'develop' => sub {

+ 5 - 7
dist.ini

@@ -1,5 +1,4 @@
 name = Selenium-Remote-Driver
 name = Selenium-Remote-Driver
-version = 0.2202
 author = Aditya Ivaturi <ivaturi@gmail.com>
 author = Aditya Ivaturi <ivaturi@gmail.com>
 author = Daniel Gempesaw <gempesaw@gmail.com>
 author = Daniel Gempesaw <gempesaw@gmail.com>
 author = Luke Closs <cpan@5thplane.com>
 author = Luke Closs <cpan@5thplane.com>
@@ -11,7 +10,6 @@ copyright_year = 2014
 [TravisYML]
 [TravisYML]
 build_branch = /cpan|build/
 build_branch = /cpan|build/
 perl_version = 5.20 5.18 5.16 5.14 5.12 5.10
 perl_version = 5.20 5.18 5.16 5.14 5.12 5.10
-dzil_branch =
 test_authordeps = 0
 test_authordeps = 0
 test_deps = 0
 test_deps = 0
 
 
@@ -20,6 +18,7 @@ allow_dirty = dist.ini
 allow_dirty = cpanfile
 allow_dirty = cpanfile
 allow_dirty = Changes
 allow_dirty = Changes
 [Git::Commit]
 [Git::Commit]
+[Git::NextVersion]
 [Git::Tag]
 [Git::Tag]
 ;[Git::Push]
 ;[Git::Push]
 [Git::CommitBuild]
 [Git::CommitBuild]
@@ -66,6 +65,7 @@ copy = cpanfile
 [PodCoverageTests]
 [PodCoverageTests]
 [PodSyntaxTests]
 [PodSyntaxTests]
 
 
+[CheckChangesHasContent]
 [TestRelease]
 [TestRelease]
 [ConfirmRelease]
 [ConfirmRelease]
 [UploadToCPAN]
 [UploadToCPAN]
@@ -74,11 +74,9 @@ copy = cpanfile
 perl = 5.010
 perl = 5.010
 Moo = 1.005
 Moo = 1.005
 
 
-[MetaResources]
-bugtracker.web = https://github.com/gempesaw/Selenium-Remote-Driver/issues
-repository.type = git
-repository.url = git://github.com/gempesaw/Selenium-Remote-Driver.git
-repository.web = https://github.com/gempesaw/Selenium-Remote-Driver
+[GithubMeta]
+issues = 1
+user = gempesaw
 
 
 [Encoding]
 [Encoding]
 filename = t/www/icon.gif
 filename = t/www/icon.gif

+ 344 - 0
lib/Selenium/CanStartBinary.pm

@@ -0,0 +1,344 @@
+package Selenium::CanStartBinary;
+
+# 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 probe_port/;
+use Selenium::Firefox::Binary qw/setup_firefox_binary_env/;
+use Selenium::Waiter qw/wait_until/;
+use Moo::Role;
+
+=head1 SYNOPSIS
+
+    package My::Selenium::Chrome {
+        use Moo;
+        extends 'Selenium::Remote::Driver';
+
+        has 'binary' => ( is => 'ro', default => 'chromedriver' );
+        has 'binary_port' => ( is => 'ro', default => 9515 );
+        has '_binary_args' => ( is => 'ro', default => sub {
+            return ' --port=' . shift->port . ' --url-base=wd/hub ';
+        });
+        with 'Selenium::CanStartBinary';
+        1
+    };
+
+    my $chrome_via_binary = My::Selenium::Chrome->new;
+    my $chrome_with_path  = My::Selenium::Chrome->new(
+        binary => './chromedriver'
+    );
+
+=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.
+
+=attr 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>.
+
+=cut
+
+requires 'binary';
+
+=attr 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.
+
+=cut
+
+requires 'binary_port';
+
+=attr _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.
+
+=cut
+
+requires '_binary_args';
+
+=attr 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>.
+
+=cut
+
+has '+port' => (
+    is => 'lazy',
+    builder => sub {
+        my ($self) = @_;
+
+        if ($self->binary) {
+            return find_open_port_above($self->binary_port);
+        }
+        else {
+            return 4444
+        }
+    }
+);
+
+=attr 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.
+
+=cut
+
+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;
+    }
+);
+
+=attr 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>.
+
+=cut
+
+has 'window_title' => (
+    is => 'lazy',
+    init_arg => undef,
+    builder => sub {
+        my ($self) = @_;
+        my (undef, undef, $file) = File::Spec->splitpath( $self->binary );
+        my $port = $self->port;
+
+        return $file . ':' . $port;
+    }
+);
+
+use constant IS_WIN => $^O eq 'MSWin32';
+
+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 ( $class, %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->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->isa('Selenium::Firefox')) {
+        my @args = ($port);
+
+        if ($self->has_firefox_profile) {
+            push @args, $self->firefox_profile;
+        }
+
+        setup_firefox_binary_env(@args);
+    }
+
+    my $command = $self->_construct_command;
+    system($command);
+
+    my $success = wait_until { probe_port($port) } timeout => 10;
+    if ($success) {
+        return 1;
+    }
+    else {
+        die 'Unable to connect to the ' . $self->binary . ' binary on port ' . $port;
+    }
+}
+
+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;
+        my $res = $ua->get('http://127.0.0.1:' . $port . '/wd/hub/shutdown');
+
+        # Close the orphaned command windows on windows
+        $self->shutdown_windows_binary;
+    }
+}
+
+sub shutdown_windows_binary {
+    my ($self) = @_;
+
+    if (IS_WIN) {
+        if ($self->isa('Selenium::Firefox')) {
+            # 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;
+        }
+        else {
+            my $kill = 'taskkill /FI "WINDOWTITLE eq ' . $self->window_title . '" > nul 2>&1';
+            system($kill);
+        }
+    }
+}
+
+# We want to do things before the DEMOLISH phase, as during DEMOLISH
+# we apparently have no guarantee that anything is still around
+before DEMOLISH => sub {
+    my ($self) = @_;
+    $self->shutdown_binary;
+};
+
+sub _construct_command {
+    my ($self) = @_;
+    my $executable = $self->binary;
+
+    # Executable path names may have spaces
+    $executable = '"' . $executable . '"';
+
+    # The different binaries take different arguments for proper setup
+    $executable .= $self->_binary_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) = @_;
+
+    if (IS_WIN) {
+        my $prefix = 'start "' . $self->window_title . '"';
+
+        # Let's minimize the command windows for the drivers that have
+        # separate binaries - but let's not minimize the Firefox
+        # window itself.
+        if (! $self->isa('Selenium::Firefox')) {
+            $prefix .= ' /MIN ';
+        }
+        return $prefix;
+    }
+    else {
+        return '';
+    }
+}
+
+sub _cmd_suffix {
+    # TODO: allow users to specify whether & where they want driver
+    # output to go
+
+    if (IS_WIN) {
+        return ' > /nul 2>&1 ';
+    }
+    else {
+        return ' > /dev/null 2>&1 &';
+    }
+}
+
+=head1 SEE ALSO
+
+Selenium::Chrome
+Selenium::Firefox
+Selenium::PhantomJS
+
+=cut
+
+1;

+ 70 - 0
lib/Selenium/CanStartBinary/FindBinary.pm

@@ -0,0 +1,70 @@
+package Selenium::CanStartBinary::FindBinary;
+
+# ABSTRACT: Coercions for finding webdriver binaries on your system
+use File::Which qw/which/;
+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 $naive_binary binary in your \$PATH. We'll try falling back to standard Remote Driver);
+        return;
+    }
+}

+ 35 - 0
lib/Selenium/CanStartBinary/ProbePort.pm

@@ -0,0 +1,35 @@
+package Selenium::CanStartBinary::ProbePort;
+
+# 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 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 probe_port {
+    my ($port) = @_;
+
+    return IO::Socket::INET->new(
+        PeerAddr => '127.0.0.1',
+        PeerPort => $port,
+        Timeout => 3
+    );
+}

+ 82 - 0
lib/Selenium/Chrome.pm

@@ -0,0 +1,82 @@
+package Selenium::Chrome;
+
+# ABSTRACT: Use ChromeDriver without a Selenium server
+use Moo;
+use Selenium::CanStartBinary::FindBinary qw/coerce_simple_binary/;
+extends 'Selenium::Remote::Driver';
+
+=head1 SYNOPSIS
+
+    my $driver = Selenium::Chrome->new;
+
+=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.
+
+=cut
+
+has '+browser_name' => (
+    is => 'ro',
+    default => sub { 'chrome' }
+);
+
+=attr 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>.
+
+=cut
+
+has 'binary' => (
+    is => 'lazy',
+    coerce => \&coerce_simple_binary,
+    default => sub { 'chromedriver' },
+    predicate => 1
+);
+
+=attr 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.
+
+=cut
+
+has 'binary_port' => (
+    is => 'lazy',
+    default => sub { 9515 }
+);
+
+has '_binary_args' => (
+    is => 'lazy',
+    builder => sub {
+        my ($self) = @_;
+
+        return ' --port=' . $self->port . ' --url-base=wd/hub ';
+    }
+);
+
+with 'Selenium::CanStartBinary';
+
+1;

+ 84 - 0
lib/Selenium/Firefox.pm

@@ -0,0 +1,84 @@
+package Selenium::Firefox;
+
+# ABSTRACT: Use FirefoxDriver without a Selenium server
+use Moo;
+use Selenium::CanStartBinary::FindBinary qw/coerce_firefox_binary/;
+extends 'Selenium::Remote::Driver';
+
+=head1 SYNOPSIS
+
+    my $driver = Selenium::Firefox->new;
+
+=head1 DESCRIPTION
+
+This class allows you to use the FirefoxDriver 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
+Firefox executable in your $PATH. We'll try to start the binary
+connect to it, shutting 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, we'll assume you
+know what you're doing and take no additional behavior.
+
+If you're curious whether your Selenium::Firefox instance is using a
+separate Firefox binary, or through the selenium server, you can check
+the C<binary_mode> attr after instantiation.
+
+=cut
+
+has '+browser_name' => (
+    is => 'ro',
+    default => sub { 'firefox' }
+);
+
+=attr binary
+
+Optional: specify the path to your binary. If you don't specify
+anything, we'll try to find it on our own in the default installation
+paths for Firefox. If your Firefox is elsewhere, we probably won't be
+able to find it, so you may be well served by specifying it yourself.
+
+=cut
+
+has 'binary' => (
+    is => 'lazy',
+    coerce => \&coerce_firefox_binary,
+    default => sub { 'firefox' },
+    predicate => 1
+);
+
+=attr 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.
+
+=cut
+
+has 'binary_port' => (
+    is => 'lazy',
+    default => sub { 9090 }
+);
+
+has '_binary_args' => (
+    is => 'lazy',
+    builder => sub {
+        my ($self) = @_;
+
+        return ' -no-remote';
+    }
+);
+
+with 'Selenium::CanStartBinary';
+
+1;

+ 79 - 0
lib/Selenium/Firefox/Binary.pm

@@ -0,0 +1,79 @@
+package Selenium::Firefox::Binary;
+
+# 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, $caller_profile) = @_;
+
+    $profile = $caller_profile || Selenium::Firefox::Profile->new;
+    $profile->add_webdriver($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
+}
+
+
+1;

+ 286 - 0
lib/Selenium/Firefox/Profile.pm

@@ -0,0 +1,286 @@
+package Selenium::Firefox::Profile;
+
+# 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 qw(unzip $UnzipError);
+use JSON qw/decode_json/;
+use MIME::Base64;
+use Scalar::Util qw(blessed looks_like_number);
+use XML::Simple;
+
+=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 Selenium::Remote::Driver.
+
+=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();
+
+=cut
+
+sub new {
+    my $class = shift;
+
+    # TODO: add handling for a pre-existing profile folder passed into
+    # the constructor
+
+    # TODO: accept user prefs, boolean prefs, and extensions in
+    # constructor
+    my $self = {
+        profile_dir => File::Temp->newdir(),
+        user_prefs => {},
+        extensions => []
+      };
+    bless $self, $class or die "Can't bless $class: $!";
+
+    return $self;
+}
+
+=method 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");
+
+=cut
+
+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;
+    }
+}
+
+=method 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);
+
+=cut
+
+sub set_boolean_preference {
+    my ($self, %prefs) = @_;
+
+    foreach (keys %prefs) {
+        my $value = $prefs{$_};
+
+        $self->{user_prefs}->{$_} = $value ? 'true' : 'false';
+    }
+}
+
+=method 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"
+
+=cut
+
+sub get_preference {
+    my ($self, $pref) = @_;
+
+    return $self->{user_prefs}->{$pref};
+}
+
+=method 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');
+
+=cut
+
+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);
+}
+
+=method add_webdriver
+
+Primarily for internal use, we add the webdriver extension to the
+current Firefox profile.
+
+=cut
+
+sub add_webdriver {
+    my ($self, $port) = @_;
+
+    my $this_dir = dirname(abs_path(__FILE__));
+    my $webdriver_extension = $this_dir . '/webdriver.xpi';
+    my $default_prefs_filename = $this_dir . '/webdriver_prefs.json';
+
+    my $json;
+    {
+        local $/;
+        open (my $fh, '<', $default_prefs_filename);
+        $json = <$fh>;
+        close ($fh);
+    }
+    my $webdriver_prefs = decode_json($json);
+
+    # TODO: Let the user's mutable preferences persist instead of
+    # overwriting them here.
+    $self->set_preference(%{ $webdriver_prefs->{mutable} });
+    $self->set_preference(%{ $webdriver_prefs->{frozen} });
+
+    $self->add_extension($webdriver_extension);
+    $self->set_preference('webdriver_firefox_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();
+    my $dir_member = $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 $fh;
+        unzip $xpi => \$fh, Name => "install.rdf"
+          or die "unzip failed: $UnzipError\n";
+
+        my $rdf = XMLin($fh);
+        my $name = $rdf->{Description}->{'em:id'};
+
+        my $xpi_dest = $extension_dir . $name . ".xpi";
+        copy($xpi, $xpi_dest)
+            or croak "Error copying $_ to $xpi_dest : $!";
+    }
+}
+
+1;
+
+__END__
+
+=head1 SEE ALSO
+
+http://kb.mozillazine.org/About:config_entries
+https://developer.mozilla.org/en-US/docs/Mozilla/Preferences/A_brief_guide_to_Mozilla_preferences

BIN
lib/Selenium/Firefox/amd64/libibushandler.so


BIN
lib/Selenium/Firefox/amd64/x_ignore_nofocus.so


BIN
lib/Selenium/Firefox/webdriver.xpi


+ 68 - 0
lib/Selenium/Firefox/webdriver_prefs.json

@@ -0,0 +1,68 @@
+{
+  "frozen": {
+    "app.update.auto": false,
+    "app.update.enabled": false,
+    "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.safebrowsing.enabled": false,
+    "browser.safebrowsing.malware.enabled": false,
+    "browser.search.update": false,
+    "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.logging.enabled": true,
+    "extensions.update.enabled": false,
+    "extensions.update.notifyUser": false,
+    "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,
+    "security.warn_entering_secure": false,
+    "security.warn_entering_secure.show_once": false,
+    "security.warn_entering_weak": false,
+    "security.warn_entering_weak.show_once": false,
+    "security.warn_leaving_secure": false,
+    "security.warn_leaving_secure.show_once": false,
+    "security.warn_submit_insecure": false,
+    "security.warn_viewing_mixed": false,
+    "security.warn_viewing_mixed.show_once": false,
+    "signon.rememberSignons": false,
+    "toolkit.networkmanager.disable": true,
+    "toolkit.telemetry.prompted": 2,
+    "toolkit.telemetry.enabled": false,
+    "toolkit.telemetry.rejected": true
+  },
+  "mutable": {
+    "browser.dom.window.dump.enabled": true,
+    "browser.newtab.url": "about:blank",
+    "browser.newtabpage.enabled": false,
+    "browser.startup.page": 0,
+    "browser.startup.homepage": "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,
+    "network.http.max-connections-per-server": 10,
+    "startup.homepage_welcome_url": "about:blank",
+    "webdriver_accept_untrusted_certs": true,
+    "webdriver_assume_untrusted_issuer": true
+  }
+}

BIN
lib/Selenium/Firefox/x86/libibushandler.so


BIN
lib/Selenium/Firefox/x86/x_ignore_nofocus.so


+ 23 - 0
lib/Selenium/InternetExplorer.pm

@@ -0,0 +1,23 @@
+package Selenium::InternetExplorer;
+
+# ABSTRACT: A convenience package for creating a IE instance
+use Moo;
+extends 'Selenium::Remote::Driver';
+
+=head1 SYNOPSIS
+
+    my $driver = Selenium::InternetExplorer->new;
+
+=cut
+
+has '+browser_name' => (
+    is => 'ro',
+    default => sub { 'internet_explorer' }
+);
+
+has '+platform' => (
+    is => 'ro',
+    default => sub { 'WINDOWS' }
+);
+
+1;

+ 95 - 0
lib/Selenium/PhantomJS.pm

@@ -0,0 +1,95 @@
+package Selenium::PhantomJS;
+
+# ABSTRACT: Use GhostDriver without a Selenium server
+use Moo;
+use Selenium::CanStartBinary::FindBinary qw/coerce_simple_binary/;
+extends 'Selenium::Remote::Driver';
+
+=head1 SYNOPSIS
+
+    my $driver = Selenium::PhantomJS->new;
+
+=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!
+
+=cut
+
+has '+browser_name' => (
+    is => 'ro',
+    default => sub { 'phantomjs' }
+);
+
+=attr 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>.
+
+=cut
+
+has 'binary' => (
+    is => 'lazy',
+    coerce => \&coerce_simple_binary,
+    default => sub { 'phantomjs' },
+    predicate => 1
+);
+
+=attr 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.
+
+=cut
+
+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;

+ 37 - 2
lib/Selenium/Remote/Commands.pm

@@ -347,7 +347,7 @@ has '_cmds' => (
             'uploadFile' => {
             'uploadFile' => {
                 'method'             => 'POST',
                 'method'             => 'POST',
                 'url'                => 'session/:sessionId/file',
                 'url'                => 'session/:sessionId/file',
-                'no_content_success' => 1
+                'no_content_success' => 0
             },
             },
             'getLocalStorageItem' => {
             'getLocalStorageItem' => {
                 'method'             => 'GET',
                 'method'             => 'GET',
@@ -358,7 +358,42 @@ has '_cmds' => (
                 'method'             => 'DELETE',
                 'method'             => 'DELETE',
                 'url'                => '/session/:sessionId/local_storage/key/:key',
                 'url'                => '/session/:sessionId/local_storage/key/:key',
                 'no_content_success' => 1
                 '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
+            },
 
 
             # /session/:sessionId/local_storage
             # /session/:sessionId/local_storage
             # /session/:sessionId/local_storage/key/:key
             # /session/:sessionId/local_storage/key/:key

+ 320 - 25
lib/Selenium/Remote/Driver.pm

@@ -20,6 +20,11 @@ use Scalar::Util;
 use Selenium::Remote::RemoteConnection;
 use Selenium::Remote::RemoteConnection;
 use Selenium::Remote::Commands;
 use Selenium::Remote::Commands;
 use Selenium::Remote::WebElement;
 use Selenium::Remote::WebElement;
+use File::Spec::Functions ();
+use File::Basename ();
+use Sub::Install ();
+use Cwd ();
+use MIME::Base64 ();
 
 
 use constant FINDERS => {
 use constant FINDERS => {
     class             => 'class name',
     class             => 'class name',
@@ -34,6 +39,7 @@ use constant FINDERS => {
     xpath             => 'xpath',
     xpath             => 'xpath',
 };
 };
 
 
+
 =head1 SYNOPSIS
 =head1 SYNOPSIS
 
 
     use Selenium::Remote::Driver;
     use Selenium::Remote::Driver;
@@ -62,7 +68,16 @@ the Selenium Server (Selenium Server is a Java application).
 
 
 =cut
 =cut
 
 
-=head1 USAGE (read this first)
+=head1 USAGE
+
+=head2 Without Standalone Server
+
+As of v0.25, it's possible to use this module without a standalone
+server - that is, you would not need the JRE or the JDK to run your
+Selenium tests. See L<Selenium::Chrome>, L<Selenium::PhantomJS>, and
+L<Selenium::Firefox> for details. If you'd like additional browsers
+besides these, give us a holler over in
+L<Github|https://github.com/gempesaw/Selenium-Remote-Driver/issues>.
 
 
 =head2 Remote Driver Response
 =head2 Remote Driver Response
 
 
@@ -101,10 +116,14 @@ For example, a testing-subclass may extend the web-element object with testing m
 
 
 =head1 TESTING
 =head1 TESTING
 
 
-If are writing automated tests using this module, make sure you also see
-L<Test::Selenium::Remote::Driver> which is also included in this distribution.
-It includes convenience testing methods for many of the selenum methods
-available here.
+If are writing automated tests using this module, you may be
+interested in L<Test::Selenium::Remote::Driver> which is also included
+in this distribution. It includes convenience testing methods for many
+of the selenum methods available here.
+
+Your other option is to use this module in conjunction with your
+choice of testing modules, like L<Test::Spec> or L<Test::More> as
+you please.
 
 
 =head1 FUNCTIONS
 =head1 FUNCTIONS
 
 
@@ -124,7 +143,7 @@ available here.
         'platform'             - <string>   - desired platform: {WINDOWS|XP|VISTA|MAC|LINUX|UNIX|ANY}
         'platform'             - <string>   - desired platform: {WINDOWS|XP|VISTA|MAC|LINUX|UNIX|ANY}
         'javascript'           - <boolean>  - whether javascript should be supported
         'javascript'           - <boolean>  - whether javascript should be supported
         'accept_ssl_certs'     - <boolean>  - whether SSL certs should be accepted, default is true.
         'accept_ssl_certs'     - <boolean>  - whether SSL certs should be accepted, default is true.
-        'firefox_profile'      - Profile    - Use S::R::D::Firefox::Profile to create a Firefox profile for the browser to use
+        'firefox_profile'      - Profile    - Use Selenium::Firefox::Profile to create a Firefox profile for the browser to use
         'proxy'                - HASH       - Proxy configuration with the following keys:
         'proxy'                - HASH       - Proxy configuration with the following keys:
             'proxyType' - <string> - REQUIRED, Possible values are:
             'proxyType' - <string> - REQUIRED, Possible values are:
                 direct     - A direct connection - no proxy in use,
                 direct     - A direct connection - no proxy in use,
@@ -201,7 +220,6 @@ available here.
 =head2 new_from_caps
 =head2 new_from_caps
 
 
  Description:
  Description:
-
     For experienced users who want complete control over the desired
     For experienced users who want complete control over the desired
     capabilities, use this alternative constructor along with the
     capabilities, use this alternative constructor along with the
     C<desired_capabilities> hash key in the init hash. Unlike "new",
     C<desired_capabilities> hash key in the init hash. Unlike "new",
@@ -256,6 +274,7 @@ has 'remote_server_addr' => (
     is      => 'rw',
     is      => 'rw',
     coerce  => sub { ( defined($_[0]) ? $_[0] : 'localhost' )},
     coerce  => sub { ( defined($_[0]) ? $_[0] : 'localhost' )},
     default => sub {'localhost'},
     default => sub {'localhost'},
+    predicate => 1
 );
 );
 
 
 has 'browser_name' => (
 has 'browser_name' => (
@@ -265,8 +284,7 @@ has 'browser_name' => (
 );
 );
 
 
 has 'base_url' => (
 has 'base_url' => (
-    is      => 'rw',
-    lazy    => 1,
+    is      => 'lazy',
     coerce  => sub {
     coerce  => sub {
         my $base_url = shift;
         my $base_url = shift;
         $base_url =~ s|/$||;
         $base_url =~ s|/$||;
@@ -285,6 +303,7 @@ has 'port' => (
     is      => 'rw',
     is      => 'rw',
     coerce  => sub { ( defined($_[0]) ? $_[0] : '4444' )},
     coerce  => sub { ( defined($_[0]) ? $_[0] : '4444' )},
     default => sub {'4444'},
     default => sub {'4444'},
+    predicate => 1
 );
 );
 
 
 has 'version' => (
 has 'version' => (
@@ -382,24 +401,22 @@ has 'firefox_profile' => (
     coerce    => sub {
     coerce    => sub {
         my $profile = shift;
         my $profile = shift;
         unless (Scalar::Util::blessed($profile)
         unless (Scalar::Util::blessed($profile)
-          && $profile->isa('Selenium::Remote::Driver::Firefox::Profile')) {
-            croak "firefox_profile should be a Selenium::Remote::Driver::Firefox::Profile\n";
+          && $profile->isa('Selenium::Firefox::Profile')) {
+            croak "firefox_profile should be a Selenium::Firefox::Profile\n";
         }
         }
 
 
-        return $profile->_encode;
+        return $profile;
     },
     },
     predicate => 'has_firefox_profile'
     predicate => 'has_firefox_profile'
 );
 );
 
 
 has 'desired_capabilities' => (
 has 'desired_capabilities' => (
-    is        => 'rw',
-    lazy      => 1,
+    is        => 'lazy',
     predicate => 'has_desired_capabilities'
     predicate => 'has_desired_capabilities'
 );
 );
 
 
 has 'inner_window_size' => (
 has 'inner_window_size' => (
-    is        => 'rw',
-    lazy      => 1,
+    is        => 'lazy',
     predicate => 1,
     predicate => 1,
     coerce    => sub {
     coerce    => sub {
         my $size = shift;
         my $size = shift;
@@ -417,6 +434,8 @@ has 'inner_window_size' => (
 
 
 );
 );
 
 
+with 'Selenium::Remote::Finders';
+
 sub BUILD {
 sub BUILD {
     my $self = shift;
     my $self = shift;
 
 
@@ -435,6 +454,22 @@ sub BUILD {
         my $size = $self->inner_window_size;
         my $size = $self->inner_window_size;
         $self->set_inner_window_size(@$size);
         $self->set_inner_window_size(@$size);
     }
     }
+
+    # Setup non-croaking, parameter versions of finders
+    foreach my $by (keys %{ $self->FINDERS }) {
+        my $finder_name = 'find_element_by_' . $by;
+        # In case we get instantiated multiple times, we don't want to
+        # install into the name space every time.
+        unless ($self->can($finder_name)) {
+            my $find_sub = $self->_build_find_by($by);
+
+            Sub::Install::install_sub({
+                code => $find_sub,
+                into => __PACKAGE__,
+                as   => $finder_name,
+            });
+        }
+    }
 }
 }
 
 
 sub new_from_caps {
 sub new_from_caps {
@@ -475,6 +510,8 @@ sub _execute_command {
                 }
                 }
                 elsif ( $resp->{cmd_return} ) {
                 elsif ( $resp->{cmd_return} ) {
                     if ( ref( $resp->{cmd_return} ) eq 'HASH' ) {
                     if ( ref( $resp->{cmd_return} ) eq 'HASH' ) {
+                        $msg .= ": $res->{command}"
+                          if $res->{command};
                         $msg .= ": $resp->{cmd_return}->{error}->{msg}"
                         $msg .= ": $resp->{cmd_return}->{error}->{msg}"
                           if $resp->{cmd_return}->{error}->{msg};
                           if $resp->{cmd_return}->{error}->{msg};
                         $msg .= ": $resp->{cmd_return}->{message}"
                         $msg .= ": $resp->{cmd_return}->{message}"
@@ -516,7 +553,7 @@ sub new_session {
 
 
     if ($args->{desiredCapabilities}->{browserName} =~ /firefox/i
     if ($args->{desiredCapabilities}->{browserName} =~ /firefox/i
           && $self->has_firefox_profile) {
           && $self->has_firefox_profile) {
-        $args->{desiredCapabilities}->{firefox_profile} = $self->firefox_profile;
+        $args->{desiredCapabilities}->{firefox_profile} = $self->firefox_profile->_encode;
     }
     }
 
 
     $self->_request_new_session($args);
     $self->_request_new_session($args);
@@ -1391,7 +1428,6 @@ sub capture_screenshot {
     my ( $self, $filename ) = @_;
     my ( $self, $filename ) = @_;
     croak '$filename is required' unless $filename;
     croak '$filename is required' unless $filename;
 
 
-    require MIME::Base64;
     open( my $fh, '>', $filename );
     open( my $fh, '>', $filename );
     binmode $fh;
     binmode $fh;
     print $fh MIME::Base64::decode_base64( $self->screenshot() );
     print $fh MIME::Base64::decode_base64( $self->screenshot() );
@@ -1581,6 +1617,8 @@ sub set_window_size {
     if ( not defined $height and not defined $width ) {
     if ( not defined $height and not defined $width ) {
         croak "height & width of browser are required";
         croak "height & width of browser are required";
     }
     }
+    $height += 0;
+    $width += 0;
     my $res = { 'command' => 'setWindowSize', 'window_handle' => $window };
     my $res = { 'command' => 'setWindowSize', 'window_handle' => $window };
     my $params = { 'height' => $height, 'width' => $width };
     my $params = { 'height' => $height, 'width' => $width };
     my $ret = $self->_execute_command( $res, $params );
     my $ret = $self->_execute_command( $res, $params );
@@ -1747,8 +1785,27 @@ sub get_page_source {
 =head2 find_element
 =head2 find_element
 
 
  Description:
  Description:
-    Search for an element on the page, starting from the document root. The
-    located element will be returned as a WebElement object.
+    Search for an element on the page, starting from the document
+    root. The located element will be returned as a WebElement
+    object. If the element cannot be found, we will CROAK, killing
+    your script. If you wish for a warning instead, use the
+    parameterized version of the finders:
+
+        find_element_by_class
+        find_element_by_class_name
+        find_element_by_css
+        find_element_by_id
+        find_element_by_link
+        find_element_by_link_text
+        find_element_by_name
+        find_element_by_partial_link_text
+        find_element_by_tag_name
+        find_element_by_xpath
+
+    These functions all take a single STRING argument: the locator
+    search target of the element you want. If the element is found, we
+    will receive a WebElement. Otherwise, we will return 0. Note that
+    invoking methods on 0 will of course kill your script.
 
 
  Input: 2 (1 optional)
  Input: 2 (1 optional)
     Required:
     Required:
@@ -2000,6 +2057,46 @@ sub find_child_elements {
     }
     }
 }
 }
 
 
+=head2 find_element_by_class
+
+See L</find_element>.
+
+=head2 find_element_by_class_name
+
+See L</find_element>.
+
+=head2 find_element_by_css
+
+See L</find_element>.
+
+=head2 find_element_by_id
+
+See L</find_element>.
+
+=head2 find_element_by_link
+
+See L</find_element>.
+
+=head2 find_element_by_link_text
+
+See L</find_element>.
+
+=head2 find_element_by_name
+
+See L</find_element>.
+
+=head2 find_element_by_partial_link_text
+
+See L</find_element>.
+
+=head2 find_element_by_tag_name
+
+See L</find_element>.
+
+=head2 find_element_by_xpath
+
+See L</find_element>.
+
 =head2 get_active_element
 =head2 get_active_element
 
 
  Description:
  Description:
@@ -2027,6 +2124,167 @@ sub get_active_element {
     }
     }
 }
 }
 
 
+=head2 cache_status
+
+ Description:
+    Get the status of the html5 application cache.
+
+ Usage:
+    print $driver->cache_status;
+
+ Output:
+    <number> - Status code for application cache: {UNCACHED = 0, IDLE = 1, CHECKING = 2, DOWNLOADING = 3, UPDATE_READY = 4, OBSOLETE = 5}
+
+=cut
+
+sub cache_status {
+    my ($self) = @_;
+    my $res = { 'command' => 'cacheStatus' };
+    return $self->_execute_command($res);
+}
+
+=head2 set_geolocation
+
+ Description:
+    Set the current geographic location - note that your driver must
+    implement this endpoint, or else it will crash your session. At the
+    very least, it works in v2.12 of Chromedriver.
+
+ Input:
+    Required:
+        HASH: A hash with key C<location> whose value is a Location hashref. See
+        usage section for example.
+
+ Usage:
+    $driver->set_geolocation( location => {
+        latitude  => 40.714353,
+        longitude => -74.005973,
+        altitude  => 0.056747
+    });
+
+ Output:
+    BOOLEAN - success or failure
+
+=cut
+
+sub set_geolocation {
+    my ( $self, %params ) = @_;
+    my $res = { 'command' => 'setGeolocation' };
+    return $self->_execute_command( $res, \%params );
+}
+
+=head2 get_geolocation
+
+ Description:
+    Get the current geographic location. Note that your webdriver must
+    implement this endpoint - otherwise, it will crash your session. At
+    the time of release, we couldn't get this to work on the desktop
+    FirefoxDriver or desktop Chromedriver.
+
+ Usage:
+    print $driver->get_geolocation;
+
+ Output:
+    { latitude: number, longitude: number, altitude: number } - The current geo location.
+
+=cut
+
+sub get_geolocation {
+    my ($self) = @_;
+    my $res = { 'command' => 'getGeolocation' };
+    return $self->_execute_command($res);
+}
+
+=head2 get_log
+
+ Description:
+    Get the log for a given log type. Log buffer is reset after each request.
+
+ Input:
+    Required:
+        <STRING> - Type of log to retrieve:
+        {client|driver|browser|server}. There may be others available; see
+        get_log_types for a full list for your driver.
+
+ Usage:
+    $driver->get_log( $log_type );
+
+ Output:
+    <ARRAY|ARRAYREF> - An array of log entries since the most recent request.
+
+=cut
+
+sub get_log {
+    my ( $self, $type ) = @_;
+    my $res = { 'command' => 'getLog' };
+    return $self->_execute_command( $res, { type => $type });
+}
+
+=head2 get_log_types
+
+ Description:
+    Get available log types. By default, every driver should have client,
+    driver, browser, and server types, but there may be more available,
+    depending on your driver.
+
+ Usage:
+    my @types = $driver->get_log_types;
+    $driver->get_log($types[0]);
+
+ Output:
+    <ARRAYREF> - The list of log types.
+
+=cut
+
+sub get_log_types {
+    my ($self) = @_;
+    my $res = { 'command' => 'getLogTypes' };
+    return $self->_execute_command($res);
+}
+
+
+=head2 set_orientation
+
+ Description:
+    Set the browser orientation.
+
+ Input:
+    Required:
+        <STRING> - Orientation {LANDSCAPE|PORTRAIT}
+
+ Usage:
+    $driver->set_orientation( $orientation  );
+
+ Output:
+    BOOLEAN - success or failure
+
+=cut
+
+sub set_orientation {
+    my ( $self, $orientation ) = @_;
+    my $res = { 'command' => 'setOrientation' };
+    return $self->_execute_command( $res, { orientation => $orientation } );
+}
+
+=head2 get_orientation
+
+ Description:
+    Get the current browser orientation. Returns either LANDSCAPE|PORTRAIT.
+
+ Usage:
+    print $driver->get_orientation;
+
+ Output:
+    <STRING> - your orientation.
+
+=cut
+
+sub get_orientation {
+    my ($self) = @_;
+    my $res = { 'command' => 'getOrientation' };
+    return $self->_execute_command($res);
+}
+
 =head2 send_modifier
 =head2 send_modifier
 
 
  Description:
  Description:
@@ -2186,6 +2444,13 @@ sub button_up {
     machine. That file then can be used for testing file upload on web
     machine. That file then can be used for testing file upload on web
     forms. Returns the remote-server's path to the file.
     forms. Returns the remote-server's path to the file.
 
 
+    Passing raw data as an argument past the filename will upload
+    that rather than the file's contents.
+
+    When passing raw data, be advised that it expects a zipped
+    and then base64 encoded version of a single file.
+    Multiple files are not supported by the remote server.
+
  Usage:
  Usage:
     my $remote_fname = $driver->upload_file( $fname );
     my $remote_fname = $driver->upload_file( $fname );
     my $element = $driver->find_element( '//input[@id="file"]' );
     my $element = $driver->find_element( '//input[@id="file"]' );
@@ -2197,18 +2462,48 @@ sub button_up {
 # org.openqa.selenium.remote.RemoteWebElement java class.
 # org.openqa.selenium.remote.RemoteWebElement java class.
 
 
 sub upload_file {
 sub upload_file {
-    my ( $self, $filename ) = @_;
+    my ( $self, $filename, $raw_content ) = @_;
+
+    my $params;
+    if (defined $raw_content) {
+        #If no processing is passed, send the argument raw
+        $params = {
+            file => $raw_content
+        };
+    }
+    else {
+        #Otherwise, zip/base64 it.
+        $params = $self->_prepare_file($filename);
+    }
+
+    my $res = { 'command' => 'uploadFile' };    # /session/:SessionId/file
+    my $ret = $self->_execute_command( $res, $params );
+
+    #WORKAROUND: Since this is undocumented selenium functionality,
+    #work around a bug.
+    my ($drive, $path, $file) = File::Spec::Functions::splitpath($ret);
+    if ($file ne $filename) {
+        $ret = File::Spec::Functions::catpath($drive,$path,$filename);
+    }
+
+    return $ret;
+}
+
+sub _prepare_file {
+    my ($self,$filename) = @_;
+
+    #Apparently zip chokes on non-canonical paths, creating double
+    #submissions sometimes
+    $filename = Cwd::abs_path($filename);
+
     if ( not -r $filename ) { die "upload_file: no such file: $filename"; }
     if ( not -r $filename ) { die "upload_file: no such file: $filename"; }
     my $string = "";    # buffer
     my $string = "";    # buffer
     zip $filename => \$string
     zip $filename => \$string
       or die "zip failed: $ZipError\n";    # compress the file into string
       or die "zip failed: $ZipError\n";    # compress the file into string
-    my $res = { 'command' => 'uploadFile' };    # /session/:SessionId/file
-    require MIME::Base64;
 
 
-    my $params = {
+    return {
         file => MIME::Base64::encode_base64($string)          # base64-encoded string
         file => MIME::Base64::encode_base64($string)          # base64-encoded string
     };
     };
-    return $self->_execute_command( $res, $params );
 }
 }
 
 
 =head2 get_text
 =head2 get_text

+ 8 - 236
lib/Selenium/Remote/Driver/Firefox/Profile.pm

@@ -1,254 +1,26 @@
 package Selenium::Remote::Driver::Firefox::Profile;
 package Selenium::Remote::Driver::Firefox::Profile;
 
 
 # ABSTRACT: Use custom profiles with Selenium::Remote::Driver
 # ABSTRACT: Use custom profiles with Selenium::Remote::Driver
-
 use strict;
 use strict;
 use warnings;
 use warnings;
+use Selenium::Firefox::Profile;
 
 
-use Archive::Zip qw( :ERROR_CODES );
-use Archive::Extract;
-use Carp qw(croak);
-use Cwd qw(abs_path);
-use File::Copy qw(copy);
-use File::Temp;
-use MIME::Base64;
-use Scalar::Util qw(looks_like_number);
-
-=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 Selenium::Remote::Driver.
-
-=head1 SYNPOSIS
-
-    use Selenium::Remote::Driver;
-    use Selenium::Remote::Driver::Firefox::Profile;
-
-    my $profile = Selenium::Remote::Driver::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();
-
-=cut
-
-sub new {
-    my $class = shift;
-
-    # TODO: add handling for a pre-existing profile folder passed into
-    # the constructor
-
-    # TODO: accept user prefs, boolean prefs, and extensions in
-    # constructor
-    my $self = {
-        profile_dir => File::Temp->newdir(),
-        user_prefs => {},
-        extensions => []
-      };
-    bless $self, $class or die "Can't bless $class: $!";
-
-    return $self;
-}
-
-=method 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,
-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");
-
-=cut
-
-sub set_preference {
-    my ($self, %prefs) = @_;
-
-    foreach (keys %prefs) {
-        my $value = $prefs{$_};
-        my $clean_value = '';
-
-        if ($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;
-    }
-}
-
-=method 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);
-
-=cut
-
-sub set_boolean_preference {
-    my ($self, %prefs) = @_;
-
-    foreach (keys %prefs) {
-        my $value = $prefs{$_};
-
-        $self->{user_prefs}->{$_} = $value ? 'true' : 'false';
-    }
-}
-
-=method 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"
-
-=cut
-
-sub get_preference {
-    my ($self, $pref) = @_;
-
-    return $self->{user_prefs}->{$pref};
+BEGIN {
+    push our @ISA, 'Selenium::Firefox::Profile';
 }
 }
 
 
-=method 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.
+=head1 DESCRIPTION
 
 
-    $profile->add_extension('t/www/redisplay.xpi');
+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.
 
 
 =cut
 =cut
 
 
-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 _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();
-    my $dir_member = $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 (@{$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 $ae = Archive::Extract->new( archive => $_,
-                                        type => "zip");
-
-        my $tempDir = File::Temp->newdir();
-        $ae->extract( to => $tempDir );
-        my $install = $ae->extract_path();
-        $install .= '/install.rdf';
-
-        open (my $fh, "<", $install)
-            or croak "No install.rdf inside $_: $!";
-        my (@file) = <$fh>;
-        close ($fh);
-
-        my @name = grep { chomp; $_ =~ /<em:id>[^{]/ } @file;
-        $name[0] =~ s/.*<em:id>(.*)<\/em:id>.*/$1/;
-
-        my $xpi_dest = $extension_dir . $name[0] . ".xpi";
-        copy($_, $xpi_dest)
-            or croak "Error copying $_ to $xpi_dest : $!";
-    }
-}
-
 1;
 1;
 
 
-__END__
-
 =head1 SEE ALSO
 =head1 SEE ALSO
 
 
+Selenium::Firefox::Profile
 http://kb.mozillazine.org/About:config_entries
 http://kb.mozillazine.org/About:config_entries
 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

+ 36 - 0
lib/Selenium/Remote/Finders.pm

@@ -0,0 +1,36 @@
+package Selenium::Remote::Finders;
+
+# ABSTRACT: Handle construction of generic parameter finders
+use Try::Tiny;
+use Carp qw/carp/;
+use Moo::Role;
+use namespace::clean;
+
+=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.
+
+=cut
+
+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;

+ 8 - 6
lib/Selenium/Remote/Mock/RemoteConnection.pm

@@ -52,12 +52,16 @@ has 'session_id' => (
     default => sub { undef },
     default => sub { undef },
 );
 );
 
 
+has 'remote_server_addr' => (
+    is => 'lazy',
+    default => sub { 'localhost' }
+);
+
 sub BUILD {
 sub BUILD {
     my $self = shift;
     my $self = shift;
     croak 'Cannot define replay and record attributes at the same time' if (($self->replay) && ($self->record));
     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_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));
     croak 'replay attribute needs to be defined' if (!($self->replay) && ($self->replay_file));
-    $self->remote_server_addr('localhost');
     $self->port('4444');
     $self->port('4444');
     if ($self->replay) {
     if ($self->replay) {
         $self->load_session_store($self->replay_file);
         $self->load_session_store($self->replay_file);
@@ -113,6 +117,9 @@ sub request {
         $content = $json->allow_nonref->utf8->canonical(1)->encode($params);
         $content = $json->allow_nonref->utf8->canonical(1)->encode($params);
     }
     }
     my $url_params = $resource->{url_params};
     my $url_params = $resource->{url_params};
+
+    print "REQ: $method, $url, $content\n" if $self->debug;
+
     if ( $self->record ) {
     if ( $self->record ) {
         my $response = $self->SUPER::request( $resource, $params, 1 );
         my $response = $self->SUPER::request( $resource, $params, 1 );
         push @{$self->session_store->{"$method $url $content"}},$response->as_string;
         push @{$self->session_store->{"$method $url $content"}},$response->as_string;
@@ -140,10 +147,6 @@ sub request {
     my $ret = { cmd_status => 'OK', cmd_return => 1 };
     my $ret = { cmd_status => 'OK', cmd_return => 1 };
     if ( defined( $spec->{$cmd} ) ) {
     if ( defined( $spec->{$cmd} ) ) {
         my $return_sub = $spec->{$cmd};
         my $return_sub = $spec->{$cmd};
-        if ($no_content_success) {
-            $ret->{cmd_return} = 1;
-        }
-        else {
             my $mock_return = $return_sub->( $url_params, $params );
             my $mock_return = $return_sub->( $url_params, $params );
             if ( ref($mock_return) eq 'HASH' ) {
             if ( ref($mock_return) eq 'HASH' ) {
                 $ret->{cmd_status} = $mock_return->{status};
                 $ret->{cmd_status} = $mock_return->{status};
@@ -153,7 +156,6 @@ sub request {
             else {
             else {
                 $ret = $mock_return;
                 $ret = $mock_return;
             }
             }
-        }
         $ret->{session_id} = $self->fake_session_id if ( ref($ret) eq 'HASH' );
         $ret->{session_id} = $self->fake_session_id if ( ref($ret) eq 'HASH' );
     }
     }
     else {
     else {

+ 111 - 0
lib/Selenium/Waiter.pm

@@ -0,0 +1,111 @@
+package Selenium::Waiter;
+
+# ABSTRACT: Provides a utility wait_until function
+use Try::Tiny;
+require Exporter;
+our @ISA = qw/Exporter/;
+our @EXPORT = qw/wait_until/;
+
+=head1 SYNOPSIS
+
+    use Selenium::Waiter qw/wait_until/;
+    my $d = Selenium::Remote::Driver->new;
+
+    my $div = wait_until { $d->find_element('div', css') };
+
+=func 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;
+
+=cut
+
+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 $try_ret = try {
+            my $assert_ret = $assert->();
+            return $assert_ret if $assert_ret;
+        }
+        catch {
+            $exception = $_;
+            warn $_ if $args->{debug};
+            return '';
+        }
+        finally {
+            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;

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

@@ -33,7 +33,7 @@ has func_list => (
             'send_modifier_ok', 'accept_alert_ok', 'dismiss_alert_ok',
             'send_modifier_ok', 'accept_alert_ok', 'dismiss_alert_ok',
             'get_ok', 'go_back_ok', 'go_forward_ok', 'add_cookie_ok',
             'get_ok', 'go_back_ok', 'go_forward_ok', 'add_cookie_ok',
             'get_page_source_ok', 'find_element_ok', 'find_elements_ok',
             'get_page_source_ok', 'find_element_ok', 'find_elements_ok',
-            'find_child_element_ok', 'find_child_elements_ok',
+            'find_child_element_ok', 'find_child_elements_ok', 'find_no_element_ok',
             'compare_elements_ok', 'click_ok', 'double_click_ok',
             'compare_elements_ok', 'click_ok', 'double_click_ok',
             'body_like',
             'body_like',
         ];
         ];
@@ -44,7 +44,12 @@ sub has_args {
     my $self          = shift;
     my $self          = shift;
     my $fun_name      = shift;
     my $fun_name      = shift;
     my $hash_fun_args = {
     my $hash_fun_args = {
-        'find_element'     => 1,
+        'find_element'     => 2,
+        'find_no_element' => 2,
+        'find_child_element'     => 3,
+        'find_child_elements'     => 3,
+        'find_element'     => 2,
+        'find_elements'     => 2,
         'compare_elements' => 2,
         'compare_elements' => 2,
         'get' => 1,
         'get' => 1,
     };
     };
@@ -234,66 +239,173 @@ more documentation, see the related test methods in L<Selenium::Remote::Driver>
 
 
     click_ok
     click_ok
     double_click_ok
     double_click_ok
+=cut
+
 
 
-=head2 $twd->type_element_ok($search_target, $keys, [, $desc ]);
+# function composing a find_element with locator with a webelement test
 
 
-   $twd->type_element_ok( $search_target, $keys [, $desc ] );
+sub _find_element_with_action { 
+    my $self = shift; 
+    my $method = shift;
+    my ($locator,$locator_strategy,$params,$desc) = @_;
+    # case 4 args 
+    if ($desc) { 
+        $self->croak('Invalid locator strategy') unless ($self->FINDERS->{$locator_strategy});
+    }
+    else { 
+        if ($params) { 
+            # means that we called it the 'old way' (no locator strategy)
+            if (!defined($self->FINDERS->{$locator_strategy})) { 
+                $desc = $params; 
+                $params = $locator_strategy; 
+                $locator_strategy = $self->_get_finder_key($self->default_finder);
+            }
+        }
+        else { 
+            # means it was called with no locator strategy and no desc 
+            if ($locator_strategy) { 
+                if (!defined($self->FINDERS->{$locator_strategy})) { 
+                    $params = $locator_strategy; 
+                    $locator_strategy = $self->_get_finder_key($self->default_finder);
+                }
+            }
+            else { 
+                $self->croak('Not enough arguments');
+            }
+        }
+    }
+    unless ($desc) {
+        $desc = $method;
+        $desc .= "'" . join( " ", ($params // '') ) . "'";
+    }
+    return $self->find_element($locator,$locator_strategy)->$method( $params, $desc );
+}
+
+
+=head2 $twd->type_element_ok($search_target [,$locator], $keys, [, $desc ]);
+
+   $twd->type_element_ok( $search_target [,$locator], $keys [, $desc ] );
 
 
 Use L<Selenium::Remote::Driver/find_element> to resolve the C<$search_target>
 Use L<Selenium::Remote::Driver/find_element> to resolve the C<$search_target>
-to a web element, and then type C<$keys> into it, providing an optional test
+to a web element and an optional locator, and then type C<$keys> into it, providing an optional test
 label.
 label.
 
 
-Currently, other finders besides the default are not supported for C<type_ok()>.
 
 
 =cut
 =cut
 
 
 sub type_element_ok {
 sub type_element_ok {
     my $self    = shift;
     my $self    = shift;
-    my $locator = shift;
-    my $keys    = shift;
-    my $desc    = shift;
-    return $self->find_element($locator)->send_keys_ok( $keys, $desc );
+    my $method = 'send_keys_ok'; 
+    return $self->_find_element_with_action($method,@_);
+}
+
+
+=head2 $twd->element_text_is($search_target[,$finder],$expected_text [,$desc]);
+
+    $twd->element_text_is($search_target[,$finder],$expected_text [,$desc]);
+
+=cut
+
+sub element_text_is {
+    my $self = shift; 
+    my $method = 'text_is';
+    return $self->_find_element_with_action($method,@_);
 }
 }
 
 
+=head2 $twd->element_value_is($search_target[,$finder],$expected_value [,$desc]);
 
 
-=head2 $twd->find_element_ok($search_target [, $desc ]);
+    $twd->element_value_is($search_target[,$finder],$expected_value [,$desc]);
+
+=cut
 
 
-   $twd->find_element_ok( $search_target [, $desc ] );
+sub element_value_is {
+    my $self = shift; 
+    my $method = 'value_is';
+    return $self->_find_element_with_action($method,@_);
+}
+
+=head2 $twd->click_element_ok($search_target [,$desc]);
+
+    $twd->click_element_ok($search_target [,$desc]);
+
+Find an element and then click on it.
+
+=cut
+
+sub click_element_ok {
+    my $self = shift; 
+    my $method = 'click_ok';
+    return $self->_find_element_with_action($method,@_);
+}
+
+=head2 $twd->clear_element_ok($search_target [,$desc]);
+
+    $twd->clear_element_ok($search_target [,$desc]);
+
+Find an element and then clear on it.
+
+=cut
+
+sub clear_element_ok {
+    my $self = shift; 
+    my $method = 'clear_ok';
+    return $self->_find_element_with_action($method,@_);
+}
+
+=head2 $twd->is_element_displayed_ok($search_target [,$desc]);
+
+    $twd->is_element_displayed_ok($search_target [,$desc]);
+
+Find an element and check to confirm that it is displayed. (visible)
+
+=cut
+
+sub is_element_displayed_ok {
+    my $self = shift; 
+    my $method = 'is_displayed_ok';
+    return $self->_find_element_with_action($method,@_);
+}
+
+=head2 $twd->is_element_enabled_ok($search_target [,$desc]);
+
+    $twd->is_element_enabled_ok($search_target [,$desc]);
+
+Find an element and check to confirm that it is enabled.
+
+=cut
+
+sub is_element_enabled_ok {
+    my $self = shift; 
+    my $method = 'is_enabled_ok';
+    return $self->_find_element_with_action($method,@_);
+}
+
+
+=head2 $twd->find_element_ok($search_target [,$finder, $desc ]);
+
+   $twd->find_element_ok( $search_target [,$finder, $desc ] );
 
 
 Returns true if C<$search_target> is successfully found on the page. L<$search_target>
 Returns true if C<$search_target> is successfully found on the page. L<$search_target>
-is passed to L<Selenium::Remote::Driver/find_element> using the C<default_finder>. See
-there for more details on the format. Currently, other finders besides the default are not supported
-for C<find_element_ok()>.
+is passed to L<Selenium::Remote::Driver/find_element> using a finder or the C<default_finder>
+if none passed.
+See there for more details on the format for C<find_element_ok()>.
 
 
 =cut
 =cut
 
 
 # Eventually, it would be nice to support other finds like Test::WWW::Selenium does, like this:
 # Eventually, it would be nice to support other finds like Test::WWW::Selenium does, like this:
 # 'xpath=//foo', or 'css=.foo', etc.
 # 'xpath=//foo', or 'css=.foo', etc.
 
 
-=head2 $twd->find_no_element_ok($search_target [, $desc ]);
+=head2 $twd->find_no_element_ok($search_target [,$finder, $desc ]);
 
 
-   $twd->find_no_element_ok( $search_target [, $desc ] );
+   $twd->find_no_element_ok( $search_target [,$finder, $desc ] );
 
 
 Returns true if C<$search_target> is I<not> found on the page. L<$search_target>
 Returns true if C<$search_target> is I<not> found on the page. L<$search_target>
-is passed to L<Selenium::Remote::Driver/find_element> using the C<default_finder>. See
-there for more details on the format. Currently, other finders besides the default are not supported
+is passed to L<Selenium::Remote::Driver/find_element> using a finder or the
+C<default_finder> if none passed.See there for more details on the format. 
 for C<find_no_element_ok()>.
 for C<find_no_element_ok()>.
 
 
 =cut
 =cut
 
 
-sub find_no_element_ok {
-    my $self          = shift;
-    my $search_target = shift;
-    my $desc          = shift;
-    my $rv = 0 ;
-    local $Test::Builder::Level = $Test::Builder::Level + 1;
-    try {
-        $self->find_element($search_target)
-    } catch {
-        $rv = 1 if ($_);
-    };
-    return $self->ok($rv == 1,$desc);
-}
 
 
 =head2 $twd->content_like( $regex [, $desc ] )
 =head2 $twd->content_like( $regex [, $desc ] )
 
 
@@ -348,18 +460,17 @@ sub content_unlike {
     my $self  = shift;
     my $self  = shift;
     my $regex = shift;
     my $regex = shift;
     my $desc  = shift;
     my $desc  = shift;
-
     local $Test::Builder::Level = $Test::Builder::Level + 1;
     local $Test::Builder::Level = $Test::Builder::Level + 1;
 
 
     my $content = $self->get_page_source();
     my $content = $self->get_page_source();
 
 
     if ( not ref $regex eq 'ARRAY' ) {
     if ( not ref $regex eq 'ARRAY' ) {
-        my $desc = qq{Content is unlike "$regex"} if ( not defined $desc );
+        $desc = qq{Content is unlike "$regex"} if ( not defined $desc );
         return unlike_string( $content, $regex, $desc );
         return unlike_string( $content, $regex, $desc );
     }
     }
     elsif ( ref $regex eq 'ARRAY' ) {
     elsif ( ref $regex eq 'ARRAY' ) {
         for my $re (@$regex) {
         for my $re (@$regex) {
-            my $desc = qq{Content is unlike "$re"} if ( not defined $desc );
+            $desc = qq{Content is unlike "$re"} if ( not defined $desc );
             unlike_string( $content, $re, $desc );
             unlike_string( $content, $re, $desc );
         }
         }
     }
     }
@@ -392,12 +503,12 @@ sub body_text_like {
     my $text = $self->get_body();
     my $text = $self->get_body();
 
 
     if ( not ref $regex eq 'ARRAY' ) {
     if ( not ref $regex eq 'ARRAY' ) {
-        my $desc = qq{Text is like "$regex"} if ( not defined $desc );
+        $desc = qq{Text is like "$regex"} if ( not defined $desc );
         return like_string( $text, $regex, $desc );
         return like_string( $text, $regex, $desc );
     }
     }
     elsif ( ref $regex eq 'ARRAY' ) {
     elsif ( ref $regex eq 'ARRAY' ) {
         for my $re (@$regex) {
         for my $re (@$regex) {
-            my $desc = qq{Text is like "$re"} if ( not defined $desc );
+            $desc = qq{Text is like "$re"} if ( not defined $desc );
             like_string( $text, $re, $desc );
             like_string( $text, $re, $desc );
         }
         }
     }
     }
@@ -430,12 +541,12 @@ sub body_text_unlike {
     my $text = $self->get_body();
     my $text = $self->get_body();
 
 
     if ( not ref $regex eq 'ARRAY' ) {
     if ( not ref $regex eq 'ARRAY' ) {
-        my $desc = qq{Text is unlike "$regex"} if ( not defined $desc );
+        $desc = qq{Text is unlike "$regex"} if ( not defined $desc );
         return unlike_string( $text, $regex, $desc );
         return unlike_string( $text, $regex, $desc );
     }
     }
     elsif ( ref $regex eq 'ARRAY' ) {
     elsif ( ref $regex eq 'ARRAY' ) {
         for my $re (@$regex) {
         for my $re (@$regex) {
-            my $desc = qq{Text is unlike "$re"} if ( not defined $desc );
+            $desc = qq{Text is unlike "$re"} if ( not defined $desc );
             unlike_string( $text, $re, $desc );
             unlike_string( $text, $re, $desc );
         }
         }
     }
     }
@@ -467,12 +578,12 @@ sub content_contains {
     my $content = $self->get_page_source();
     my $content = $self->get_page_source();
 
 
     if ( not ref $str eq 'ARRAY' ) {
     if ( not ref $str eq 'ARRAY' ) {
-        my $desc = qq{Content contains "$str"} if ( not defined $desc );
+        $desc = qq{Content contains "$str"} if ( not defined $desc );
         return contains_string( $content, $str, $desc );
         return contains_string( $content, $str, $desc );
     }
     }
     elsif ( ref $str eq 'ARRAY' ) {
     elsif ( ref $str eq 'ARRAY' ) {
         for my $s (@$str) {
         for my $s (@$str) {
-            my $desc = qq{Content contains "$s"} if ( not defined $desc );
+            $desc = qq{Content contains "$s"} if ( not defined $desc );
             contains_string( $content, $s, $desc );
             contains_string( $content, $s, $desc );
         }
         }
     }
     }
@@ -502,12 +613,12 @@ sub content_lacks {
     my $content = $self->get_page_source();
     my $content = $self->get_page_source();
 
 
     if ( not ref $str eq 'ARRAY' ) {
     if ( not ref $str eq 'ARRAY' ) {
-        my $desc = qq{Content lacks "$str"} if ( not defined $desc );
+        $desc = qq{Content lacks "$str"} if ( not defined $desc );
         return lacks_string( $content, $str, $desc );
         return lacks_string( $content, $str, $desc );
     }
     }
     elsif ( ref $str eq 'ARRAY' ) {
     elsif ( ref $str eq 'ARRAY' ) {
         for my $s (@$str) {
         for my $s (@$str) {
-            my $desc = qq{Content lacks "$s"} if ( not defined $desc );
+            $desc = qq{Content lacks "$s"} if ( not defined $desc );
             lacks_string( $content, $s, $desc );
             lacks_string( $content, $s, $desc );
         }
         }
     }
     }
@@ -540,12 +651,12 @@ sub body_text_contains {
     my $text = $self->get_body();
     my $text = $self->get_body();
 
 
     if ( not ref $str eq 'ARRAY' ) {
     if ( not ref $str eq 'ARRAY' ) {
-        my $desc = qq{Text contains "$str"} if ( not defined $desc );
+        $desc = qq{Text contains "$str"} if ( not defined $desc );
         return contains_string( $text, $str, $desc );
         return contains_string( $text, $str, $desc );
     }
     }
     elsif ( ref $str eq 'ARRAY' ) {
     elsif ( ref $str eq 'ARRAY' ) {
         for my $s (@$str) {
         for my $s (@$str) {
-            my $desc = qq{Text contains "$s"} if ( not defined $desc );
+            $desc = qq{Text contains "$s"} if ( not defined $desc );
             contains_string( $text, $s, $desc );
             contains_string( $text, $s, $desc );
         }
         }
     }
     }
@@ -578,90 +689,17 @@ sub body_text_lacks {
     my $text = $self->get_body();
     my $text = $self->get_body();
 
 
     if ( not ref $str eq 'ARRAY' ) {
     if ( not ref $str eq 'ARRAY' ) {
-        my $desc = qq{Text is lacks "$str"} if ( not defined $desc );
+        $desc = qq{Text is lacks "$str"} if ( not defined $desc );
         return lacks_string( $text, $str, $desc );
         return lacks_string( $text, $str, $desc );
     }
     }
     elsif ( ref $str eq 'ARRAY' ) {
     elsif ( ref $str eq 'ARRAY' ) {
         for my $s (@$str) {
         for my $s (@$str) {
-            my $desc = qq{Text is lacks "$s"} if ( not defined $desc );
+            $desc = qq{Text is lacks "$s"} if ( not defined $desc );
             lacks_string( $text, $s, $desc );
             lacks_string( $text, $s, $desc );
         }
         }
     }
     }
 }
 }
 
 
-=head2 $twd->element_text_is($search_target,$expected_text [,$desc]);
-
-    $twd->element_text_is($search_target,$expected_text [,$desc]);
-
-=cut
-
-sub element_text_is {
-    my ( $self, $search_target, $expected, $desc ) = @_;
-    return $self->find_element($search_target)->text_is( $expected, $desc );
-}
-
-=head2 $twd->element_value_is($search_target,$expected_value [,$desc]);
-
-    $twd->element_value_is($search_target,$expected_value [,$desc]);
-
-=cut
-
-sub element_value_is {
-    my ( $self, $search_target, $expected, $desc ) = @_;
-    return $self->find_element($search_target)->value_is( $expected, $desc );
-}
-
-=head2 $twd->click_element_ok($search_target [,$desc]);
-
-    $twd->click_element_ok($search_target [,$desc]);
-
-Find an element and then click on it.
-
-=cut
-
-sub click_element_ok {
-    my ( $self, $search_target, $desc ) = @_;
-    return $self->find_element($search_target)->click_ok($desc);
-}
-
-=head2 $twd->clear_element_ok($search_target [,$desc]);
-
-    $twd->clear_element_ok($search_target [,$desc]);
-
-Find an element and then clear on it.
-
-=cut
-
-sub clear_element_ok {
-    my ( $self, $search_target, $desc ) = @_;
-    return $self->find_element($search_target)->clear_ok($desc);
-}
-
-=head2 $twd->is_element_displayed_ok($search_target [,$desc]);
-
-    $twd->is_element_displayed_ok($search_target [,$desc]);
-
-Find an element and check to confirm that it is displayed. (visible)
-
-=cut
-
-sub is_element_displayed_ok {
-    my ( $self, $search_target, $desc ) = @_;
-    return $self->find_element($search_target)->is_displayed_ok($desc);
-}
-
-=head2 $twd->is_element_enabled_ok($search_target [,$desc]);
-
-    $twd->is_element_enabled_ok($search_target [,$desc]);
-
-Find an element and check to confirm that it is enabled.
-
-=cut
-
-sub is_element_enabled_ok {
-    my ( $self, $search_target, $desc ) = @_;
-    return $self->find_element($search_target)->is_enabled_ok($desc);
-}
 
 
 1;
 1;
 
 

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

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

+ 92 - 7
t/01-driver.t

@@ -13,6 +13,7 @@ use Selenium::Remote::Mock::RemoteConnection;
 use FindBin;
 use FindBin;
 use lib $FindBin::Bin . '/lib';
 use lib $FindBin::Bin . '/lib';
 use TestHarness;
 use TestHarness;
+use Test::Fatal;
 
 
 my $harness = TestHarness->new(
 my $harness = TestHarness->new(
     this_file => $FindBin::Script
     this_file => $FindBin::Script
@@ -20,9 +21,16 @@ my $harness = TestHarness->new(
 my %selenium_args = %{ $harness->base_caps };
 my %selenium_args = %{ $harness->base_caps };
 
 
 my $driver = Selenium::Remote::Driver->new(%selenium_args);
 my $driver = Selenium::Remote::Driver->new(%selenium_args);
-my $website = 'http://localhost:63636';
+my $domain = $harness->domain;
+my $website = $harness->website;
 my $ret;
 my $ret;
 
 
+my $chrome;
+eval { $chrome = Selenium::Remote::Driver->new(
+    %selenium_args,
+    browser_name => 'chrome'
+); };
+
 DESIRED_CAPABILITIES: {
 DESIRED_CAPABILITIES: {
     # We're using a different test method for these because we needed
     # We're using a different test method for these because we needed
     # to inspect payload of the POST to /session, and the method of
     # to inspect payload of the POST to /session, and the method of
@@ -192,6 +200,14 @@ WINDOW: {
     ok(!$@,"Reset implicit wait timeout");
     ok(!$@,"Reset implicit wait timeout");
     $ret = $driver->get("$website/frameset.html");
     $ret = $driver->get("$website/frameset.html");
     $ret = $driver->switch_to_frame('second');
     $ret = $driver->switch_to_frame('second');
+
+  SKIP: {
+        skip 'Cannot rotate desktop browsers', 3;
+        ok($driver->get_orientation eq 'PORTRAIT', 'Can get default orientation');
+        $ret = $driver->set_orientation('LANDSCAPE');
+        ok($ret, 'Can change orientation to LANDSCAPE');
+        ok($driver->get_orientation eq 'LANDSCAPE', 'Can get changed orientation');
+    }
 }
 }
 
 
 COOKIES: {
 COOKIES: {
@@ -202,7 +218,7 @@ COOKIES: {
     pass('Deleting cookies...');
     pass('Deleting cookies...');
     $ret = $driver->get_all_cookies();
     $ret = $driver->get_all_cookies();
     is(@{$ret}, 0, 'Deleted all cookies.');
     is(@{$ret}, 0, 'Deleted all cookies.');
-    $ret = $driver->add_cookie('foo', 'bar', '/', 'localhost', 0);
+    $ret = $driver->add_cookie('foo', 'bar', '/', $domain, 0);
     pass('Adding cookie foo...');
     pass('Adding cookie foo...');
     $ret = $driver->get_all_cookies();
     $ret = $driver->get_all_cookies();
     is(@{$ret}, 1, 'foo cookie added.');
     is(@{$ret}, 1, 'foo cookie added.');
@@ -424,13 +440,8 @@ USER_AGENT: {
 }
 }
 
 
 STORAGE: {
 STORAGE: {
-    my $chrome;
-    my %selenium_chrome_args = ( browser_name => 'chrome');
-    $selenium_chrome_args{remote_conn} = $selenium_args{remote_conn};
-
   SKIP: {
   SKIP: {
         eval {
         eval {
-            $chrome = Selenium::Remote::Driver->new(%selenium_chrome_args);
             $chrome->get($website);
             $chrome->get($website);
         };
         };
 
 
@@ -450,6 +461,80 @@ STORAGE: {
     }
     }
 }
 }
 
 
+HTML5: {
+  SKIP: {
+        skip 'HTML5 Application Cache is not supported by firefox or chrome', 1 if 1;
+        $driver->get($website);
+        my $status = $driver->cache_status;
+        ok($status, 'we can get application cache status');
+    }
+
+  SKIP: {
+        skip 'Geolocation requires Chrome to test', 2 unless $chrome;
+
+        my $ret = $chrome->set_geolocation( location => {
+            latitude => 40.714353,
+            longitude => -74.005973,
+            altitude => 0.056747
+        });
+        ok($ret, 'can set geolocation');
+
+      TODO: {
+            local $TODO = 'GET geolocation has a cast Long to Double error in Chromedriver';
+            my $ret = {};
+            eval { $ret = $chrome->get_geolocation };
+            ok(exists $ret->{location}, 'get_geolocation returns a location dictionary.');
+        }
+    }
+}
+
+LOGS: {
+    $driver->get($website);
+
+    my $types = $driver->get_log_types;
+    ok(scalar @$types >= 4, 'Can get log types');
+    foreach (@$types) {
+        my $log = $driver->get_log($_);
+        ok(defined $log, 'Can get logs from the ' . $_);
+    }
+}
+
+UPLOAD: {
+    #Webdriver only returns the full filename if there isn't any path components in it, so test both cases
+    my $testFile = "UEsDBBQACAAIAFtuNEYAAAAAAAAAAAAAAAAKABUAdXBsb2FkVGVzdFVUCQADjbG+VJ6xvlRVeAQA\n6APoAytJLS4BAFBLBwgMfn/YBgAAAAQAAABQSwECFAMUAAgACABbbjRGDH5/2AYAAAAEAAAACgAN\nAAAAAAAAAAAApIEAAAAAdXBsb2FkVGVzdFVUBQABjbG+VFV4AABQSwUGAAAAAAEAAQBFAAAAUwAA\nAAAA\n";
+    my $otherTestFile = "UEsDBBQACAAIAFtuNEYAAAAAAAAAAAAAAAAMABUAdC91cGxvYWRUZXN0VVQJAAOesb5UnrG+VFV4\nBADoA+gDK0ktLgEAUEsHCAx+f9gGAAAABAAAAFBLAQIUAxQACAAIAFtuNEYMfn/YBgAAAAQAAAAM\nAA0AAAAAAAAAAACkgQAAAAB0L3VwbG9hZFRlc3RVVAUAAZ6xvlRVeAAAUEsFBgAAAAABAAEARwAA\nAFUAAAAAAA==\n";
+
+    like( $driver->upload_file('uploadTest',$testFile),qr/uploadTest$/,'upload_file returns FULL path to the file: cwd');
+    like( $driver->upload_file('t/uploadTest',$otherTestFile),qr/uploadTest$/,'upload_file returns FULL path to the file: subdir');
+
+    my $fake_driver;
+    if ($harness->record) {
+        $fake_driver = $driver;
+    } else {
+        #Going to have to use a custom UA to test this, since bogosity ensues
+        my $ftua = Test::LWP::UserAgent->new;
+        my $fake_header = bless( {'content-type' => 'application/json; charset=utf-8'}, 'HTTP::Headers' );
+        #Seems a bit heavy handed, but shouldn't be a problem.
+        $ftua->map_response(qr{.*}, HTTP::Response->new(200, 'OK', $fake_header, '{"sessionId":"89726c41-409f-421e-95a8-8b1fa482fa33","status":0,"state":"success","value":"/tmp/89726c41-409f-421e-95a8-8b1fa482fa33/upload1105744174029267337file/uploadTest","class":"org.openqa.selenium.remote.Response","hCode":516517658}'));
+        $fake_driver = Selenium::Remote::Driver->new(
+            ua => $ftua
+        );
+
+    }
+
+    #In the case this is not mocked, it tests a real issue (.. in paths), if not, it makes sure the zip/base64 bits at least don't make us explode.
+    like( $fake_driver->upload_file('t/uploadTest'),qr/uploadTest$/,'upload_file: zip/base64 branch' );
+    like( $fake_driver->upload_file('t/../t/uploadTest'),qr/uploadTest$/,'upload_file: zip/base64 branch with .. in path' );
+
+    #Negative tests to verify that our expected behavior codepath is travelled by tests
+    like( exception { $driver->upload_file('@@@SomeFileThatDoesNotExist@@@')},qr/no such file/i,"Passing missing file terminates program");
+    SKIP: {
+        skip 'purposefully excluding this test from the recording', 1
+          if $harness->record;
+        like( exception { $driver->upload_file(__FILE__) },qr/501/,"Passing this file rightly fails due to mock not being present");
+    }
+}
+
 QUIT: {
 QUIT: {
     $ret = $driver->quit();
     $ret = $driver->quit();
     ok((not defined $driver->{'session_id'}), 'Killed the remote session');
     ok((not defined $driver->{'session_id'}), 'Killed the remote session');

+ 3 - 1
t/02-webelement.t

@@ -15,7 +15,9 @@ my $harness = TestHarness->new(
 my %selenium_args = %{ $harness->base_caps };
 my %selenium_args = %{ $harness->base_caps };
 
 
 my $driver = Selenium::Remote::Driver->new(%selenium_args);
 my $driver = Selenium::Remote::Driver->new(%selenium_args);
-my $website = 'http://localhost:63636';
+my $domain = $harness->domain;
+my $website = $harness->website;
+
 $driver->get("$website/formPage.html");
 $driver->get("$website/formPage.html");
 my $ret;
 my $ret;
 my $elem;
 my $elem;

+ 6 - 8
t/03-spec-coverage.t

@@ -1,19 +1,19 @@
 #!perl
 #!perl
+
 use strict;
 use strict;
 use warnings;
 use warnings;
 
 
+use LWP::UserAgent;
+use Selenium::Remote::Commands;
 use Test::More;
 use Test::More;
 
 
 unless($ENV{RELEASE_TESTING}) {
 unless($ENV{RELEASE_TESTING}) {
   plan(skip_all=>"Author tests not required for installation.");
   plan(skip_all=>"Author tests not required for installation.");
 }
 }
 
 
-eval {use LWP::Simple;};
-plan skip_all => "need LWP::Simple" if $@;
-use Selenium::Remote::Commands;
-
 my $uri  = "http://selenium.googlecode.com/svn/wiki/JsonWireProtocol.wiki";
 my $uri  = "http://selenium.googlecode.com/svn/wiki/JsonWireProtocol.wiki";
-my $data = get($uri);
+my $ua = LWP::UserAgent->new;
+my $data = $ua->get($uri)->content;
 plan skip_all => "need internet connection to run spec test" if !$data;
 plan skip_all => "need internet connection to run spec test" if !$data;
 
 
 my $todo_list = {
 my $todo_list = {
@@ -35,6 +35,7 @@ my $todo_list = {
    'POST session/:sessionId/keys'                          => 1,
    'POST session/:sessionId/keys'                          => 1,
    'GET session/:sessionId/location'                       => 1,
    'GET session/:sessionId/location'                       => 1,
    'POST session/:sessionId/location'                      => 1,
    'POST session/:sessionId/location'                      => 1,
+   'POST session/:sessionId/window/:windowHandle/maximize' => 1,
    'GET session/:sessionId/local_storage'                  => 1,
    'GET session/:sessionId/local_storage'                  => 1,
    'POST session/:sessionId/local_storage'                 => 1,
    'POST session/:sessionId/local_storage'                 => 1,
    'DELETE session/:sessionId/local_storage'               => 1,
    'DELETE session/:sessionId/local_storage'               => 1,
@@ -47,9 +48,6 @@ my $todo_list = {
    'GET session/:sessionId/session_storage/key/:key'       => 1,
    'GET session/:sessionId/session_storage/key/:key'       => 1,
    'DELETE session/:sessionId/session_storage/key/:key'    => 1,
    'DELETE session/:sessionId/session_storage/key/:key'    => 1,
    'GET session/:sessionId/session_storage/size'           => 1,
    'GET session/:sessionId/session_storage/size'           => 1,
-   'POST session/:sessionId/log'                           => 1,
-   'GET session/:sessionId/log/types'                      => 1,
-   'GET session/:sessionId/application_cache/status'       => 1,
 };
 };
 my @lines = split(/\n/, $data);
 my @lines = split(/\n/, $data);
 my @methods;
 my @methods;

+ 89 - 0
t/CanStartBinary.t

@@ -0,0 +1,89 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use File::Which qw/which/;
+use Selenium::Chrome;
+use Selenium::Firefox;
+use Selenium::Firefox::Binary;
+use Selenium::PhantomJS;
+use Test::Fatal;
+use Test::More;
+
+unless ( $ENV{RELEASE_TESTING} ) {
+    plan skip_all => "Author tests not required for installation.";
+}
+
+PHANTOMJS: {
+  SKIP: {
+        my $has_phantomjs = which('phantomjs');
+        skip 'Phantomjs binary not found in path', 3
+          unless $has_phantomjs;
+
+        skip 'PhantomJS binary not found in path', 3
+          unless is_proper_phantomjs_available();
+
+        my $phantom = Selenium::PhantomJS->new;
+        is( $phantom->browser_name, 'phantomjs', 'binary phantomjs is okay');
+        isnt( $phantom->port, 4444, 'phantomjs can start up its own binary');
+
+        ok( Selenium::CanStartBinary::probe_port( $phantom->port ), 'the phantomjs binary is listening on its port');
+    }
+}
+
+MANUAL: {
+    ok( exception { PhantomJS->new( binary => '/bad/executable') },
+        'we throw if the user specified binary is not executable');
+
+  SKIP: {
+        my $phantom_binary = which('phantomjs');
+        skip 'PhantomJS needed for manual binary path tests', 2
+          unless $phantom_binary;
+
+        my $manual_phantom = Selenium::PhantomJS->new(
+            binary => $phantom_binary
+        );
+        isnt( $manual_phantom->port, 4444, 'manual phantom can start up user specified binary');
+        ok( Selenium::CanStartBinary::probe_port( $manual_phantom->port ), 'the manual chrome binary is listening on its port');
+    }
+}
+
+CHROME: {
+  SKIP: {
+        my $has_chromedriver = which('chromedriver');
+        skip 'Chrome binary not found in path', 3
+          unless $has_chromedriver;
+
+        my $chrome = Selenium::Chrome->new;
+        ok( $chrome->browser_name eq 'chrome', 'convenience chrome is okay' );
+        isnt( $chrome->port, 4444, 'chrome can start up its own binary');
+
+        ok( Selenium::CanStartBinary::probe_port( $chrome->port ), 'the chrome binary is listening on its port');
+    }
+}
+
+FIREFOX: {
+  SKIP: {
+        skip 'Firefox will not start up on UNIX without a display', 3
+          if ($^O ne 'MSWin32' && ! $ENV{DISPLAY});
+        my $binary = Selenium::Firefox::Binary::firefox_path();
+        skip 'Firefox binary not found in path', 3
+          unless $binary;
+
+        ok(-x $binary, 'we can find some sort of firefox');
+
+        my $firefox = Selenium::Firefox->new;
+        isnt( $firefox->port, 4444, 'firefox can start up its own binary');
+        ok( Selenium::CanStartBinary::probe_port( $firefox->port ), 'the firefox binary is listening on its port');
+    }
+}
+
+sub is_proper_phantomjs_available {
+    my $ver = `phantomjs --version` // '';
+    chomp $ver;
+
+    $ver =~ s/^(\d\.\d).*/$1/;
+    return $ver >= 1.9;
+}
+
+done_testing;

+ 50 - 0
t/Finders.t

@@ -0,0 +1,50 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use Test::More;
+use Selenium::Remote::Driver;
+use FindBin;
+use lib $FindBin::Bin . '/lib';
+use TestHarness;
+
+my $harness = TestHarness->new(
+    this_file => $FindBin::Script
+);
+
+my %selenium_args = %{ $harness->base_caps };
+
+my $driver = Selenium::Remote::Driver->new(%selenium_args);
+$driver->get('http://danielgempesaw.com/Selenium-Remote-Driver/xhtmlTest.html');
+
+# This depends explicitly on the page we're visiting (xhtmlTest.html),
+my %finders = (
+    class => 'navigation',
+    class_name => 'navigation',
+    css => 'html',
+    id => 'linkId',
+    link => 'this goes to the same place',
+    link_text => 'this goes to the same place',
+    name => 'windowOne',
+    partial_link_text => 'this goes to the same',
+    tag_name => 'html',
+    xpath => '//html'
+);
+
+foreach my $by (keys %finders) {
+    my $locator = $finders{$by};
+    my $method = 'find_element_by_' . $by;
+
+    ok($driver->can($method), $method . ':  installed properly');
+    my $elem = $driver->$method($locator);
+    ok($elem, $method . ': finds an element properly');
+    ok($elem->isa('Selenium::Remote::WebElement'), $method . ': element is a WebElement');
+    {
+        # Briefly suppress warning output for prettier tests
+        my $warned = 0;
+        local $SIG{__WARN__} = sub { $warned++ };
+        ok(!$driver->$method('missing') , $method . ': does not croak on unavailable elements');
+        ok($warned, $method . ': unavailable elements throw a warning');
+    }
+}
+done_testing;

+ 16 - 15
t/Firefox-Profile.t

@@ -7,7 +7,7 @@ use Selenium::Remote::Driver;
 use Test::More;
 use Test::More;
 
 
 use MIME::Base64 qw/decode_base64/;
 use MIME::Base64 qw/decode_base64/;
-use Archive::Extract;
+use IO::Uncompress::Unzip qw(unzip $UnzipError);
 use File::Temp;
 use File::Temp;
 use JSON;
 use JSON;
 use Selenium::Remote::Mock::RemoteConnection;
 use Selenium::Remote::Mock::RemoteConnection;
@@ -26,7 +26,8 @@ my $fixture_dir = $FindBin::Bin . '/www/';
 
 
 CUSTOM_EXTENSION_LOADED: {
 CUSTOM_EXTENSION_LOADED: {
     my $profile = Selenium::Remote::Driver::Firefox::Profile->new;
     my $profile = Selenium::Remote::Driver::Firefox::Profile->new;
-    my $website = 'http://localhost:63636';
+    my $domain = $harness->domain;
+    my $website = $harness->website;
     my $mock_encoded_profile = $fixture_dir . 'encoded_profile.b64';
     my $mock_encoded_profile = $fixture_dir . 'encoded_profile.b64';
     my $encoded;
     my $encoded;
 
 
@@ -74,7 +75,7 @@ CUSTOM_EXTENSION_LOADED: {
     # elements)
     # elements)
     $driver->set_implicit_wait_timeout(30000);
     $driver->set_implicit_wait_timeout(30000);
     $driver->find_element("h1", "tag_name");
     $driver->find_element("h1", "tag_name");
-    cmp_ok($driver->get_current_url, '=~', qr/localhost/i,
+    cmp_ok($driver->get_current_url, '=~', qr/$domain/i,
            "profile loaded and preference respected!");
            "profile loaded and preference respected!");
 
 
     $driver->get($website . '/index.html');
     $driver->get($website . '/index.html');
@@ -125,6 +126,15 @@ PREFERENCES: {
             cmp_ok($profile->get_preference($_), "eq", $expected->{$_},
             cmp_ok($profile->get_preference($_), "eq", $expected->{$_},
                    "$_ pref is formatted correctly");
                    "$_ pref is formatted correctly");
         }
         }
+
+        $profile->set_preference(
+            'boolean.true.2' => JSON::true,
+            'boolean.false.2' => JSON::false
+        );
+        is($profile->get_preference('boolean.true.2'), 'true',
+           'format true booleans via set_preference & JSON::true');
+        is($profile->get_preference('boolean.false.2'), 'false',
+           'format false booleans via set_preference & JSON::false');
     }
     }
 
 
   PACK_AND_UNPACK: {
   PACK_AND_UNPACK: {
@@ -132,19 +142,10 @@ PREFERENCES: {
         my $fh = File::Temp->new();
         my $fh = File::Temp->new();
         print $fh decode_base64($encoded);
         print $fh decode_base64($encoded);
         close $fh;
         close $fh;
-        my $zip = Archive::Extract->new(
-            archive => $fh->filename,
-            type => "zip"
-        );
-        my $tempdir = File::Temp->newdir();
-        my $ok = $zip->extract( to => $tempdir );
-        my $outdir = $zip->extract_path;
 
 
-        my $filename = $tempdir . "/user.js";
-        open ($fh, "<", $filename);
-        my (@file) = <$fh>;
-        close ($fh);
-        my $userjs = join('', @file);
+        my $userjs;
+        unzip $fh->filename => \$userjs, Name => "user.js"
+          or die "unzip failed: $UnzipError\n";
 
 
         foreach (keys %$expected) {
         foreach (keys %$expected) {
             my $value = $expected->{$_};
             my $value = $expected->{$_};

+ 3 - 2
t/Remote-Connection.t

@@ -3,7 +3,7 @@
 use strict;
 use strict;
 use warnings;
 use warnings;
 use Test::More;
 use Test::More;
-use Test::Exception;
+use Test::Fatal;
 use Test::LWP::UserAgent;
 use Test::LWP::UserAgent;
 
 
 BEGIN: {
 BEGIN: {
@@ -32,7 +32,8 @@ REDIRECT: {
         url => 'http://localhost/redirect'
         url => 'http://localhost/redirect'
     };
     };
 
 
-    lives_ok(sub { $conn->request($redirect_endpoint) }, '303 redirects no longer kill us');
+    is( exception { $conn->request($redirect_endpoint) }, undef,
+        '303 redirects no longer kill us');
 }
 }
 
 
 
 

+ 116 - 9
t/Test-Selenium-Remote-Driver.t

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

+ 48 - 0
t/Waiter.t

@@ -0,0 +1,48 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use Test::More;
+use Test::Warn;
+use Test::Fatal;
+use Time::Mock throttle => 100;
+use Selenium::Waiter;
+
+SIMPLE_WAIT: {
+    my $ret;
+    waits_ok( sub { $ret = wait_until { 1 } }, '<', 5, 'immediately true returns quickly' );
+    ok($ret == 1, 'return value for a true wait_until is passed up');
+    waits_ok( sub { $ret = wait_until { 0 } }, '>', 25, 'never true expires the timeout' );
+    ok($ret eq '', 'return value for a false wait is an empty string');
+}
+
+EVENTUALLY: {
+    my $ret = 0;
+    waits_ok( sub { wait_until { $ret++ > 2 } }, '>', 2, 'eventually true takes time');
+
+    $ret = 0;
+    my %opts = ( interval => 2, timeout => 5 );
+    waits_ok(
+        sub { wait_until { $ret++; 0 } %opts }, '>', 4,
+        'timeout is respected'
+    );
+    ok(1 <= $ret && $ret <= 3, 'interval option changes iteration speed');
+}
+
+EXCEPTIONS: {
+    my %opts = ( timeout => 2 );
+    warning_is { wait_until { die 'caught!' } %opts } 'caught!',
+      'exceptions usually only warn once';
+}
+
+sub waits_ok  {
+    my ($sub, $cmp, $expected_duration, $test_desc) = @_;
+
+    my $start = time;
+    $sub->();
+    my $elapsed = time - $start;
+
+    cmp_ok($elapsed, $cmp, $expected_duration, $test_desc);
+}
+
+done_testing;

+ 39 - 0
t/convenience.t

@@ -0,0 +1,39 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use Selenium::Chrome;
+use Selenium::Firefox;
+use Selenium::InternetExplorer;
+use Selenium::PhantomJS;
+use Test::More;
+
+use FindBin;
+use lib $FindBin::Bin . '/lib';
+use TestHarness;
+
+my $harness = TestHarness->new(
+    this_file => $FindBin::Script
+);
+
+my %caps = %{ $harness->base_caps };
+$caps{remote_server_addr} = '127.0.0.1';
+delete $caps{browser_name};
+
+my $firefox = Selenium::Firefox->new( %caps );
+ok( $firefox->browser_name eq 'firefox', 'convenience firefox is okay' );
+$firefox->quit;
+
+my $chrome = Selenium::Chrome->new( %caps );
+ok( $chrome->browser_name eq 'chrome', 'convenience chrome is okay' );
+$chrome->quit;
+
+SKIP: {
+    skip 'Can only test IE on windows', 1 unless $^O eq 'MSWin32';
+
+    my $ie = Selenium::InternetExplorer->new( %caps );
+    ok( $ie->browser_name eq 'internet_explorer', 'convenience ie is okay' );
+    $ie->quit;
+}
+
+done_testing;

+ 26 - 28
t/error.t

@@ -3,7 +3,7 @@
 use strict;
 use strict;
 use warnings;
 use warnings;
 use Test::More;
 use Test::More;
-use Test::Exception;
+use Test::Fatal;
 use Test::LWP::UserAgent;
 use Test::LWP::UserAgent;
 use IO::Socket::INET;
 use IO::Socket::INET;
 
 
@@ -25,26 +25,24 @@ UNAVAILABLE_BROWSER: {
         '{"status":13,"sessionId":null,"value":{"message":"The path to..."} }'
         '{"status":13,"sessionId":null,"value":{"message":"The path to..."} }'
     ));
     ));
 
 
-    throws_ok(
-        sub {
-            Selenium::Remote::Driver->new_from_caps(
-                ua => $tua,
-                desired_capabilities => {
-                    browserName => 'chrome'
-                }
-            );
-        }, qr/Could not create new session.*path to/,
-        'Errors in browser configuration are passed to user'
-    );
+    like( exception {
+        Selenium::Remote::Driver->new_from_caps(
+            ua => $tua,
+            desired_capabilities => {
+                browserName => 'chrome'
+            }
+        );
+    }, qr/Could not create new session.*path to/,
+          'Errors in browser configuration are passed to user' );
 }
 }
 
 
 LOCAL: {
 LOCAL: {
-    throws_ok(
-        sub {
-            Selenium::Remote::Driver->new_from_caps( port => 80 );
-        }, qr/Selenium server did not return proper status/,
-        'Error message for not finding a selenium server is helpful'
-    );
+    like( exception {
+        Selenium::Remote::Driver->new_from_caps(
+            port => 80
+        );
+    }, qr/Selenium server did not return proper status/,
+          'Error message for not finding a selenium server is helpful' );
 }
 }
 
 
 SAUCE: {
 SAUCE: {
@@ -59,16 +57,16 @@ SAUCE: {
         skip 'Cannot reach saucelabs for Sauce error case ', 1
         skip 'Cannot reach saucelabs for Sauce error case ', 1
           unless $sock;
           unless $sock;
 
 
-        throws_ok(
-            sub {
-                Selenium::Remote::Driver->new_from_caps(
-                    remote_server_addr => $host,
-                    port => $port,
-                    desired_capabilities => {
-                        browserName => 'invalid'
-                    }
-                );
-            }, qr/Sauce Labs/, 'Saucelabs errors are passed to user');
+        like(exception {
+            Selenium::Remote::Driver->new_from_caps(
+                remote_server_addr => $host,
+                port => $port,
+                desired_capabilities => {
+                    browserName => 'invalid'
+                }
+            );
+        }, qr/Sauce Labs/,
+             'Saucelabs errors are passed to user');
 
 
     }
     }
 }
 }

+ 25 - 0
t/lib/TestHarness.pm

@@ -42,6 +42,16 @@ has calling_file => (
     required => 1
     required => 1
 );
 );
 
 
+=attr record
+
+Optional. Determines whether or not this test run should record new
+mocks, or look up a previous recording to replay against them. If the
+parameter is not used during construction, the default behavior is to
+check for the environment variable WD_MOCKING_RECORD to be defined and
+equal to 1.
+
+=cut
+
 has record => (
 has record => (
     is => 'ro',
     is => 'ro',
     init_args => undef,
     init_args => undef,
@@ -129,6 +139,21 @@ has mock_file => (
     }
     }
 );
 );
 
 
+has website => (
+    is => 'ro',
+    default => sub {
+        my ($self) = @_;
+        my $port = 63636;
+
+        return 'http://' . $self->domain . ':' . $port;
+    }
+);
+
+has domain => (
+    is => 'ro',
+    default => sub { 'localhost' }
+);
+
 sub DEMOLISH {
 sub DEMOLISH {
     my ($self) = @_;
     my ($self) = @_;
     if ($self->record) {
     if ($self->record) {

Fichier diff supprimé car celui-ci est trop grand
+ 19 - 20
t/mock-recordings/01-driver-mock-MSWin32.json


Fichier diff supprimé car celui-ci est trop grand
+ 3 - 4
t/mock-recordings/01-driver-mock-darwin.json


Fichier diff supprimé car celui-ci est trop grand
+ 6 - 9
t/mock-recordings/01-driver-mock-linux.json


Fichier diff supprimé car celui-ci est trop grand
+ 2 - 3
t/mock-recordings/02-webelement-mock-darwin.json


Fichier diff supprimé car celui-ci est trop grand
+ 13 - 18
t/mock-recordings/02-webelement-mock-linux.json


+ 28 - 28
t/mock-recordings/10-switch-to-window-mock-darwin.json

@@ -1,41 +1,41 @@
 {
 {
-   "GET session/0c22e3f0-51f7-48f4-8b7c-7997729a50bd/window_handles {}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:07:59 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 197\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:07:59 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":\"success\",\"value\":[\"{fadff9d4-cbdc-dc49-8341-520b5c91818a}\"],\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1963633841}\n",
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:08:00 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 238\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:00 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":\"success\",\"value\":[\"{fadff9d4-cbdc-dc49-8341-520b5c91818a}\",\"{ef20de3a-4853-f144-ac85-54d82419498c}\"],\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1225013973}\n"
+   "GET session/46aa7fd1-95f1-46d7-8596-befe7ff6a01d/title {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:21 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 200\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:21 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":\"success\",\"value\":\"The Perl Programming Language - www.perl.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":535096297}\n",
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:21 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 200\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:21 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":\"success\",\"value\":\"The Perl Programming Language - www.perl.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":632942286}\n",
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:21 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 210\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:22 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":\"success\",\"value\":\"The Comprehensive Perl Archive Network - www.cpan.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1673698413}\n",
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:22 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 201\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:22 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":\"success\",\"value\":\"The Perl Programming Language - www.perl.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1981091695}\n",
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:22 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 209\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:22 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":\"success\",\"value\":\"The Comprehensive Perl Archive Network - www.cpan.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":567364661}\n",
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:22 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 201\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:22 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":\"success\",\"value\":\"The Perl Programming Language - www.perl.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1323608166}\n"
    ],
    ],
-   "POST session/0c22e3f0-51f7-48f4-8b7c-7997729a50bd/window {\"name\":\"cpanorg\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:08:00 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 159\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:00 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1450435222}\n"
+   "POST session/46aa7fd1-95f1-46d7-8596-befe7ff6a01d/window {\"name\":\"{00a43d5a-ae7a-6e41-bab5-b543628d0812}\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:21 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 159\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:21 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":2091283074}\n"
    ],
    ],
-   "POST session/0c22e3f0-51f7-48f4-8b7c-7997729a50bd/execute {\"args\":[],\"script\":\"$(window.open('http://cpan.org/', 'cpanorg'))\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:07:59 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 159\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:00 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1629117058}\n"
-   ],
-   "POST session/0c22e3f0-51f7-48f4-8b7c-7997729a50bd/window {\"name\":\"{fadff9d4-cbdc-dc49-8341-520b5c91818a}\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:08:00 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 159\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:00 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1014129120}\n"
+   "POST session/46aa7fd1-95f1-46d7-8596-befe7ff6a01d/url {\"url\":\"http://perl.org/\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:19 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 158\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:21 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":995585163}\n"
    ],
    ],
    "POST session {\"desiredCapabilities\":{\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"javascriptEnabled\":true,\"platform\":\"ANY\",\"version\":null}}" : [
    "POST session {\"desiredCapabilities\":{\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"javascriptEnabled\":true,\"platform\":\"ANY\",\"version\":null}}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:07:54 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 545\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:07:58 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":null,\"value\":{\"platform\":\"MAC\",\"javascriptEnabled\":true,\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"rotatable\":false,\"locationContextEnabled\":true,\"webdriver.remote.sessionid\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"version\":\"32.0.3\",\"databaseEnabled\":true,\"cssSelectorsEnabled\":true,\"handlesAlerts\":true,\"webStorageEnabled\":true,\"nativeEvents\":false,\"applicationCacheEnabled\":true,\"takesScreenshot\":true},\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1387044607}\n"
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:14 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 545\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:19 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":null,\"value\":{\"platform\":\"MAC\",\"javascriptEnabled\":true,\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"rotatable\":false,\"locationContextEnabled\":true,\"webdriver.remote.sessionid\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"version\":\"34.0.5\",\"databaseEnabled\":true,\"cssSelectorsEnabled\":true,\"handlesAlerts\":true,\"webStorageEnabled\":true,\"nativeEvents\":false,\"applicationCacheEnabled\":true,\"takesScreenshot\":true},\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1345975507}\n"
+   ],
+   "POST session/46aa7fd1-95f1-46d7-8596-befe7ff6a01d/execute {\"args\":[],\"script\":\"$(window.open('http://cpan.org/', 'cpanorg'))\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:21 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 159\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:21 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1714323681}\n"
    ],
    ],
-   "DELETE session/0c22e3f0-51f7-48f4-8b7c-7997729a50bd {}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:08:00 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 159\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:00 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1935882440}\n"
+   "POST session/46aa7fd1-95f1-46d7-8596-befe7ff6a01d/execute {\"args\":[],\"script\":\"return window.name = 'perlorg';\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:21 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 163\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:21 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":\"success\",\"value\":\"perlorg\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":452687837}\n"
    ],
    ],
-   "GET session/0c22e3f0-51f7-48f4-8b7c-7997729a50bd/title {}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:07:59 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 201\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:07:59 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":\"success\",\"value\":\"The Perl Programming Language - www.perl.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1434205134}\n",
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:08:00 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 200\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:00 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":\"success\",\"value\":\"The Perl Programming Language - www.perl.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":935491704}\n",
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:08:00 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 210\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:00 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":\"success\",\"value\":\"The Comprehensive Perl Archive Network - www.cpan.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1263174034}\n",
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:08:00 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 200\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:00 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":\"success\",\"value\":\"The Perl Programming Language - www.perl.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":722361133}\n",
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:08:00 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 210\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:00 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":\"success\",\"value\":\"The Comprehensive Perl Archive Network - www.cpan.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":2033285398}\n",
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:08:00 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 199\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:00 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":\"success\",\"value\":\"The Perl Programming Language - www.perl.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":65977332}\n"
+   "DELETE session/46aa7fd1-95f1-46d7-8596-befe7ff6a01d {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:22 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 159\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:22 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1669332173}\n"
    ],
    ],
-   "POST session/0c22e3f0-51f7-48f4-8b7c-7997729a50bd/window {\"name\":\"perlorg\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:08:00 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 159\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:00 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1968635741}\n"
+   "POST session/46aa7fd1-95f1-46d7-8596-befe7ff6a01d/window {\"name\":\"cpanorg\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:22 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 159\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:22 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1436872962}\n"
    ],
    ],
-   "POST session/0c22e3f0-51f7-48f4-8b7c-7997729a50bd/url {\"url\":\"http://perl.org/\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:07:58 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 159\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:07:59 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1107966583}\n"
+   "GET session/46aa7fd1-95f1-46d7-8596-befe7ff6a01d/window_handles {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:21 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 196\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:21 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":\"success\",\"value\":[\"{7d7301fb-0ba6-e649-aac2-5fdd32b1fc06}\"],\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":753721152}\n",
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:21 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 237\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:21 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":\"success\",\"value\":[\"{7d7301fb-0ba6-e649-aac2-5fdd32b1fc06}\",\"{00a43d5a-ae7a-6e41-bab5-b543628d0812}\"],\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":693034498}\n"
    ],
    ],
-   "POST session/0c22e3f0-51f7-48f4-8b7c-7997729a50bd/execute {\"args\":[],\"script\":\"return window.name = 'perlorg';\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:07:59 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 163\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:07:59 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":\"success\",\"value\":\"perlorg\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":361227765}\n"
+   "POST session/46aa7fd1-95f1-46d7-8596-befe7ff6a01d/window {\"name\":\"perlorg\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:22 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 159\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:22 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1242399465}\n"
    ],
    ],
-   "POST session/0c22e3f0-51f7-48f4-8b7c-7997729a50bd/window {\"name\":\"{ef20de3a-4853-f144-ac85-54d82419498c}\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:08:00 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 158\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:00 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0c22e3f0-51f7-48f4-8b7c-7997729a50bd\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":972578961}\n"
+   "POST session/46aa7fd1-95f1-46d7-8596-befe7ff6a01d/window {\"name\":\"{7d7301fb-0ba6-e649-aac2-5fdd32b1fc06}\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:22 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 158\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:22 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"46aa7fd1-95f1-46d7-8596-befe7ff6a01d\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":568541048}\n"
    ]
    ]
 }
 }

+ 28 - 28
t/mock-recordings/10-switch-to-window-mock-linux.json

@@ -1,41 +1,41 @@
 {
 {
-   "POST session/20dc20a9-b96c-437b-a61e-41748e18f185/window {\"name\":\"{2740fe00-52ec-4af2-bcb7-d35c76573605}\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:20 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 157\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:20 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":null,\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":24331845}\n"
+   "POST session/7636e703-b0ff-4853-9e8f-04cdcd197392/execute {\"args\":[],\"script\":\"return window.name = 'perlorg';\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:47 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 161\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:47 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":\"success\",\"value\":\"perlorg\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":2291165}\n"
    ],
    ],
-   "POST session/20dc20a9-b96c-437b-a61e-41748e18f185/window {\"name\":\"{1075249a-12bb-4a12-bc59-1743aca55275}\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:19 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 156\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:19 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":null,\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":4399653}\n"
+   "POST session/7636e703-b0ff-4853-9e8f-04cdcd197392/window {\"name\":\"perlorg\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:48 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 156\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:48 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":8938795}\n"
    ],
    ],
-   "GET session/20dc20a9-b96c-437b-a61e-41748e18f185/window_handles {}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:19 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 195\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:19 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":[\"{2740fe00-52ec-4af2-bcb7-d35c76573605}\"],\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":27331543}\n",
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:19 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 235\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:19 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":[\"{2740fe00-52ec-4af2-bcb7-d35c76573605}\",\"{1075249a-12bb-4a12-bc59-1743aca55275}\"],\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":2369167}\n"
-   ],
-   "POST session/20dc20a9-b96c-437b-a61e-41748e18f185/window {\"name\":\"cpanorg\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:20 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 157\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:20 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":null,\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":25525711}\n"
+   "POST session/7636e703-b0ff-4853-9e8f-04cdcd197392/execute {\"args\":[],\"script\":\"$(window.open('http://cpan.org/', 'cpanorg'))\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:47 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 157\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:47 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":26466764}\n"
    ],
    ],
    "POST session {\"desiredCapabilities\":{\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"javascriptEnabled\":true,\"platform\":\"ANY\",\"version\":null}}" : [
    "POST session {\"desiredCapabilities\":{\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"javascriptEnabled\":true,\"platform\":\"ANY\",\"version\":null}}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:06 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 577\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:16 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":{\"platform\":\"LINUX\",\"javascriptEnabled\":true,\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"rotatable\":false,\"locationContextEnabled\":true,\"webdriver.remote.sessionid\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"version\":\"27.0.1\",\"databaseEnabled\":true,\"cssSelectorsEnabled\":true,\"handlesAlerts\":true,\"browserConnectionEnabled\":true,\"webStorageEnabled\":true,\"nativeEvents\":false,\"applicationCacheEnabled\":true,\"takesScreenshot\":true},\"state\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":17834946}\n"
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:43 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 577\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:45 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":null,\"value\":{\"platform\":\"LINUX\",\"javascriptEnabled\":true,\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"rotatable\":false,\"locationContextEnabled\":true,\"webdriver.remote.sessionid\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"version\":\"27.0.1\",\"databaseEnabled\":true,\"cssSelectorsEnabled\":true,\"handlesAlerts\":true,\"browserConnectionEnabled\":true,\"webStorageEnabled\":true,\"nativeEvents\":false,\"applicationCacheEnabled\":true,\"takesScreenshot\":true},\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":19471836}\n"
+   ],
+   "GET session/7636e703-b0ff-4853-9e8f-04cdcd197392/window_handles {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:47 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 193\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:47 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":\"success\",\"value\":[\"{6a65d322-dbf8-454a-9456-48e9825cdb7d}\"],\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":355015}\n",
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:47 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 236\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:47 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":\"success\",\"value\":[\"{6a65d322-dbf8-454a-9456-48e9825cdb7d}\",\"{df147381-f1a4-4322-87e1-48063cb40e37}\"],\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":21793902}\n"
    ],
    ],
-   "POST session/20dc20a9-b96c-437b-a61e-41748e18f185/execute {\"args\":[],\"script\":\"$(window.open('http://cpan.org/', 'cpanorg'))\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:19 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 156\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:19 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":null,\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":2944777}\n"
+   "POST session/7636e703-b0ff-4853-9e8f-04cdcd197392/window {\"name\":\"{df147381-f1a4-4322-87e1-48063cb40e37}\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:47 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 156\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:47 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1989637}\n"
    ],
    ],
-   "POST session/20dc20a9-b96c-437b-a61e-41748e18f185/window {\"name\":\"perlorg\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:20 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 156\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:20 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":null,\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":7348436}\n"
+   "POST session/7636e703-b0ff-4853-9e8f-04cdcd197392/window {\"name\":\"cpanorg\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:47 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 156\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:47 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":2083787}\n"
    ],
    ],
-   "DELETE session/20dc20a9-b96c-437b-a61e-41748e18f185 {}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:20 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 156\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:20 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":null,\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":3350041}\n"
+   "DELETE session/7636e703-b0ff-4853-9e8f-04cdcd197392 {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:48 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 157\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:48 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":24960974}\n"
    ],
    ],
-   "GET session/20dc20a9-b96c-437b-a61e-41748e18f185/title {}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:19 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 198\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:19 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":\"The Perl Programming Language - www.perl.org\",\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":8590277}\n",
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:19 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 199\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:19 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":\"The Perl Programming Language - www.perl.org\",\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":25589988}\n",
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:19 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 208\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:20 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":\"The Comprehensive Perl Archive Network - www.cpan.org\",\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":30157936}\n",
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:20 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 198\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:20 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":\"The Perl Programming Language - www.perl.org\",\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":7798062}\n",
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:20 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 208\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:20 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":\"The Comprehensive Perl Archive Network - www.cpan.org\",\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":28986901}\n",
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:20 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 199\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:20 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":\"The Perl Programming Language - www.perl.org\",\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":13394224}\n"
+   "POST session/7636e703-b0ff-4853-9e8f-04cdcd197392/window {\"name\":\"{6a65d322-dbf8-454a-9456-48e9825cdb7d}\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:47 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 156\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:47 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":2488371}\n"
    ],
    ],
-   "POST session/20dc20a9-b96c-437b-a61e-41748e18f185/execute {\"args\":[],\"script\":\"return window.name = 'perlorg';\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:19 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 162\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:19 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":\"perlorg\",\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":21429697}\n"
+   "GET session/7636e703-b0ff-4853-9e8f-04cdcd197392/title {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:47 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 199\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:47 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":\"success\",\"value\":\"The Perl Programming Language - www.perl.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":29125428}\n",
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:47 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 198\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:47 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":\"success\",\"value\":\"The Perl Programming Language - www.perl.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":6953343}\n",
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:47 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 208\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:47 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":\"success\",\"value\":\"The Comprehensive Perl Archive Network - www.cpan.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":31987789}\n",
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:47 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 199\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:47 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":\"success\",\"value\":\"The Perl Programming Language - www.perl.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":29357411}\n",
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:47 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 208\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:48 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":\"success\",\"value\":\"The Comprehensive Perl Archive Network - www.cpan.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":19292607}\n",
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:48 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 198\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:48 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":\"success\",\"value\":\"The Perl Programming Language - www.perl.org\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":5237134}\n"
    ],
    ],
-   "POST session/20dc20a9-b96c-437b-a61e-41748e18f185/url {\"url\":\"http://perl.org/\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:16 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 156\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:19 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"20dc20a9-b96c-437b-a61e-41748e18f185\",\"value\":null,\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":4075103}\n"
+   "POST session/7636e703-b0ff-4853-9e8f-04cdcd197392/url {\"url\":\"http://perl.org/\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:45 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 157\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:47 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"7636e703-b0ff-4853-9e8f-04cdcd197392\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":28723770}\n"
    ]
    ]
 }
 }

+ 20 - 0
t/mock-recordings/convenience-mock-MSWin32.json

@@ -0,0 +1,20 @@
+{
+   "DELETE session/281c1464-e8df-46d3-8ff2-7f0c80ff0541 {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 18 Feb 2015 13:52:39 GMT\nServer: Jetty/5.1.x (Windows 7/6.1 amd64 java/1.8.0_31\nContent-Length: 158\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 18 Feb 2015 13:52:39 GMT\nClient-Peer: ::1:4444\nClient-Response-Num: 1\n\n{\"state\":\"success\",\"sessionId\":\"281c1464-e8df-46d3-8ff2-7f0c80ff0541\",\"hCode\":881303524,\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"status\":0}\n"
+   ],
+   "POST session {\"desiredCapabilities\":{\"acceptSslCerts\":true,\"browserName\":\"internet_explorer\",\"javascriptEnabled\":true,\"platform\":\"WINDOWS\",\"version\":\"\"}}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 18 Feb 2015 13:52:42 GMT\nServer: Jetty/5.1.x (Windows 7/6.1 amd64 java/1.8.0_31\nContent-Length: 818\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 18 Feb 2015 13:52:44 GMT\nClient-Peer: ::1:4444\nClient-Response-Num: 1\n\n{\"state\":null,\"sessionId\":\"45d7a89f-b0db-488b-a2d0-09bfa606a79d\",\"hCode\":1696048499,\"value\":{\"browserAttachTimeout\":0,\"enablePersistentHover\":true,\"ie.forceCreateProcessApi\":false,\"ie.usePerProcessProxy\":false,\"ignoreZoomSetting\":false,\"handlesAlerts\":true,\"version\":\"11\",\"platform\":\"WINDOWS\",\"nativeEvents\":true,\"ie.ensureCleanSession\":false,\"elementScrollBehavior\":0,\"ie.browserCommandLineSwitches\":\"\",\"webdriver.remote.sessionid\":\"45d7a89f-b0db-488b-a2d0-09bfa606a79d\",\"requireWindowFocus\":false,\"browserName\":\"internet explorer\",\"initialBrowserUrl\":\"http://localhost:42913/\",\"takesScreenshot\":true,\"javascriptEnabled\":true,\"ignoreProtectedModeSettings\":false,\"enableElementCacheCleanup\":true,\"cssSelectorsEnabled\":true,\"unexpectedAlertBehaviour\":\"dismiss\"},\"class\":\"org.openqa.selenium.remote.Response\",\"status\":0}\n"
+   ],
+   "POST session {\"desiredCapabilities\":{\"acceptSslCerts\":true,\"browserName\":\"chrome\",\"javascriptEnabled\":true,\"platform\":\"ANY\",\"version\":\"\"}}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 18 Feb 2015 13:52:39 GMT\nServer: Jetty/5.1.x (Windows 7/6.1 amd64 java/1.8.0_31\nContent-Length: 726\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 18 Feb 2015 13:52:41 GMT\nClient-Peer: ::1:4444\nClient-Response-Num: 1\n\n{\"state\":null,\"sessionId\":\"3bf9fe23-aa6c-4a4f-968b-b04f56b59464\",\"hCode\":204950963,\"value\":{\"applicationCacheEnabled\":false,\"rotatable\":false,\"mobileEmulationEnabled\":false,\"chrome\":{\"userDataDir\":\"C:\\\\Users\\\\Daniel\\\\AppData\\\\Local\\\\Temp\\\\scoped_dir3696_7705\"},\"takesHeapSnapshot\":true,\"databaseEnabled\":false,\"handlesAlerts\":true,\"version\":\"40.0.2214.94\",\"platform\":\"XP\",\"browserConnectionEnabled\":false,\"nativeEvents\":true,\"acceptSslCerts\":true,\"webdriver.remote.sessionid\":\"3bf9fe23-aa6c-4a4f-968b-b04f56b59464\",\"locationContextEnabled\":true,\"webStorageEnabled\":true,\"browserName\":\"chrome\",\"takesScreenshot\":true,\"javascriptEnabled\":true,\"cssSelectorsEnabled\":true},\"class\":\"org.openqa.selenium.remote.Response\",\"status\":0}\n"
+   ],
+   "DELETE session/3bf9fe23-aa6c-4a4f-968b-b04f56b59464 {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 18 Feb 2015 13:52:41 GMT\nServer: Jetty/5.1.x (Windows 7/6.1 amd64 java/1.8.0_31\nContent-Length: 157\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 18 Feb 2015 13:52:42 GMT\nClient-Peer: ::1:4444\nClient-Response-Num: 1\n\n{\"state\":\"success\",\"sessionId\":\"3bf9fe23-aa6c-4a4f-968b-b04f56b59464\",\"hCode\":80186222,\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"status\":0}\n"
+   ],
+   "DELETE session/45d7a89f-b0db-488b-a2d0-09bfa606a79d {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 18 Feb 2015 13:52:44 GMT\nServer: Jetty/5.1.x (Windows 7/6.1 amd64 java/1.8.0_31\nContent-Length: 159\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 18 Feb 2015 13:52:45 GMT\nClient-Peer: ::1:4444\nClient-Response-Num: 1\n\n{\"state\":\"success\",\"sessionId\":\"45d7a89f-b0db-488b-a2d0-09bfa606a79d\",\"hCode\":1563676228,\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"status\":0}\n"
+   ],
+   "POST session {\"desiredCapabilities\":{\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"javascriptEnabled\":true,\"platform\":\"ANY\",\"version\":\"\"}}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 18 Feb 2015 13:52:37 GMT\nServer: Jetty/5.1.x (Windows 7/6.1 amd64 java/1.8.0_31\nContent-Length: 580\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 18 Feb 2015 13:52:39 GMT\nClient-Peer: ::1:4444\nClient-Response-Num: 1\n\n{\"state\":null,\"sessionId\":\"281c1464-e8df-46d3-8ff2-7f0c80ff0541\",\"hCode\":482940564,\"value\":{\"applicationCacheEnabled\":true,\"rotatable\":false,\"handlesAlerts\":true,\"databaseEnabled\":true,\"version\":\"27.0.1\",\"platform\":\"WINDOWS\",\"browserConnectionEnabled\":true,\"nativeEvents\":false,\"acceptSslCerts\":true,\"webdriver.remote.sessionid\":\"281c1464-e8df-46d3-8ff2-7f0c80ff0541\",\"webStorageEnabled\":true,\"locationContextEnabled\":true,\"browserName\":\"firefox\",\"takesScreenshot\":true,\"javascriptEnabled\":true,\"cssSelectorsEnabled\":true},\"class\":\"org.openqa.selenium.remote.Response\",\"status\":0}\n"
+   ]
+}

+ 14 - 0
t/mock-recordings/convenience-mock-darwin.json

@@ -0,0 +1,14 @@
+{
+   "DELETE session/ffa689f5-d71b-42fc-9d54-46ba96f46c07 {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Mon, 26 Jan 2015 00:39:22 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 158\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Mon, 26 Jan 2015 00:39:22 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"ffa689f5-d71b-42fc-9d54-46ba96f46c07\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":307272646}\n"
+   ],
+   "POST session {\"desiredCapabilities\":{\"acceptSslCerts\":true,\"browserName\":\"chrome\",\"javascriptEnabled\":true,\"platform\":\"ANY\",\"version\":\"\"}}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Mon, 26 Jan 2015 00:39:22 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 744\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Mon, 26 Jan 2015 00:39:23 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0bb63c2d-038d-4b2a-84dd-ed913c0f9e7c\",\"status\":0,\"state\":null,\"value\":{\"platform\":\"MAC\",\"acceptSslCerts\":true,\"javascriptEnabled\":true,\"browserName\":\"chrome\",\"chrome\":{\"userDataDir\":\"/var/folders/19/fy4zlkw13c160qyl6h5px_dh55vrly/T/.org.chromium.Chromium.7X4hmM\"},\"rotatable\":false,\"locationContextEnabled\":true,\"mobileEmulationEnabled\":false,\"webdriver.remote.sessionid\":\"0bb63c2d-038d-4b2a-84dd-ed913c0f9e7c\",\"version\":\"40.0.2214.91\",\"takesHeapSnapshot\":true,\"cssSelectorsEnabled\":true,\"databaseEnabled\":false,\"handlesAlerts\":true,\"browserConnectionEnabled\":false,\"nativeEvents\":true,\"webStorageEnabled\":true,\"applicationCacheEnabled\":false,\"takesScreenshot\":true},\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":85267270}\n"
+   ],
+   "POST session {\"desiredCapabilities\":{\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"javascriptEnabled\":true,\"platform\":\"ANY\",\"version\":\"\"}}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Mon, 26 Jan 2015 00:39:19 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 545\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Mon, 26 Jan 2015 00:39:22 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"ffa689f5-d71b-42fc-9d54-46ba96f46c07\",\"status\":0,\"state\":null,\"value\":{\"platform\":\"MAC\",\"javascriptEnabled\":true,\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"rotatable\":false,\"locationContextEnabled\":true,\"webdriver.remote.sessionid\":\"ffa689f5-d71b-42fc-9d54-46ba96f46c07\",\"version\":\"34.0.5\",\"databaseEnabled\":true,\"cssSelectorsEnabled\":true,\"handlesAlerts\":true,\"webStorageEnabled\":true,\"nativeEvents\":false,\"applicationCacheEnabled\":true,\"takesScreenshot\":true},\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1080275328}\n"
+   ],
+   "DELETE session/0bb63c2d-038d-4b2a-84dd-ed913c0f9e7c {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Mon, 26 Jan 2015 00:39:23 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 159\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Mon, 26 Jan 2015 00:39:23 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"0bb63c2d-038d-4b2a-84dd-ed913c0f9e7c\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":2132036725}\n"
+   ]
+}

+ 14 - 0
t/mock-recordings/convenience-mock-linux.json

@@ -0,0 +1,14 @@
+{
+   "DELETE session/101f1644-09ec-4501-8858-5451574cd8e3 {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sun, 01 Feb 2015 04:25:59 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 156\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sun, 01 Feb 2015 04:25:59 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"101f1644-09ec-4501-8858-5451574cd8e3\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":2313854}\n"
+   ],
+   "POST session {\"desiredCapabilities\":{\"acceptSslCerts\":true,\"browserName\":\"chrome\",\"javascriptEnabled\":true,\"platform\":\"ANY\",\"version\":\"\"}}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sun, 01 Feb 2015 04:25:59 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 698\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sun, 01 Feb 2015 04:26:00 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"be2c5bf8-64ab-4470-8971-6317a9822c60\",\"status\":0,\"state\":null,\"value\":{\"platform\":\"LINUX\",\"acceptSslCerts\":true,\"javascriptEnabled\":true,\"browserName\":\"chrome\",\"chrome\":{\"userDataDir\":\"/tmp/.com.google.Chrome.pJgqtA\"},\"rotatable\":false,\"locationContextEnabled\":true,\"mobileEmulationEnabled\":false,\"webdriver.remote.sessionid\":\"be2c5bf8-64ab-4470-8971-6317a9822c60\",\"version\":\"40.0.2214.94\",\"takesHeapSnapshot\":true,\"cssSelectorsEnabled\":true,\"databaseEnabled\":false,\"handlesAlerts\":true,\"browserConnectionEnabled\":false,\"nativeEvents\":true,\"webStorageEnabled\":true,\"applicationCacheEnabled\":false,\"takesScreenshot\":true},\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":21990978}\n"
+   ],
+   "POST session {\"desiredCapabilities\":{\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"javascriptEnabled\":true,\"platform\":\"ANY\",\"version\":\"\"}}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sun, 01 Feb 2015 04:25:57 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 577\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sun, 01 Feb 2015 04:25:59 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"101f1644-09ec-4501-8858-5451574cd8e3\",\"status\":0,\"state\":null,\"value\":{\"platform\":\"LINUX\",\"javascriptEnabled\":true,\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"rotatable\":false,\"locationContextEnabled\":true,\"webdriver.remote.sessionid\":\"101f1644-09ec-4501-8858-5451574cd8e3\",\"version\":\"27.0.1\",\"databaseEnabled\":true,\"cssSelectorsEnabled\":true,\"handlesAlerts\":true,\"browserConnectionEnabled\":true,\"webStorageEnabled\":true,\"nativeEvents\":false,\"applicationCacheEnabled\":true,\"takesScreenshot\":true},\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":22283134}\n"
+   ],
+   "DELETE session/be2c5bf8-64ab-4470-8971-6317a9822c60 {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sun, 01 Feb 2015 04:26:00 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 157\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sun, 01 Feb 2015 04:26:01 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"be2c5bf8-64ab-4470-8971-6317a9822c60\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":16791543}\n"
+   ]
+}

Fichier diff supprimé car celui-ci est trop grand
+ 8 - 0
t/mock-recordings/finders-mock-MSWin32.json


Fichier diff supprimé car celui-ci est trop grand
+ 2 - 0
t/mock-recordings/finders-mock-darwin.json


Fichier diff supprimé car celui-ci est trop grand
+ 2 - 0
t/mock-recordings/finders-mock-linux.json


Fichier diff supprimé car celui-ci est trop grand
+ 6 - 6
t/mock-recordings/firefox-profile-mock-darwin.json


Fichier diff supprimé car celui-ci est trop grand
+ 6 - 9
t/mock-recordings/firefox-profile-mock-linux.json


+ 12 - 12
t/mock-recordings/test-selenium-remote-driver-google-mock-darwin.json

@@ -1,20 +1,20 @@
 {
 {
-   "GET session/bd8a6e01-32b2-4c40-b96b-6af25a394754/title {}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:08:08 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 162\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:08 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"bd8a6e01-32b2-4c40-b96b-6af25a394754\",\"status\":0,\"state\":\"success\",\"value\":\"Google\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":845292969}\n"
+   "DELETE session/e54f6f37-692a-4b18-b2c0-acee4b00db98 {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:26 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 159\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:26 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"e54f6f37-692a-4b18-b2c0-acee4b00db98\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1883757406}\n"
    ],
    ],
-   "POST session/bd8a6e01-32b2-4c40-b96b-6af25a394754/url {\"url\":\"http://www.google.com\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:08:07 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 158\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:08 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"bd8a6e01-32b2-4c40-b96b-6af25a394754\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":234481742}\n"
-   ],
-   "DELETE session/bd8a6e01-32b2-4c40-b96b-6af25a394754 {}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:08:08 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 159\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:08 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"bd8a6e01-32b2-4c40-b96b-6af25a394754\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1184195640}\n"
+   "POST session/e54f6f37-692a-4b18-b2c0-acee4b00db98/url {\"url\":\"http://www.google.com\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:25 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 158\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:26 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"e54f6f37-692a-4b18-b2c0-acee4b00db98\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":829766234}\n"
    ],
    ],
    "POST session {\"desiredCapabilities\":{\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"javascriptEnabled\":false,\"platform\":\"ANY\",\"version\":null}}" : [
    "POST session {\"desiredCapabilities\":{\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"javascriptEnabled\":false,\"platform\":\"ANY\",\"version\":null}}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:07:54 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 544\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:07 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"bd8a6e01-32b2-4c40-b96b-6af25a394754\",\"status\":0,\"state\":null,\"value\":{\"platform\":\"MAC\",\"javascriptEnabled\":true,\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"rotatable\":false,\"locationContextEnabled\":true,\"webdriver.remote.sessionid\":\"bd8a6e01-32b2-4c40-b96b-6af25a394754\",\"version\":\"32.0.3\",\"databaseEnabled\":true,\"cssSelectorsEnabled\":true,\"handlesAlerts\":true,\"webStorageEnabled\":true,\"nativeEvents\":false,\"applicationCacheEnabled\":true,\"takesScreenshot\":true},\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":656518097}\n"
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:14 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 544\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:25 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"e54f6f37-692a-4b18-b2c0-acee4b00db98\",\"status\":0,\"state\":null,\"value\":{\"platform\":\"MAC\",\"javascriptEnabled\":true,\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"rotatable\":false,\"locationContextEnabled\":true,\"webdriver.remote.sessionid\":\"e54f6f37-692a-4b18-b2c0-acee4b00db98\",\"version\":\"34.0.5\",\"databaseEnabled\":true,\"cssSelectorsEnabled\":true,\"handlesAlerts\":true,\"webStorageEnabled\":true,\"nativeEvents\":false,\"applicationCacheEnabled\":true,\"takesScreenshot\":true},\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":653167242}\n"
+   ],
+   "POST session/e54f6f37-692a-4b18-b2c0-acee4b00db98/element {\"using\":\"xpath\",\"value\":\"//body\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:26 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 169\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:26 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"e54f6f37-692a-4b18-b2c0-acee4b00db98\",\"status\":0,\"state\":\"success\",\"value\":{\"ELEMENT\":\"0\"},\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":943097115}\n"
    ],
    ],
-   "POST session/bd8a6e01-32b2-4c40-b96b-6af25a394754/element {\"using\":\"xpath\",\"value\":\"//body\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:08:08 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 170\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:08 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"bd8a6e01-32b2-4c40-b96b-6af25a394754\",\"status\":0,\"state\":\"success\",\"value\":{\"ELEMENT\":\"0\"},\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1020548632}\n"
+   "GET session/e54f6f37-692a-4b18-b2c0-acee4b00db98/title {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:26 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 162\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:26 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"e54f6f37-692a-4b18-b2c0-acee4b00db98\",\"status\":0,\"state\":\"success\",\"value\":\"Google\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":206684361}\n"
    ],
    ],
-   "GET session/bd8a6e01-32b2-4c40-b96b-6af25a394754/element/0/text {}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:08:08 GMT\nServer: Jetty/5.1.x (Mac OS X/10.9.5 x86_64 java/1.7.0_67\nContent-Length: 356\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:08:08 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"bd8a6e01-32b2-4c40-b96b-6af25a394754\",\"status\":0,\"state\":\"success\",\"value\":\"+You\\nGmail\\nImages\\nSign in\\nGoogle SearchI\\u0027m Feeling Lucky\\nGet real-time results and the latest news from the 2014 Midterm Elections\\nPrivacy \\u0026 Terms Settings\\nAdvertising Business About\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1601623645}\n"
+   "GET session/e54f6f37-692a-4b18-b2c0-acee4b00db98/element/0/text {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Sat, 24 Jan 2015 18:42:26 GMT\nServer: Jetty/5.1.x (Mac OS X/10.10.1 x86_64 java/1.7.0_67\nContent-Length: 274\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Sat, 24 Jan 2015 18:42:26 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"e54f6f37-692a-4b18-b2c0-acee4b00db98\",\"status\":0,\"state\":\"success\",\"value\":\"+You\\nGmail\\nImages\\nSign in\\nGoogle SearchI\\u0027m Feeling Lucky\\nPrivacy Terms Settings\\nAdvertising Business About\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":1994517208}\n"
    ]
    ]
 }
 }

+ 12 - 12
t/mock-recordings/test-selenium-remote-driver-google-mock-linux.json

@@ -1,20 +1,20 @@
 {
 {
-   "GET session/74b05200-870b-421c-8ecc-48d7fca92bcb/element/0/text {}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:16 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 344\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:17 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"74b05200-870b-421c-8ecc-48d7fca92bcb\",\"value\":\"+You\\nGmail\\nImages\\nSign in\\nGoogle SearchI'm Feeling Lucky\\nGet real-time results and the latest news from the 2014 Midterm Elections\\nPrivacy & Terms Settings\\nAdvertising Business About\",\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":10983873}\n"
+   "GET session/3f90768c-7a6b-426a-9453-94612bc1b5fe/title {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:54 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 161\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:54 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"3f90768c-7a6b-426a-9453-94612bc1b5fe\",\"status\":0,\"state\":\"success\",\"value\":\"Google\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":28986901}\n"
    ],
    ],
-   "POST session/74b05200-870b-421c-8ecc-48d7fca92bcb/url {\"url\":\"http://www.google.com\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:13 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 157\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:16 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"74b05200-870b-421c-8ecc-48d7fca92bcb\",\"value\":null,\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":28907098}\n"
+   "POST session/3f90768c-7a6b-426a-9453-94612bc1b5fe/element {\"using\":\"xpath\",\"value\":\"//body\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:54 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 167\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:54 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"3f90768c-7a6b-426a-9453-94612bc1b5fe\",\"status\":0,\"state\":\"success\",\"value\":{\"ELEMENT\":\"0\"},\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":7348436}\n"
    ],
    ],
-   "GET session/74b05200-870b-421c-8ecc-48d7fca92bcb/title {}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:16 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 161\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:16 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"74b05200-870b-421c-8ecc-48d7fca92bcb\",\"value\":\"Google\",\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":26993204}\n"
+   "POST session {\"desiredCapabilities\":{\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"javascriptEnabled\":false,\"platform\":\"ANY\",\"version\":null}}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:43 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 577\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:53 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"3f90768c-7a6b-426a-9453-94612bc1b5fe\",\"status\":0,\"state\":null,\"value\":{\"platform\":\"LINUX\",\"javascriptEnabled\":true,\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"rotatable\":false,\"locationContextEnabled\":true,\"webdriver.remote.sessionid\":\"3f90768c-7a6b-426a-9453-94612bc1b5fe\",\"version\":\"27.0.1\",\"databaseEnabled\":true,\"cssSelectorsEnabled\":true,\"handlesAlerts\":true,\"browserConnectionEnabled\":true,\"webStorageEnabled\":true,\"nativeEvents\":false,\"applicationCacheEnabled\":true,\"takesScreenshot\":true},\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":19003540}\n"
    ],
    ],
-   "DELETE session/74b05200-870b-421c-8ecc-48d7fca92bcb {}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:17 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 157\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:17 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"74b05200-870b-421c-8ecc-48d7fca92bcb\",\"value\":null,\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":10558334}\n"
+   "GET session/3f90768c-7a6b-426a-9453-94612bc1b5fe/element/0/text {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:54 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 363\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:55 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"3f90768c-7a6b-426a-9453-94612bc1b5fe\",\"status\":0,\"state\":\"success\",\"value\":\"+You\\nGmail\\nImages\\nSign in\\nGoogle SearchI\\u0027m Feeling Lucky\\nLive! Watch President Obama\\u0027s State of the Union address. Tonight on YouTube at 9p ET\\nPrivacy Terms Settings\\nAdvertising Business About\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":5194611}\n"
    ],
    ],
-   "POST session {\"desiredCapabilities\":{\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"javascriptEnabled\":false,\"platform\":\"ANY\",\"version\":null}}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:06 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 577\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:13 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"74b05200-870b-421c-8ecc-48d7fca92bcb\",\"value\":{\"platform\":\"LINUX\",\"javascriptEnabled\":true,\"acceptSslCerts\":true,\"browserName\":\"firefox\",\"rotatable\":false,\"locationContextEnabled\":true,\"webdriver.remote.sessionid\":\"74b05200-870b-421c-8ecc-48d7fca92bcb\",\"version\":\"27.0.1\",\"databaseEnabled\":true,\"cssSelectorsEnabled\":true,\"handlesAlerts\":true,\"browserConnectionEnabled\":true,\"webStorageEnabled\":true,\"nativeEvents\":false,\"applicationCacheEnabled\":true,\"takesScreenshot\":true},\"state\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":10999907}\n"
+   "DELETE session/3f90768c-7a6b-426a-9453-94612bc1b5fe {}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:55 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 157\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:55 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"3f90768c-7a6b-426a-9453-94612bc1b5fe\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":13293943}\n"
    ],
    ],
-   "POST session/74b05200-870b-421c-8ecc-48d7fca92bcb/element {\"using\":\"xpath\",\"value\":\"//body\"}" : [
-      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Wed, 05 Nov 2014 04:44:16 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 167\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Wed, 05 Nov 2014 04:44:16 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"status\":0,\"sessionId\":\"74b05200-870b-421c-8ecc-48d7fca92bcb\",\"value\":{\"ELEMENT\":\"0\"},\"state\":\"success\",\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":4324892}\n"
+   "POST session/3f90768c-7a6b-426a-9453-94612bc1b5fe/url {\"url\":\"http://www.google.com\"}" : [
+      "HTTP/1.1 200 OK\nCache-Control: no-cache\nCache-Control: no-cache\nConnection: close\nDate: Tue, 20 Jan 2015 21:32:53 GMT\nServer: Jetty/5.1.x (Linux/3.2.0-23-generic-pae i386 java/1.7.0_51\nContent-Length: 157\nContent-Type: application/json; charset=utf-8\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nExpires: Thu, 01 Jan 1970 00:00:00 GMT\nClient-Date: Tue, 20 Jan 2015 21:32:54 GMT\nClient-Peer: 127.0.0.1:4444\nClient-Response-Num: 1\n\n{\"sessionId\":\"3f90768c-7a6b-426a-9453-94612bc1b5fe\",\"status\":0,\"state\":\"success\",\"value\":null,\"class\":\"org.openqa.selenium.remote.Response\",\"hCode\":25525711}\n"
    ]
    ]
 }
 }

+ 0 - 0
t/uploadTest


Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff