Bladeren bron

Merge branch 'dev' in anticipation of 0.18

gempesaw 11 jaren geleden
bovenliggende
commit
9a637ee954

+ 2 - 0
.gitignore

@@ -6,3 +6,5 @@ pm_to_blib
 cover_db
 *.komodo*
 *.sublime*
+.build
+Selenium-Remote-Driver-*

+ 14 - 0
.travis.yml

@@ -0,0 +1,14 @@
+language: perl
+perl:
+   - 5.12
+   - 5.16
+before_install:
+   - export AUTOMATED_TESTING=1 HARNESS_OPTIONS=j10:c HARNESS_TIMER=1
+   - git config --global user.name "TravisCI"
+   - git config --global user.email $HOSTNAME":not-for-mail@travis-ci.org"
+install:
+   - cpanm --quiet --notest --skip-satisfied Dist::Zilla
+   - "dzil authordeps | grep -vP '[^\\w:]' | xargs -n 5 -P 10 cpanm --quiet --notest --skip-satisfied"
+   - "dzil listdeps   | grep -vP '[^\\w:]' | cpanm --verbose --skip-satisfied"
+script:
+   - dzil smoke --release --author

+ 15 - 9
Changes

@@ -1,6 +1,9 @@
 Revision history for Selenium-Remote-Driver
 
-0.18   7-26-2013
+0.18   3-28-2014
+        - New maintainer: Daniel Gempesaw <gempesaw@gmail.com>
+        - Fix failing tests in CPAN distribution!
+
         [THINGS THAT MIGHT BREAK YOUR CODE]
         - We now 'croak' instead of returning error strings in a number of
           cases where invalid method calls were made. This should make
@@ -15,7 +18,7 @@ Revision history for Selenium-Remote-Driver
 
         [NEW FEATURES]
         - Merged Test::WebDriver into this distribution as Test::Selenium::Remote::Driver.
-          Several updates to it are included since the last release. (Mark Stosberg)   
+          Several updates to it are included since the last release. (Mark Stosberg)
 
         - The existence of most (or all?) key testing methods in
           Test::Selenium::Remote::Driver is now documented, rather than leaving it to users
@@ -32,7 +35,7 @@ Revision history for Selenium-Remote-Driver
              type_element_ok   - find an element and type into it.
              element_text_is   - find an element and check the text.
              element_value_is  - find an element and check the value.
-             click_element_ok  - find an element and click on it. 
+             click_element_ok  - find an element and click on it.
              clear_element_ok  - find an element and clear it.
              is_element_displayed_ok - find an element and check to see if it's displayed.
              is_element_enabled_ok - find an element and check to see if it's enabled.
@@ -50,7 +53,7 @@ Revision history for Selenium-Remote-Driver
            https://github.com/aivaturi/Selenium-Remote-Driver/pull/49
 
         - new() now accepts a 'webelement_class' option to allow you to specify an alternate WebElement class.
-          This could be used to add testing methods or functionality for WebElements.  
+          This could be used to add testing methods or functionality for WebElements.
 
         - Shortcut methods absorbed from Test::WebDriver into Selenium::Remote::Driver:
 
@@ -59,9 +62,9 @@ Revision history for Selenium-Remote-Driver
           get_path() Returns the path part of the current URL.
 
         - New short method to save a screenshot to a file: $driver->capture_screenshot($filename).
-          The name was chosen for compatibility with a WWW::Selenium method of the same name.  
+          The name was chosen for compatibility with a WWW::Selenium method of the same name.
 
-        - Removed 'get_location()' as an alias in Test::Selenium::Remote::Driver. 
+        - Removed 'get_location()' as an alias in Test::Selenium::Remote::Driver.
           Please use the more specific 'get_current_url()' instead.
           It's a more specific name that matches the Selenium API method name.
 
@@ -98,6 +101,10 @@ Revision history for Selenium-Remote-Driver
         - Updated ChangeLog to reflect that debug_on() and debug_off() methods were added in 0.16.
 
         [INTERNALS]
+        - Switch S::R::D to Moo; this should not have any adverse impacts, as our API is unchanged
+
+        - Take advantage of additional Dist::Zilla plugins (travis, commitbuild, autoprereqs, etc)
+
         - MIME::Base64 is now only loaded if it used for a file upload or taking a screenshot.
           This will create a minor speed-up for common cases which don't use these features.
 
@@ -168,7 +175,7 @@ Revision history for Selenium-Remote-Driver
         - Added method get_sessions()
         - Fixed issue #11
         - Fixed documentation from issue #21
-        
+
 0.13   2-8-2012
         - Added support for key events in send_keys() method
         - Added methods to get/set window position/size
@@ -187,7 +194,7 @@ Revision history for Selenium-Remote-Driver
 
 0.11   8-16-2011
   This is quite a large list for this release and will be
-  the first cpan release. The later releases will hopefully 
+  the first cpan release. The later releases will hopefully
   happen often and won't be quite as large.
 
         - subroutine calls carp when an error occurs or when
@@ -238,4 +245,3 @@ Revision history for Selenium-Remote-Driver
         - Added POD for Driver & WebElement
 0.01    2-21-2010
         First version, released on an unsuspecting world.
-

+ 205 - 0
TAGS

@@ -0,0 +1,205 @@
+
+driver-example.pl,24
+my $driver driver5,72
+
+lib/Selenium/Remote/Commands.pm,229
+package Selenium::Remote::Commands;1,0
+sub get_url Selenium::Remote::Commands::get_url295,10501
+sub get_method Selenium::Remote::Commands::get_method300,10598
+sub get_params Selenium::Remote::Commands::get_params306,10750
+
+lib/Selenium/Remote/Driver.pm,4728
+package Selenium::Remote::Driver;1,0
+sub BUILD Selenium::Remote::Driver::BUILD287,10696
+sub DESTROY Selenium::Remote::Driver::DESTROY298,10967
+sub _execute_command Selenium::Remote::Driver::_execute_command307,11334
+sub new_session Selenium::Remote::Driver::new_session347,12822
+sub debug_on Selenium::Remote::Driver::debug_on396,14358
+sub debug_off Selenium::Remote::Driver::debug_off411,14539
+sub get_sessions Selenium::Remote::Driver::get_sessions433,14963
+sub status Selenium::Remote::Driver::status454,15377
+sub get_alert_text Selenium::Remote::Driver::get_alert_text471,15692
+sub send_keys_to_active_element Selenium::Remote::Driver::send_keys_to_active_element504,16607
+sub send_keys_to_alert Selenium::Remote::Driver::send_keys_to_alert519,16914
+sub send_keys_to_prompt Selenium::Remote::Driver::send_keys_to_prompt540,17340
+sub accept_alert Selenium::Remote::Driver::accept_alert558,17753
+sub dismiss_alert Selenium::Remote::Driver::dismiss_alert577,18187
+sub mouse_move_to_location Selenium::Remote::Driver::mouse_move_to_location604,19232
+sub move_to Selenium::Remote::Driver::move_to617,19538
+sub get_capabilities Selenium::Remote::Driver::get_capabilities635,19827
+sub set_timeout Selenium::Remote::Driver::set_timeout661,20658
+sub set_async_script_timeout Selenium::Remote::Driver::set_async_script_timeout687,21410
+sub set_implicit_wait_timeout Selenium::Remote::Driver::set_implicit_wait_timeout719,22424
+sub pause Selenium::Remote::Driver::pause737,22845
+sub close Selenium::Remote::Driver::close759,23287
+sub quit Selenium::Remote::Driver::quit775,23509
+sub get_current_window_handle Selenium::Remote::Driver::get_current_window_handle795,23842
+sub get_window_handles Selenium::Remote::Driver::get_window_handles820,24418
+sub get_window_size Selenium::Remote::Driver::get_window_size843,24890
+sub get_window_position Selenium::Remote::Driver::get_window_position867,25445
+sub get_current_url Selenium::Remote::Driver::get_current_url887,25845
+sub navigate Selenium::Remote::Driver::navigate906,26162
+sub get Selenium::Remote::Driver::get924,26370
+sub get_title Selenium::Remote::Driver::get_title944,26691
+sub go_back Selenium::Remote::Driver::go_back960,26945
+sub go_forward Selenium::Remote::Driver::go_forward976,27204
+sub refresh Selenium::Remote::Driver::refresh992,27431
+sub has_javascript Selenium::Remote::Driver::has_javascript1008,27702
+sub execute_async_script Selenium::Remote::Driver::execute_async_script1047,28930
+sub execute_script Selenium::Remote::Driver::execute_script1111,30910
+sub _convert_to_webelement Selenium::Remote::Driver::_convert_to_webelement1143,31923
+sub screenshot Selenium::Remote::Driver::screenshot1184,32883
+sub capture_screenshot Selenium::Remote::Driver::capture_screenshot1204,33309
+sub available_engines Selenium::Remote::Driver::available_engines1230,33865
+sub switch_to_frame Selenium::Remote::Driver::switch_to_frame1256,34636
+sub switch_to_window Selenium::Remote::Driver::switch_to_window1294,35601
+sub get_speed Selenium::Remote::Driver::get_speed1318,36141
+sub set_speed Selenium::Remote::Driver::set_speed1341,36699
+sub set_window_position Selenium::Remote::Driver::set_window_position1369,37314
+sub set_window_size Selenium::Remote::Driver::set_window_size1402,38113
+sub get_all_cookies Selenium::Remote::Driver::get_all_cookies1437,39052
+sub add_cookie Selenium::Remote::Driver::add_cookie1462,39528
+sub delete_all_cookies Selenium::Remote::Driver::delete_all_cookies1501,40406
+sub delete_cookie_named Selenium::Remote::Driver::delete_cookie_named1522,40846
+sub get_page_source Selenium::Remote::Driver::get_page_source1544,41281
+sub find_element Selenium::Remote::Driver::find_element1574,42195
+sub find_elements Selenium::Remote::Driver::find_elements1632,44116
+sub find_child_element Selenium::Remote::Driver::find_child_element1708,46765
+sub find_child_elements Selenium::Remote::Driver::find_child_elements1769,48977
+sub get_active_element Selenium::Remote::Driver::get_active_element1824,50726
+sub send_modifier Selenium::Remote::Driver::send_modifier1860,51650
+sub compare_elements Selenium::Remote::Driver::compare_elements1891,52314
+sub click Selenium::Remote::Driver::click1918,52897
+sub double_click Selenium::Remote::Driver::double_click1945,53528
+sub button_down Selenium::Remote::Driver::button_down1964,54015
+sub button_up Selenium::Remote::Driver::button_up1983,54446
+sub upload_file Selenium::Remote::Driver::upload_file2006,55083
+sub get_text Selenium::Remote::Driver::get_text2031,55779
+sub get_body Selenium::Remote::Driver::get_body2047,56066
+sub get_path Selenium::Remote::Driver::get_path2062,56281
+
+lib/Selenium/Remote/ErrorHandler.pm,118
+package Selenium::Remote::ErrorHandler;1,0
+sub process_error Selenium::Remote::ErrorHandler::process_error115,4454
+
+lib/Selenium/Remote/RemoteConnection.pm,258
+package Selenium::Remote::RemoteConnection;1,0
+sub BUILD Selenium::Remote::RemoteConnection::BUILD31,461
+sub request Selenium::Remote::RemoteConnection::request52,1017
+sub _process_response Selenium::Remote::RemoteConnection::_process_response94,2099
+
+lib/Selenium/Remote/WDKeys.pm,38
+package Selenium::Remote::WDKeys;1,0
+
+lib/Selenium/Remote/WebElement.pm,1318
+package Selenium::Remote::WebElement;1,0
+sub click Selenium::Remote::WebElement::click48,894
+sub submit Selenium::Remote::WebElement::submit65,1229
+sub send_keys Selenium::Remote::WebElement::send_keys96,1902
+sub is_selected Selenium::Remote::WebElement::is_selected120,2412
+sub set_selected Selenium::Remote::WebElement::set_selected138,2769
+sub toggle Selenium::Remote::WebElement::toggle160,3235
+sub is_enabled Selenium::Remote::WebElement::is_enabled179,3565
+sub get_element_location Selenium::Remote::WebElement::get_element_location199,4003
+sub get_element_location_in_view Selenium::Remote::WebElement::get_element_location_in_view222,4618
+sub get_tag_name Selenium::Remote::WebElement::get_tag_name241,4986
+sub clear Selenium::Remote::WebElement::clear257,5262
+sub get_attribute Selenium::Remote::WebElement::get_attribute280,5719
+sub get_value Selenium::Remote::WebElement::get_value306,6284
+sub is_displayed Selenium::Remote::WebElement::is_displayed324,6558
+sub drag Selenium::Remote::WebElement::drag346,7052
+sub get_size Selenium::Remote::WebElement::get_size373,7643
+sub get_text Selenium::Remote::WebElement::get_text392,7951
+sub get_css_attribute Selenium::Remote::WebElement::get_css_attribute417,8555
+sub describe Selenium::Remote::WebElement::describe440,9015
+
+lib/Test/Selenium/Remote/Driver.pm,1817
+package Test::Selenium::Remote::Driver;3,26
+my $Test Test15,302
+my %comparator comparator18,367
+my $comparator_keys comparator_keys24,491
+my %no_locator no_locator27,583
+sub no_locator Test::Selenium::Remote::Driver::no_locator31,741
+sub AUTOLOAD Test::Selenium::Remote::Driver::AUTOLOAD37,842
+sub error_callback Test::Selenium::Remote::Driver::error_callback131,4297
+sub new Test::Selenium::Remote::Driver::new172,5411
+sub server_is_running Test::Selenium::Remote::Driver::server_is_running198,6194
+sub type_element_ok Test::Selenium::Remote::Driver::type_element_ok305,8512
+sub find_no_element_ok Test::Selenium::Remote::Driver::find_no_element_ok339,9674
+sub content_like Test::Selenium::Remote::Driver::content_like363,10336
+sub content_unlike Test::Selenium::Remote::Driver::content_unlike398,11343
+sub body_text_like Test::Selenium::Remote::Driver::body_text_like436,12439
+sub body_text_unlike Test::Selenium::Remote::Driver::body_text_unlike474,13516
+sub content_contains Test::Selenium::Remote::Driver::content_contains511,14509
+sub content_lacks Test::Selenium::Remote::Driver::content_lacks546,15499
+sub body_text_contains Test::Selenium::Remote::Driver::body_text_contains584,16571
+sub body_text_lacks Test::Selenium::Remote::Driver::body_text_lacks622,17632
+sub element_text_is Test::Selenium::Remote::Driver::element_text_is649,18319
+sub element_value_is Test::Selenium::Remote::Driver::element_value_is660,18619
+sub click_element_ok Test::Selenium::Remote::Driver::click_element_ok673,18928
+sub clear_element_ok Test::Selenium::Remote::Driver::clear_element_ok686,19217
+sub is_element_displayed_ok Test::Selenium::Remote::Driver::is_element_displayed_ok699,19551
+sub is_element_enabled_ok Test::Selenium::Remote::Driver::is_element_enabled_ok712,19884
+
+lib/Test/Selenium/Remote/WebElement.pm,1973
+package Test::Selenium::Remote::WebElement;1,0
+sub has_args Test::Selenium::Remote::WebElement::has_args13,285
+sub _check_method Test::Selenium::Remote::WebElement::_check_method23,466
+sub _check_ok Test::Selenium::Remote::WebElement::_check_ok43,1008
+sub clear_ok Test::Selenium::Remote::WebElement::clear_ok60,1365
+sub click_ok Test::Selenium::Remote::WebElement::click_ok65,1447
+sub submit_ok Test::Selenium::Remote::WebElement::submit_ok70,1529
+sub is_selected_ok Test::Selenium::Remote::WebElement::is_selected_ok75,1613
+sub is_enabled_ok Test::Selenium::Remote::WebElement::is_enabled_ok80,1707
+sub is_displayed_ok Test::Selenium::Remote::WebElement::is_displayed_ok85,1799
+sub send_keys_ok Test::Selenium::Remote::WebElement::send_keys_ok90,1895
+sub text_is Test::Selenium::Remote::WebElement::text_is97,1987
+sub text_isnt Test::Selenium::Remote::WebElement::text_isnt102,2082
+sub text_like Test::Selenium::Remote::WebElement::text_like107,2181
+sub text_unlike Test::Selenium::Remote::WebElement::text_unlike112,2277
+sub tag_name_is Test::Selenium::Remote::WebElement::tag_name_is117,2377
+sub tag_name_isnt Test::Selenium::Remote::WebElement::tag_name_isnt122,2480
+sub tag_name_like Test::Selenium::Remote::WebElement::tag_name_like127,2587
+sub tag_name_unlike Test::Selenium::Remote::WebElement::tag_name_unlike132,2691
+sub value_is Test::Selenium::Remote::WebElement::value_is137,2799
+sub value_isnt Test::Selenium::Remote::WebElement::value_isnt142,2896
+sub value_like Test::Selenium::Remote::WebElement::value_like147,2997
+sub value_unlike Test::Selenium::Remote::WebElement::value_unlike152,3095
+sub attribute_is Test::Selenium::Remote::WebElement::attribute_is157,3197
+sub attribute_isnt Test::Selenium::Remote::WebElement::attribute_isnt162,3302
+sub attribute_like Test::Selenium::Remote::WebElement::attribute_like167,3411
+sub attribute_unlike Test::Selenium::Remote::WebElement::attribute_unlike172,3517
+
+t/bin/generate-recordings.pl,82
+sub startServer main::startServer16,409
+sub killServer main::killServer24,605
+
+t/http-server.pl,62
+my $server server10,191
+my $server_root server_root17,326
+
+t/lib/MockSeleniumWebDriver.pm,371
+package t::lib::MockSeleniumWebDriver;1,0
+sub save_recording t::lib::MockSeleniumWebDriver::save_recording10,134
+sub load_recording t::lib::MockSeleniumWebDriver::load_recording17,270
+sub register t::lib::MockSeleniumWebDriver::register25,436
+sub psgi_app t::lib::MockSeleniumWebDriver::psgi_app45,933
+sub DESTROY t::lib::MockSeleniumWebDriver::DESTROY95,2583
+
+t/Test-Selenium-Remote-WebElement.t,0
+
+t/Test-Selenium-Remote-Driver.t,0
+
+t/Test-Selenium-Remote-Driver-google.t,0
+
+t/pod.t,0
+
+t/04-commands-implemented.t,0
+
+t/03-spec-coverage.t,0
+
+t/02-webelement.t,0
+
+t/01-driver.t,0
+
+t/00-load.t,0

+ 17 - 6
dist.ini

@@ -1,25 +1,38 @@
 name = Selenium-Remote-Driver
 version = 0.18
-author = Aditya Ivaturi <ivaturi@gmail.com>
-author = Luke Closs <cpan@5thplane.com>
-author = Mark Stosberg <mark@stosberg.com>
+author = 'Luke Closs <cpan@5thplane.com>'
+author = 'Daniel Gempesaw <gempesaw@gmail.com>'
+author = 'Aditya Ivaturi <ivaturi@gmail.com>'
+author = 'Mark Stosberg <mark@stosberg.com>'
+
 license = Apache_2_0
+copyright_holder = Daniel Gempesaw
 
-copyright_holder = Aditya Ivaturi
+[TravisYML]
+build_branch = /^build/
+notify_email = 1
+perl_version = 5.12 5.16
 
 [Git::Check]
 [Git::Commit]
 [Git::Push]
+[Git::CommitBuild]
 
 [AutoPrereqs]
+
 [GatherDir]
+include_dotfiles = 1
+
 [MakeMaker]
 [ManifestSkip]
 [Manifest]
 [MetaJSON]
 [MetaYAML]
 [PkgVersion]
+
 [PruneCruft]
+except = \.travis.yml
+
 [PodCoverageTests]
 [PodSyntaxTests]
 [PodVersion]
@@ -28,8 +41,6 @@ copyright_holder = Aditya Ivaturi
 [ConfirmRelease]
 [UploadToCPAN]
 
-[Prereqs / BuildRequires]
-
 [Prereqs / RuntimeRequires]
 perl = 5.010
 

+ 300 - 292
lib/Selenium/Remote/Commands.pm

@@ -1,310 +1,318 @@
 package Selenium::Remote::Commands;
 
-use strict;
-use warnings;
+use Moo;
 
-sub new {
-    my $class = shift;
-    
-    # http://code.google.com/p/selenium/wiki/JsonWireProtocol
-    my $self = {
-        'status'     => {
-                          'method' => 'GET',
-                          'url'    => 'status'
-        },
-        'newSession' => {
-                          'method' => 'POST',
-                          'url'    => 'session'
-        },
-        'getSessions' => {
-                          'method' => 'GET',
-                          'url'    => 'sessions'
-        },
-        'getCapabilities' => {
-                          'method' => 'GET',
-                          'url'    => 'session/:sessionId'
-        },
-        'setTimeout' => {
-                        'method' => 'POST',
-                        'url'    => 'session/:sessionId/timeouts'
-        },
-        'setAsyncScriptTimeout' => {
-                        'method' => 'POST',
-                        'url'    => 'session/:sessionId/timeouts/async_script'
-        },
-        'setImplicitWaitTimeout' => {
-                        'method' => 'POST',
-                        'url'    => 'session/:sessionId/timeouts/implicit_wait'
-        },
-        'quit' => {
-                    'method' => 'DELETE',
-                    'url'    => "session/:sessionId"
-        },
-        'getCurrentWindowHandle' => {
-                 'method' => 'GET',
-                 'url' => "session/:sessionId/window_handle"
-        },
-        'getWindowHandles' => {
+has '_cmds' => (
+    is      => 'lazy',
+    reader  => 'get_cmds',
+    builder => sub {
+        return {
+            'status' => {
                 'method' => 'GET',
-                'url' => "session/:sessionId/window_handles"
-        },
-        'getWindowSize' => {
+                'url'    => 'status'
+            },
+            'newSession' => {
+                'method' => 'POST',
+                'url'    => 'session'
+            },
+            'getSessions' => {
+                'method' => 'GET',
+                'url'    => 'sessions'
+            },
+            'getCapabilities' => {
+                'method' => 'GET',
+                'url'    => 'session/:sessionId'
+            },
+            'setTimeout' => {
+                'method' => 'POST',
+                'url'    => 'session/:sessionId/timeouts'
+            },
+            'setAsyncScriptTimeout' => {
+                'method' => 'POST',
+                'url'    => 'session/:sessionId/timeouts/async_script'
+            },
+            'setImplicitWaitTimeout' => {
+                'method' => 'POST',
+                'url'    => 'session/:sessionId/timeouts/implicit_wait'
+            },
+            'quit' => {
+                'method' => 'DELETE',
+                'url'    => "session/:sessionId"
+            },
+            'getCurrentWindowHandle' => {
+                'method' => 'GET',
+                'url'    => "session/:sessionId/window_handle"
+            },
+            'getWindowHandles' => {
+                'method' => 'GET',
+                'url'    => "session/:sessionId/window_handles"
+            },
+            'getWindowSize' => {
+                'method' => 'GET',
+                'url'    => "session/:sessionId/window/:windowHandle/size"
+            },
+            'getWindowPosition' => {
+                'method' => 'GET',
+                'url'    => "session/:sessionId/window/:windowHandle/position"
+            },
+            'setWindowSize' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/window/:windowHandle/size"
+            },
+            'setWindowPosition' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/window/:windowHandle/position"
+            },
+            'getCurrentUrl' => {
+                'method' => 'GET',
+                'url'    => "session/:sessionId/url"
+            },
+            'get' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/url"
+            },
+            'goForward' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/forward"
+            },
+            'goBack' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/back"
+            },
+            'refresh' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/refresh"
+            },
+            'executeScript' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/execute"
+            },
+            'executeAsyncScript' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/execute_async"
+            },
+            'screenshot' => {
+                'method' => 'GET',
+                'url'    => "session/:sessionId/screenshot"
+            },
+            'availableEngines' => {
+                'method' => 'GET',
+                'url'    => "session/:sessionId/ime/available_engines"
+            },
+            'switchToFrame' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/frame"
+            },
+            'switchToWindow' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/window"
+            },
+            'getAllCookies' => {
+                'method' => 'GET',
+                'url'    => "session/:sessionId/cookie"
+            },
+            'addCookie' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/cookie"
+            },
+            'deleteAllCookies' => {
+                'method' => 'DELETE',
+                'url'    => "session/:sessionId/cookie"
+            },
+            'deleteCookieNamed' => {
+                'method' => 'DELETE',
+                'url'    => "session/:sessionId/cookie/:name"
+            },
+            'getPageSource' => {
+                'method' => 'GET',
+                'url'    => "session/:sessionId/source"
+            },
+            'getTitle' => {
+                'method' => 'GET',
+                'url'    => "session/:sessionId/title"
+            },
+            'findElement' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/element"
+            },
+            'findElements' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/elements"
+            },
+            'getActiveElement' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/element/active"
+            },
+            'describeElement' => {
+                'method' => 'GET',
+                'url'    => "session/:sessionId/element/:id"
+            },
+            'findChildElement' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/element/:id/element"
+            },
+            'findChildElements' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/element/:id/elements"
+            },
+            'clickElement' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/element/:id/click"
+            },
+            'submitElement' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/element/:id/submit"
+            },
+            'sendKeysToElement' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/element/:id/value"
+            },
+            'sendKeysToActiveElement' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/keys"
+            },
+            'sendModifier' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/modifier"
+            },
+            'isElementSelected' => {
+                'method' => 'GET',
+                'url'    => "session/:sessionId/element/:id/selected"
+            },
+            'setElementSelected' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/element/:id/selected"
+            },
+            'toggleElement' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/element/:id/toggle"
+            },
+            'isElementEnabled' => {
+                'method' => 'GET',
+                'url'    => "session/:sessionId/element/:id/enabled"
+            },
+            'getElementLocation' => {
+                'method' => 'GET',
+                'url'    => "session/:sessionId/element/:id/location"
+            },
+            'getElementLocationInView' => {
                 'method' => 'GET',
-                'url' => "session/:sessionId/window/:windowHandle/size"
-        },
-        'getWindowPosition' => {
+                'url'    => "session/:sessionId/element/:id/location_in_view"
+            },
+            'getElementTagName' => {
                 'method' => 'GET',
-                'url' => "session/:sessionId/window/:windowHandle/position"
-        },
-        'setWindowSize' => {
-                'method' => 'POST',
-                'url' => "session/:sessionId/window/:windowHandle/size"
-        },
-        'setWindowPosition' => {
-                'method' => 'POST',
-                'url' => "session/:sessionId/window/:windowHandle/position"
-        },
-        'getCurrentUrl' => {
-                           'method' => 'GET',
-                           'url' => "session/:sessionId/url"
-        },
-        'get' => {
-                   'method' => 'POST',
-                   'url'    => "session/:sessionId/url"
-        },
-        'goForward' => {
-                       'method' => 'POST',
-                       'url' => "session/:sessionId/forward"
-        },
-        'goBack' => {
-                      'method' => 'POST',
-                      'url'    => "session/:sessionId/back"
-        },
-        'refresh' => {
-                       'method' => 'POST',
-                       'url' => "session/:sessionId/refresh"
-        },
-        'executeScript' => {
-                       'method' => 'POST',
-                       'url' => "session/:sessionId/execute"
-        },
-        'executeAsyncScript' => {
-                       'method' => 'POST',
-                       'url' => "session/:sessionId/execute_async"
-        },
-        'screenshot' => {
-                    'method' => 'GET',
-                    'url' => "session/:sessionId/screenshot"
-        },
-        'availableEngines' => {
-                    'method' => 'GET',
-                    'url' => "session/:sessionId/ime/available_engines"
-        },
-        'switchToFrame' => {
-                'method' => 'POST',
-                'url' => "session/:sessionId/frame"
-        },
-        'switchToWindow' => {
-             'method' => 'POST',
-             'url' => "session/:sessionId/window"
-        },
-        'getAllCookies' => {
-                        'method' => 'GET',
-                        'url' => "session/:sessionId/cookie"
-        },
-        'addCookie' => {
-                        'method' => 'POST',
-                        'url' => "session/:sessionId/cookie"
-        },
-        'deleteAllCookies' => {
-                        'method' => 'DELETE',
-                        'url' => "session/:sessionId/cookie"
-        },
-        'deleteCookieNamed' => {
-             'method' => 'DELETE',
-             'url' => "session/:sessionId/cookie/:name"
-        },
-        'getPageSource' => {
-                        'method' => 'GET',
-                        'url' => "session/:sessionId/source"
-        },
-        'getTitle' => {
-                        'method' => 'GET',
-                        'url' => "session/:sessionId/title"
-        },
-        'findElement' => {
-                       'method' => 'POST',
-                       'url' => "session/:sessionId/element"
-        },
-        'findElements' => {
-                      'method' => 'POST',
-                      'url' => "session/:sessionId/elements"
-        },
-        'getActiveElement' => {
-                'method' => 'POST',
-                'url' => "session/:sessionId/element/active"
-        },
-        'describeElement' => {
+                'url'    => "session/:sessionId/element/:id/name"
+            },
+            'clearElement' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/element/:id/clear"
+            },
+            'getElementAttribute' => {
+                'method' => 'GET',
+                'url'    => "session/:sessionId/element/:id/attribute/:name"
+            },
+            'elementEquals' => {
                 'method' => 'GET',
-                'url' => "session/:sessionId/element/:id"
-        },
-        'findChildElement' => {
-            'method' => 'POST',
-            'url' => "session/:sessionId/element/:id/element"
-        },
-        'findChildElements' => {
-            'method' => 'POST',
-            'url' => "session/:sessionId/element/:id/elements"
-        },
-        'clickElement' => {
-               'method' => 'POST',
-               'url' => "session/:sessionId/element/:id/click"
-        },
-        'submitElement' => {
-              'method' => 'POST',
-              'url' => "session/:sessionId/element/:id/submit"
-        },
-        'sendKeysToElement' => {
-               'method' => 'POST',
-               'url' => "session/:sessionId/element/:id/value"
-        },
-        'sendKeysToActiveElement' => {
-               'method' => 'POST',
-               'url' => "session/:sessionId/keys"
-        },
-        'sendModifier' => {
-               'method' => 'POST',
-               'url' => "session/:sessionId/modifier"
-        },
-        'isElementSelected' => {
-            'method' => 'GET',
-            'url' => "session/:sessionId/element/:id/selected"
-        },
-        'setElementSelected' => {
-            'method' => 'POST',
-            'url' => "session/:sessionId/element/:id/selected"
-        },
-        'toggleElement' => {
-              'method' => 'POST',
-              'url' => "session/:sessionId/element/:id/toggle"
-        },
-        'isElementEnabled' => {
-             'method' => 'GET',
-             'url' => "session/:sessionId/element/:id/enabled"
-        },
-        'getElementLocation' => {
-            'method' => 'GET',
-            'url' => "session/:sessionId/element/:id/location"
-        },
-        'getElementLocationInView' => {
-            'method' => 'GET',
-            'url' => "session/:sessionId/element/:id/location_in_view"
-        },
-        'getElementTagName' => {
+                'url'    => "session/:sessionId/element/:id/equals/:other"
+            },
+            'isElementDisplayed' => {
+                'method' => 'GET',
+                'url'    => "session/:sessionId/element/:id/displayed"
+            },
+            'close' => {
+                'method' => 'DELETE',
+                'url'    => "session/:sessionId/window"
+            },
+            'dragElement' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/element/:id/drag"
+            },
+            'getElementSize' => {
                 'method' => 'GET',
-                'url' => "session/:sessionId/element/:id/name"
-        },
-        'clearElement' => {
-               'method' => 'POST',
-               'url' => "session/:sessionId/element/:id/clear"
-        },
-        'getElementAttribute' => {
-            'method' => 'GET',
-            'url' =>
-"session/:sessionId/element/:id/attribute/:name"
-        },
-        'elementEquals' => {
-            'method' => 'GET',
-            'url' => "session/:sessionId/element/:id/equals/:other"
-        },
-        'isElementDisplayed' => {
-            'method' => 'GET',
-            'url' => "session/:sessionId/element/:id/displayed"
-        },
-        'close' => {
-                     'method' => 'DELETE',
-                     'url'    => "session/:sessionId/window"
-        },
-        'dragElement' => {
-                'method' => 'POST',
-                'url' => "session/:sessionId/element/:id/drag"
-        },
-        'getElementSize' => {
+                'url'    => "session/:sessionId/element/:id/size"
+            },
+            'getElementText' => {
                 'method' => 'GET',
-                'url' => "session/:sessionId/element/:id/size"
-        },
-        'getElementText' => {
+                'url'    => "session/:sessionId/element/:id/text"
+            },
+            'getElementValueOfCssProperty' => {
                 'method' => 'GET',
-                'url' => "session/:sessionId/element/:id/text"
-        },
-        'getElementValueOfCssProperty' => {
-            'method' => 'GET',
-            'url' => "session/:sessionId/element/:id/css/:propertyName"
-        },
-        'mouseMoveToLocation' => {
-               'method' => 'POST',
-               'url' => "session/:sessionId/moveto"
-        },
-        'getAlertText' => {
-               'method' => 'GET',
-               'url'    => 'session/:sessionId/alert_text'
-        },
-        'sendKeysToPrompt' => {
-               'method' => 'POST',
-               'url'    => 'session/:sessionId/alert_text'
-        },
-        'acceptAlert' => {
-               'method' => 'POST',
-               'url'    => 'session/:sessionId/accept_alert'
-        },
-        'dismissAlert' => {
-               'method' => 'POST',
-               'url'    => 'session/:sessionId/dismiss_alert'
-        },
-        'click' => {
-               'method' => 'POST',
-               'url'    => 'session/:sessionId/click'
-        },
-        'doubleClick' => {
-               'method' => 'POST',
-               'url'    => 'session/:sessionId/doubleclick'
-        },
-        'buttonDown' => {
-               'method' => 'POST',
-               'url'    => 'session/:sessionId/buttondown'
-        },
-        'buttonUp' => {
-               'method' => 'POST',
-               'url'    => 'session/:sessionId/buttonup'
-        },
-        'uploadFile' => {
-               'method' => 'POST',
-               'url'    => 'session/:sessionId/file'
-        },
-        #'setVisible' => {
-        #               'method' => 'POST',
-        #               'url' => "session/:sessionId/visible"
-        #},
-        #'getVisible' => {
-        #               'method' => 'GET',
-        #               'url' => "session/:sessionId/visible"
-        #},
-    };
+                'url'    => "session/:sessionId/element/:id/css/:propertyName"
+            },
+            'mouseMoveToLocation' => {
+                'method' => 'POST',
+                'url'    => "session/:sessionId/moveto"
+            },
+            'getAlertText' => {
+                'method' => 'GET',
+                'url'    => 'session/:sessionId/alert_text'
+            },
+            'sendKeysToPrompt' => {
+                'method' => 'POST',
+                'url'    => 'session/:sessionId/alert_text'
+            },
+            'acceptAlert' => {
+                'method' => 'POST',
+                'url'    => 'session/:sessionId/accept_alert'
+            },
+            'dismissAlert' => {
+                'method' => 'POST',
+                'url'    => 'session/:sessionId/dismiss_alert'
+            },
+            'click' => {
+                'method' => 'POST',
+                'url'    => 'session/:sessionId/click'
+            },
+            'doubleClick' => {
+                'method' => 'POST',
+                'url'    => 'session/:sessionId/doubleclick'
+            },
+            'buttonDown' => {
+                'method' => 'POST',
+                'url'    => 'session/:sessionId/buttondown'
+            },
+            'buttonUp' => {
+                'method' => 'POST',
+                'url'    => 'session/:sessionId/buttonup'
+            },
+            'uploadFile' => {
+                'method' => 'POST',
+                'url'    => 'session/:sessionId/file'
+            },
+
+            #'setVisible' => {
+            #               'method' => 'POST',
+            #               'url' => "session/:sessionId/visible"
+            #},
+            #'getVisible' => {
+            #               'method' => 'GET',
+            #               'url' => "session/:sessionId/visible"
+            #},
+        };
+    }
+);
 
-    bless $self, $class or die "Can't bless $class: $!";
-    return $self;
+# helper methods to manipulate the _cmds hash
+sub get_url {
+    my ( $self, $command ) = @_;
+    return $self->get_cmds->{$command}->{url};
+}
+
+sub get_method {
+    my ( $self, $command ) = @_;
+    return $self->get_cmds->{$command}->{method};
 }
 
 # This method will replace the template & return
 sub get_params {
-    my ($self, $args) = @_;
-    if (!(defined $args->{'session_id'})) {
+    my ( $self, $args ) = @_;
+    if ( !( defined $args->{'session_id'} ) ) {
         return;
     }
-    my $data = {};
+    my $data    = {};
     my $command = $args->{'command'};
-    my $url = $self->{$command}->{'url'};
-    
+    my $url     = $self->get_url($command);
+
     # Do the var substitutions.
     $url =~ s/:sessionId/$args->{'session_id'}/;
     $url =~ s/:id/$args->{'id'}/;
@@ -313,7 +321,7 @@ sub get_params {
     $url =~ s/:other/$args->{'other'}/;
     $url =~ s/:windowHandle/$args->{'window_handle'}/;
 
-    $data->{'method'} = $self->{$command}->{'method'};
+    $data->{'method'} = $self->get_method($command);
     $data->{'url'}    = $url;
 
     return $data;
@@ -337,11 +345,11 @@ L<http://code.google.com/p/selenium/>.
 =head1 BUGS
 
 The Selenium issue tracking system is available online at
-L<http://github.com/aivaturi/Selenium-Remote-Driver/issues>.
+L<http://github.com/gempesaw/Selenium-Remote-Driver/issues>.
 
 =head1 CURRENT MAINTAINER
 
-Charles Howes C<< <chowes@cpan.org> >>
+Daniel Gempesaw C<< <gempesaw@gmail.com> >>
 
 =head1 AUTHOR
 

File diff suppressed because it is too large
+ 364 - 271
lib/Selenium/Remote/Driver.pm


+ 110 - 99
lib/Selenium/Remote/ErrorHandler.pm

@@ -1,113 +1,124 @@
 package Selenium::Remote::ErrorHandler;
 
-use strict;
-use warnings;
-
+use Moo;
 use Carp qw(croak);
 
 # We're going to handle only codes that are errors.
 # http://code.google.com/p/selenium/wiki/JsonWireProtocol
-use constant STATUS_CODE => {
-    7 => {
-            'code' => 'NO_SUCH_ELEMENT',
-            'msg'  => 'An element could not be located on the page using the given search parameters.',
-         },
-    8 => {
-            'code' => 'NO_SUCH_FRAME',
-            'msg'  => 'A request to switch to a frame could not be satisfied because the frame could not be found.',
-         },
-    9 => {
-            'code' => 'UNKNOWN_COMMAND',
-            'msg'  => 'The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource.',
-         },
-    10 => {
-            'code' => 'STALE_ELEMENT_REFERENCE',
-            'msg'  => 'An element command failed because the referenced element is no longer attached to the DOM.',
-         },
-    11 => {
-            'code' => 'ELEMENT_NOT_VISIBLE',
-            'msg'  => 'An element command could not be completed because the element is not visible on the page.',
-         },
-    12 => {
-            'code' => 'INVALID_ELEMENT_STATE',
-            'msg'  => 'An element command could not be completed because the element is in an invalid state (e.g. attempting to click a disabled element).',
-         },
-    13 => {
-            'code' => 'UNKNOWN_ERROR',
-            'msg'  => 'An unknown server-side error occurred while processing the command.',
-         },
-    15 => {
-            'code' => 'ELEMENT_IS_NOT_SELECTABLE',
-            'msg'  => 'An attempt was made to select an element that cannot be selected.',
-         },
-    19 => {
-            'code' => 'XPATH_LOOKUP_ERROR',
-            'msg'  => 'An error occurred while searching for an element by XPath.',
-         },
-    21 => {
-            'code' => 'Timeout',
-            'msg'  => 'An operation did not complete before its timeout expired.',
-         },
-    23 => {
-            'code' => 'NO_SUCH_WINDOW',
-            'msg'  => 'A request to switch to a different window could not be satisfied because the window could not be found.',
-         },
-    24 => {
-            'code' => 'INVALID_COOKIE_DOMAIN',
-            'msg'  => 'An illegal attempt was made to set a cookie under a different domain than the current page.',
-         },
-    25 => {
-            'code' => 'UNABLE_TO_SET_COOKIE',
-            'msg'  => 'A request to set a cookie\'s value could not be satisfied.',
-         },
-    26 => {
-            'code' => 'UNEXPECTED_ALERT_OPEN',
-            'msg'  => 'A modal dialog was open, blocking this operation',
-         },
-    27 => {
-            'code' => 'NO_ALERT_OPEN_ERROR',
-            'msg'  => 'An attempt was made to operate on a modal dialog when one was not open.',
-         },
-    28 => {
-            'code' => 'SCRIPT_TIMEOUT',
-            'msg'  => 'A script did not complete before its timeout expired.',
-         },
-    29 => {
-            'code' => 'INVALID_ELEMENT_COORDINATES',
-            'msg'  => 'The coordinates provided to an interactions operation are invalid.',
-         },
-    30 => {
-            'code' => 'IME_NOT_AVAILABLE',
-            'msg'  => 'IME was not available.',
-         },
-    31 => {
-            'code' => 'IME_ENGINE_ACTIVATION_FAILED',
-            'msg'  => 'An IME engine could not be started.',
-         },
-    32 => {
-            'code' => 'INVALID_SELECTOR',
-            'msg'  => 'Argument was an invalid selector (e.g. XPath/CSS).',
-         },
-};
-
-sub new {
-    my ($class) = @_;
-    
-    my $self = {};
-    bless $self, $class or die "Can't bless $class: $!";
-    
-    return $self;
-}
+has STATUS_CODE => (
+    is      => 'lazy',
+    builder => sub {
+        return {
+            7 => {
+                'code' => 'NO_SUCH_ELEMENT',
+                'msg' =>
+                  'An element could not be located on the page using the given search parameters.',
+            },
+            8 => {
+                'code' => 'NO_SUCH_FRAME',
+                'msg' =>
+                  'A request to switch to a frame could not be satisfied because the frame could not be found.',
+            },
+            9 => {
+                'code' => 'UNKNOWN_COMMAND',
+                'msg' =>
+                  'The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource.',
+            },
+            10 => {
+                'code' => 'STALE_ELEMENT_REFERENCE',
+                'msg' =>
+                  'An element command failed because the referenced element is no longer attached to the DOM.',
+            },
+            11 => {
+                'code' => 'ELEMENT_NOT_VISIBLE',
+                'msg' =>
+                  'An element command could not be completed because the element is not visible on the page.',
+            },
+            12 => {
+                'code' => 'INVALID_ELEMENT_STATE',
+                'msg' =>
+                  'An element command could not be completed because the element is in an invalid state (e.g. attempting to click a disabled element).',
+            },
+            13 => {
+                'code' => 'UNKNOWN_ERROR',
+                'msg' =>
+                  'An unknown server-side error occurred while processing the command.',
+            },
+            15 => {
+                'code' => 'ELEMENT_IS_NOT_SELECTABLE',
+                'msg' =>
+                  'An attempt was made to select an element that cannot be selected.',
+            },
+            19 => {
+                'code' => 'XPATH_LOOKUP_ERROR',
+                'msg' =>
+                  'An error occurred while searching for an element by XPath.',
+            },
+            21 => {
+                'code' => 'Timeout',
+                'msg' =>
+                  'An operation did not complete before its timeout expired.',
+            },
+            23 => {
+                'code' => 'NO_SUCH_WINDOW',
+                'msg' =>
+                  'A request to switch to a different window could not be satisfied because the window could not be found.',
+            },
+            24 => {
+                'code' => 'INVALID_COOKIE_DOMAIN',
+                'msg' =>
+                  'An illegal attempt was made to set a cookie under a different domain than the current page.',
+            },
+            25 => {
+                'code' => 'UNABLE_TO_SET_COOKIE',
+                'msg' =>
+                  'A request to set a cookie\'s value could not be satisfied.',
+            },
+            26 => {
+                'code' => 'UNEXPECTED_ALERT_OPEN',
+                'msg'  => 'A modal dialog was open, blocking this operation',
+            },
+            27 => {
+                'code' => 'NO_ALERT_OPEN_ERROR',
+                'msg' =>
+                  'An attempt was made to operate on a modal dialog when one was not open.',
+            },
+            28 => {
+                'code' => 'SCRIPT_TIMEOUT',
+                'msg' =>
+                  'A script did not complete before its timeout expired.',
+            },
+            29 => {
+                'code' => 'INVALID_ELEMENT_COORDINATES',
+                'msg' =>
+                  'The coordinates provided to an interactions operation are invalid.',
+            },
+            30 => {
+                'code' => 'IME_NOT_AVAILABLE',
+                'msg'  => 'IME was not available.',
+            },
+            31 => {
+                'code' => 'IME_ENGINE_ACTIVATION_FAILED',
+                'msg'  => 'An IME engine could not be started.',
+            },
+            32 => {
+                'code' => 'INVALID_SELECTOR',
+                'msg'  => 'Argument was an invalid selector (e.g. XPath/CSS).',
+            },
+        };
+    }
+);
+
 
 # Instead of just returning the end user a server returned error code, we will
 # put a more human readable & usable error message & that is what this method
-# is going to do. 
+# is going to do.
 sub process_error {
     my ($self, $resp) = @_;
     # TODO: Handle screen if it sent back with the response. Either we could
     # let the end user handle it or we can save it an image file at a temp
-    # location & return the path. 
-    
+    # location & return the path.
+
     my $ret;
     $ret->{'stackTrace'} = $resp->{'value'}->{'stackTrace'};
     $ret->{'error'} = $self->STATUS_CODE->{$resp->{'status'}};
@@ -135,11 +146,11 @@ L<http://code.google.com/p/selenium/>.
 =head1 BUGS
 
 The Selenium issue tracking system is available online at
-L<http://github.com/aivaturi/Selenium-Remote-Driver/issues>.
+L<http://github.com/gempesaw/Selenium-Remote-Driver/issues>.
 
 =head1 CURRENT MAINTAINER
 
-Charles Howes C<< <chowes@cpan.org> >>
+Daniel Gempesaw C<< <gempesaw@gmail.com> >>
 
 =head1 AUTHOR
 

+ 43 - 31
lib/Selenium/Remote/RemoteConnection.pm

@@ -1,8 +1,7 @@
 package Selenium::Remote::RemoteConnection;
 
-use strict;
-use warnings;
-
+use Moo;
+use Try::Tiny;
 use LWP::UserAgent;
 use HTTP::Headers;
 use HTTP::Request;
@@ -10,32 +9,46 @@ use Net::Ping;
 use Carp qw(croak);
 use JSON;
 use Data::Dumper;
-
 use Selenium::Remote::ErrorHandler;
 
-sub new {
-    my ($class, $remote_srvr, $port) = @_;
-    
-    my $self = {
-                 remote_server_addr => $remote_srvr,
-                 port               => $port,
-                 debug              => 0,
+has 'remote_server_addr' => (
+    is => 'rw',
+);
+
+has 'port' => (
+    is => 'rw',
+);
+
+has 'debug' => (
+    is => 'rw',
+    default => sub { 0 }
+);
+
+has 'ua' => (
+    is => 'lazy',
+    builder => sub { return LWP::UserAgent->new; }
+);
+
+sub BUILD {
+    my $self = shift;
+    my $status;
+    try {
+      $status = $self->request('GET','status');
+    }
+    catch {
+        croak "Could not connect to SeleniumWebDriver: $_" ;
     };
-    bless $self, $class or die "Can't bless $class: $!";
-    my $status = eval {$self->request('GET','status');};
-    croak "Could not connect to SeleniumWebDriver" if($@);
     if($status->{cmd_status} ne 'OK') {
         # Could be grid, see if we can talk to it
         $status = undef;
         $status = $self->request('GET', 'grid/api/testsession');
     }
-    if($status->{cmd_status} eq 'OK') {
-        return $self;
-    } else {
+    unless ($status->{cmd_status} eq 'OK') {
         croak "Selenium server did not return proper status";
     }
 }
 
+
 # This request method is tailored for Selenium RC server
 sub request {
     my ($self, $method, $url, $params) = @_;
@@ -49,15 +62,15 @@ sub request {
     elsif ($url =~ m/grid/g) {
         $fullurl =
             "http://"
-          . $self->{remote_server_addr} . ":"
-          . $self->{port}
+          . $self->remote_server_addr . ":"
+          . $self->port
           . "/$url";
     }
     else {
         $fullurl =
             "http://"
-          . $self->{remote_server_addr} . ":"
-          . $self->{port}
+          . $self->remote_server_addr . ":"
+          . $self->port
           . "/wd/hub/$url";
     }
 
@@ -66,16 +79,15 @@ sub request {
         $json->allow_blessed;
         $content = $json->allow_nonref->utf8->encode($params);
     }
-    
-    print "REQ: $url, $content\n" if $self->{debug};
+
+    print "REQ: $url, $content\n" if $self->debug;
 
     # HTTP request
-    my $ua = LWP::UserAgent->new;
     my $header =
       HTTP::Headers->new(Content_Type => 'application/json; charset=utf-8');
     $header->header('Accept' => 'application/json');
     my $request = HTTP::Request->new($method, $fullurl, $header, $content);
-    my $response = $ua->request($request);
+    my $response = $self->ua->request($request);
 
     return $self->_process_response($response);
 }
@@ -90,7 +102,7 @@ sub _process_response {
     }
     else {
         my $decoded_json = undef;
-        print "RES: ".$response->decoded_content."\n\n" if $self->{debug};
+        print "RES: ".$response->decoded_content."\n\n" if $self->debug;
         if (($response->message ne 'No Content') && ($response->content ne '')) {
             if ($response->content_type !~ m/json/i) {
                 $data->{'cmd_return'} = 'Server returned error message '.$response->content.' instead of data';
@@ -99,7 +111,7 @@ sub _process_response {
             $decoded_json = $json->allow_nonref(1)->utf8(1)->decode($response->content);
             $data->{'sessionId'} = $decoded_json->{'sessionId'};
         }
-        
+
         if ($response->is_error) {
             my $error_handler = new Selenium::Remote::ErrorHandler;
             $data->{'cmd_status'} = 'NOTOK';
@@ -107,7 +119,7 @@ sub _process_response {
                 $data->{'cmd_return'} = $error_handler->process_error($decoded_json);
             }
             else {
-                $data->{'cmd_return'} = 'Server returned error code '.$response->code.' and no data';          
+                $data->{'cmd_return'} = 'Server returned error code '.$response->code.' and no data';
             }
             return $data;
         }
@@ -117,7 +129,7 @@ sub _process_response {
                 $data->{'cmd_return'} = $decoded_json->{'value'};
             }
             else {
-                $data->{'cmd_return'} = 'Server returned status code '.$response->code.' but no data';          
+                $data->{'cmd_return'} = 'Server returned status code '.$response->code.' but no data';
             }
             return $data;
         }
@@ -149,11 +161,11 @@ L<http://code.google.com/p/selenium/>.
 =head1 BUGS
 
 The Selenium issue tracking system is available online at
-L<http://github.com/aivaturi/Selenium-Remote-Driver/issues>.
+L<http://github.com/gempesaw/Selenium-Remote-Driver/issues>.
 
 =head1 CURRENT MAINTAINER
 
-Charles Howes C<< <chowes@cpan.org> >>
+Daniel Gempesaw C<< <gempesaw@gmail.com> >>
 
 =head1 AUTHOR
 

+ 67 - 68
lib/Selenium/Remote/WebElement.pm

@@ -1,8 +1,6 @@
 package Selenium::Remote::WebElement;
 
-use strict;
-use warnings;
-
+use Moo;
 use Carp qw(croak);
 
 =head1 NAME
@@ -18,7 +16,7 @@ provides a mechanism to represent them as objects & perform various actions on
 the related elements. This module should not be instantiated directly by the end
 user. Selenium::Remote::Driver instantiates this module when required. Typically,
 the find_element method in Selenium::Remote::Driver returns this object on which
-various element related operations can be carried out. 
+various element related operations can be carried out.
 
 =cut
 
@@ -26,20 +24,16 @@ various element related operations can be carried out.
 
 =cut
 
-sub new {
-    my ($class, $id, $parent) = @_;
-    my $self = {
-        id => $id,
-        driver => $parent,
-    };
-    bless $self, $class or die "Can't bless $class: $!";
-    return $self;
-}
+has 'id' => (
+    is => 'rw',
+);
+
+has 'driver' => (
+    is => 'rw',
+    handles => [qw(_execute_command)],
+);
+
 
-sub _execute_command {
-    my ($self) = shift;
-    return $self->{driver}->_execute_command(@_);
-}
 
 =head2 click
 
@@ -53,7 +47,7 @@ sub _execute_command {
 
 sub click {
     my ($self) = @_;
-    my $res = { 'command' => 'clickElement', 'id' => $self->{id} };
+    my $res = { 'command' => 'clickElement', 'id' => $self->id };
     return $self->_execute_command($res);
 }
 
@@ -70,7 +64,7 @@ sub click {
 
 sub submit {
     my ($self) = @_;
-    my $res = { 'command' => 'submitElement', 'id' => $self->{id} };
+    my $res = { 'command' => 'submitElement', 'id' => $self->id };
     return $self->_execute_command($res);
 }
 
@@ -88,9 +82,9 @@ sub submit {
  Usage:
     $elem->send_keys('abcd', 'efg');
     $elem->send_keys('hijk');
-    
+
     or
-    
+
     # include the WDKeys module
     use Selenium::Remote::WDKeys;
     .
@@ -100,13 +94,13 @@ sub submit {
 =cut
 
 sub send_keys {
-    my ($self, @strings) = @_;
-    my $res = { 'command' => 'sendKeysToElement', 'id' => $self->{id} };
+    my ( $self, @strings ) = @_;
+    my $res = { 'command' => 'sendKeysToElement', 'id' => $self->id };
     map { $_ .= "" } @strings;
     my $params = {
         'value' => \@strings,
     };
-    return $self->_execute_command($res, $params);
+    return $self->_execute_command( $res, $params );
 }
 
 =head2 is_selected
@@ -125,14 +119,14 @@ sub send_keys {
 
 sub is_selected {
     my ($self) = @_;
-    my $res = { 'command' => 'isElementSelected', 'id' => $self->{id} };
+    my $res = { 'command' => 'isElementSelected', 'id' => $self->id };
     return $self->_execute_command($res);
 }
 
 =head2 set_selected
 
  Description:
-    Select an OPTION element, or an INPUT element of type checkbox or radiobutton. 
+    Select an OPTION element, or an INPUT element of type checkbox or radiobutton.
 
  Usage:
     $elem->set_selected();
@@ -143,7 +137,7 @@ sub is_selected {
 
 sub set_selected {
     my ($self) = @_;
-    my $res = { 'command' => 'setElementSelected', 'id' => $self->{id} };
+    my $res = { 'command' => 'setElementSelected', 'id' => $self->id };
     return $self->_execute_command($res);
 }
 
@@ -152,7 +146,7 @@ sub set_selected {
  Description:
     Toggle whether an OPTION element, or an INPUT element of type checkbox or
     radiobutton is currently selected.
-    
+
  Output:
     BOOLEAN - Whether the element is selected after toggling its state.
 
@@ -165,7 +159,7 @@ sub set_selected {
 
 sub toggle {
     my ($self) = @_;
-    my $res = { 'command' => 'toggleElement', 'id' => $self->{id} };
+    my $res = { 'command' => 'toggleElement', 'id' => $self->id };
     return $self->_execute_command($res);
 }
 
@@ -173,7 +167,7 @@ sub toggle {
 
  Description:
     Determine if an element is currently enabled.
-    
+
  Output:
     BOOLEAN - Whether the element is enabled.
 
@@ -184,7 +178,7 @@ sub toggle {
 
 sub is_enabled {
     my ($self) = @_;
-    my $res = { 'command' => 'isElementEnabled', 'id' => $self->{id} };
+    my $res = { 'command' => 'isElementEnabled', 'id' => $self->id };
     return $self->_execute_command($res);
 }
 
@@ -193,7 +187,7 @@ sub is_enabled {
  Description:
    Determine an element's location on the page. The point (0, 0) refers to the
    upper-left corner of the page.
-    
+
  Output:
     HASH - The X and Y coordinates for the element on the page.
 
@@ -204,7 +198,7 @@ sub is_enabled {
 
 sub get_element_location {
     my ($self) = @_;
-    my $res = { 'command' => 'getElementLocation', 'id' => $self->{id} };
+    my $res = { 'command' => 'getElementLocation', 'id' => $self->id };
     return $self->_execute_command($res);
 }
 
@@ -213,10 +207,10 @@ sub get_element_location {
  Description:
     Determine an element's location on the screen once it has been scrolled
     into view.
-    
+
     Note: This is considered an internal command and should only be used to
     determine an element's location for correctly generating native events.
-    
+
  Output:
     {x:number, y:number} The X and Y coordinates for the element on the page.
 
@@ -227,7 +221,7 @@ sub get_element_location {
 
 sub get_element_location_in_view {
     my ($self) = @_;
-    my $res = { 'command' => 'getElementLocationInView', 'id' => $self->{id} };
+    my $res = { 'command' => 'getElementLocationInView', 'id' => $self->id };
     return $self->_execute_command($res);
 }
 
@@ -235,7 +229,7 @@ sub get_element_location_in_view {
 
  Description:
     Query for an element's tag name.
-    
+
  Output:
     STRING - The element's tag name, as a lowercase string.
 
@@ -246,7 +240,7 @@ sub get_element_location_in_view {
 
 sub get_tag_name {
     my ($self) = @_;
-    my $res = { 'command' => 'getElementTagName', 'id' => $self->{id} };
+    my $res = { 'command' => 'getElementTagName', 'id' => $self->id };
     return $self->_execute_command($res);
 }
 
@@ -254,7 +248,7 @@ sub get_tag_name {
 
  Description:
     Clear a TEXTAREA or text INPUT element's value.
-    
+
  Usage:
     $elem->clear();
 
@@ -262,7 +256,7 @@ sub get_tag_name {
 
 sub clear {
     my ($self) = @_;
-    my $res = { 'command' => 'clearElement', 'id' => $self->{id} };
+    my $res = { 'command' => 'clearElement', 'id' => $self->id };
     return $self->_execute_command($res);
 }
 
@@ -274,7 +268,7 @@ sub clear {
  Input: 1
     Required:
         STRING - name of the attribute of the element
-    
+
  Output:
     {STRING | NULL} The value of the attribute, or null if it is not set on the element.
 
@@ -284,14 +278,15 @@ sub clear {
 =cut
 
 sub get_attribute {
-    my ($self, $attr_name) = @_;
-    if (not defined $attr_name) {
+    my ( $self, $attr_name ) = @_;
+    if ( not defined $attr_name ) {
         croak 'Attribute name not provided';
     }
-    my $res = {'command' => 'getElementAttribute',
-               'id' => $self->{id},
-               'name' => $attr_name,
-               };
+    my $res = {
+        'command' => 'getElementAttribute',
+        'id'      => $self->id,
+        'name'    => $attr_name,
+    };
     return $self->_execute_command($res);
 }
 
@@ -317,7 +312,7 @@ sub get_value {
 
  Description:
     Determine if an element is currently displayed.
-    
+
  Output:
     BOOLEAN - Whether the element is displayed.
 
@@ -328,7 +323,7 @@ sub get_value {
 
 sub is_displayed {
     my ($self) = @_;
-    my $res = { 'command' => 'isElementDisplayed', 'id' => $self->{id} };
+    my $res = { 'command' => 'isElementDisplayed', 'id' => $self->id };
     return $self->_execute_command($res);
 }
 
@@ -342,23 +337,23 @@ sub is_displayed {
     Required:
         NUMBER - X axis distance in pixels
         NUMBER - Y axis distance in pixels
-    
+
  Usage:
     $elem->drag(216,158);
 
 =cut
 
 sub drag {
-    my ($self, $x, $y) = @_;
-    if ((not defined $x) || (not defined $y)){
+    my ( $self, $x, $y ) = @_;
+    if ( ( not defined $x ) || ( not defined $y ) ) {
         croak 'X & Y pixel coordinates not provided';
     }
-    my $res = {'command' => 'dragElement','id' => $self->{id}};
+    my $res = { 'command' => 'dragElement', 'id' => $self->id };
     my $params = {
         'x' => $x,
         'y' => $y,
     };
-    return $self->_execute_command($res, $params);
+    return $self->_execute_command( $res, $params );
 }
 
 =head2 get_size
@@ -369,7 +364,7 @@ sub drag {
 
  Output:
     HASH - The width and height of the element, in pixels.
-    
+
  Usage:
     $elem->get_size();
 
@@ -377,7 +372,7 @@ sub drag {
 
 sub get_size {
     my ($self) = @_;
-    my $res = { 'command' => 'getElementSize', 'id' => $self->{id} };
+    my $res = { 'command' => 'getElementSize', 'id' => $self->id };
     return $self->_execute_command($res);
 }
 
@@ -388,7 +383,7 @@ sub get_size {
 
  Output:
     STRING - innerText of an element
-    
+
  Usage:
     $elem->get_text();
 
@@ -396,7 +391,7 @@ sub get_size {
 
 sub get_text {
     my ($self) = @_;
-    my $res = { 'command' => 'getElementText', 'id' => $self->{id} };
+    my $res = { 'command' => 'getElementText', 'id' => $self->id };
     return $self->_execute_command($res);
 }
 
@@ -413,21 +408,22 @@ sub get_text {
 
  Output:
     STRING - Value of the css attribute
-    
+
  Usage:
     $elem->get_css_attribute('background-color');
 
 =cut
 
 sub get_css_attribute {
-    my ($self, $attr_name) = @_;
-    if (not defined $attr_name) {
+    my ( $self, $attr_name ) = @_;
+    if ( not defined $attr_name ) {
         croak 'CSS attribute name not provided';
     }
-    my $res = {'command' => 'getElementValueOfCssProperty',
-               'id' => $self->{id},
-               'property_name' => $attr_name,
-               };
+    my $res = {
+        'command'       => 'getElementValueOfCssProperty',
+        'id'            => $self->id,
+        'property_name' => $attr_name,
+    };
     return $self->_execute_command($res);
 }
 
@@ -440,9 +436,10 @@ sub get_css_attribute {
     $elem->describe();
 
 =cut
+
 sub describe {
     my ($self) = @_;
-    my $res = { 'command' => 'describeElement', 'id' => $self->{id} };
+    my $res = { 'command' => 'describeElement', 'id' => $self->id };
     return $self->_execute_command($res);
 }
 
@@ -456,11 +453,11 @@ L<http://code.google.com/p/selenium/>.
 =head1 BUGS
 
 The Selenium issue tracking system is available online at
-L<http://github.com/aivaturi/Selenium-Remote-Driver/issues>.
+L<http://github.com/gempesaw/Selenium-Remote-Driver/issues>.
 
 =head1 CURRENT MAINTAINER
 
-Charles Howes C<< <chowes@cpan.org> >>
+Daniel Gempesaw C<< <gempesaw@gmail.com> >>
 
 =head1 AUTHOR
 
@@ -481,3 +478,5 @@ distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
+480:    hit eof while in pod documentation (no =cut seen)
+        this can cause trouble with some pod utilities

+ 11 - 8
lib/Test/Selenium/Remote/Driver.pm

@@ -1,6 +1,7 @@
+package Test::Selenium::Remote::Driver;
+
 use strict;
 use warnings;
-package Test::Selenium::Remote::Driver;
 use parent  'Selenium::Remote::Driver';
 # ABSTRACT: Useful testing subclass for Selenium::Remote::Driver
 
@@ -83,9 +84,11 @@ sub AUTOLOAD {
         my $cmd = $1;
 
         # make a subroutine for ok() around the selenium command
+        # TODO: fix the thing for get_ok, it won't work as its arg get
+        # pop'd in $name (so the call to get has no args => end of game)
         $sub = sub {
             my $self = shift;
-            my $name = pop;
+            my $name = (@_ > 1 ? pop @_ : $cmd);
             my ($arg1, $arg2) = @_;
             if ($self->{default_names} and !defined $name) {
                 $name = $cmd;
@@ -172,11 +175,11 @@ sub new {
 
     for my $opt (qw/remote_server_addr port browser_name version platform
                     javascript auto_close extra_capabilities/) {
-        $p{$opt} ||= $ENV{ 'TWD_' . uc($opt) };
+        $p{$opt} //= $ENV{ 'TWD_' . uc($opt) };
     }
-    $p{browser_name}       ||= $ENV{TWD_BROWSER}; # ykwim
-    $p{remote_server_addr} ||= $ENV{TWD_HOST};    # ykwim
-    $p{webelement_class}   ||= 'Test::Selenium::Remote::WebElement';
+    $p{browser_name}       //= $ENV{TWD_BROWSER}; # ykwim
+    $p{remote_server_addr} //= $ENV{TWD_HOST};    # ykwim
+    $p{webelement_class}   //= 'Test::Selenium::Remote::WebElement';
 
     my $self = $class->SUPER::new(%p);
     $self->{verbose} = $p{verbose};
@@ -185,7 +188,7 @@ sub new {
 
 =head2 server_is_running( $host, $port )
 
-Returns true if a Selenium server is running.  The host and port 
+Returns true if a Selenium server is running.  The host and port
 parameters are optional, and default to C<localhost:4444>.
 
 Environment vars C<TWD_HOST> and C<TWD_PORT> can also be used to
@@ -703,7 +706,7 @@ sub is_element_displayed_ok {
 
     $twd->is_element_enabled_ok($search_target [,$desc]);
 
-Find an element and check to confirm that it is enabled. 
+Find an element and check to confirm that it is enabled.
 
 =cut
 

+ 166 - 134
lib/Test/Selenium/Remote/WebElement.pm

@@ -1,146 +1,178 @@
 package Test::Selenium::Remote::WebElement;
-use parent 'Selenium::Remote::WebElement';
 
-use Test::More;
+use parent 'Selenium::Remote::WebElement';
+use Moo;
 use Test::Builder;
- 
-our $AUTOLOAD;
+use Try::Tiny;
 
-our $Test = Test::Builder->new;
-$Test->exported_to(__PACKAGE__);
-
-our %comparator = (
-    is     => 'is_eq',
-    isnt   => 'isnt_eq',
-    like   => 'like',
-    unlike => 'unlike',
+has _builder => (
+    is      => 'lazy',
+    builder => sub { return Test::Builder->new() },
+    handles => [qw/is_eq isnt_eq like unlike ok croak/],
 );
 
-our %one_arg = map { $_ => 1 } qw(
-    get_attribute
-    send_keys
+sub has_args {
+    my $self = shift;
+    my $fun_name = shift;
+    my $hash_fun_args = {
+        'get_attribute' => 1,
+    };
+    return ($hash_fun_args->{$fun_name} // 0);
+}
+
 
-);
-sub one_arg {
-    my $self   = shift;
-    my $method = shift;
-    return $one_arg{$method};
-}
-
-our %no_arg = map { $_ => 1 } qw(
-    clear
-    click
-    get_text
-    get_value
-    get_tag_name
-    is_enabled
-    is_selected
-    submit
-    );
-
-sub no_arg {
-    my $self   = shift;
-    my $method = shift;
-    return $no_arg{$method};
-}
-
-our %no_return = map { $_ => 1 } qw(send_keys click clear submit);
-
-sub no_return {
-    my $self   = shift;
-    my $method = shift;
-    return $no_return{$method};
-}
-
-sub AUTOLOAD {
-    my $name = $AUTOLOAD;
-    $name =~ s/.*:://;
-    return if $name eq 'DESTROY';
-    my $self = $_[0];
- 
-    my $sub;
-    if ($name =~ /(\w+)_(is|isnt|like|unlike)$/i) {
-        my $getter = "get_$1";
-        my $comparator = $comparator{lc $2};
- 
-        $sub = sub {
-            my( $self, $str, $name ) = @_;
-            # There is no verbose option currently
-            #diag "Test::Selenium::Remote::WebElement running $getter (@_[1..$#_])" if $self->{verbose};
-            $name = "$getter, '$str'" if !defined $name;
-            no strict 'refs';
-            return $Test->$comparator( $self->$getter, $str, $name );
-        };
-    }
-    elsif ($name =~ /(\w+?)_?ok$/i) {
-        my $cmd = $1;
- 
-        # make a subroutine for ok() around the selenium command
-        $sub = sub {
-            my( $self, $arg1, $arg2, $name );
-            $self = $_[0];
-            if ($self->no_arg($cmd)) {
-                $name = $_[1];
-            }
-            elsif ($self->one_arg($cmd)) {
-                $arg1 = $_[1];
-                $name = $_[2];
-            }
-            else {
-                $arg1 = $_[1];
-                $arg2 = $_[2];
-                $name = $_[3];
-            }
-
-            if (!defined $name) {
-                $name = $cmd;
-                $name .= ", $arg1" if defined $arg1;
-                $name .= ", $arg2" if defined $arg2;
-            }
-            # There is no verbose option currently
-            # diag "Test::Selenium::Remote::WebElement running $cmd (@_[1..$#_])" if $self->{verbose};
- 
-            local $Test::Builder::Level = $Test::Builder::Level + 1;
-            my $rc = '';
-            eval { 
-                if ($self->no_arg($cmd)) {
-                    $rc = $self->$cmd();
-                }
-                elsif ($self->one_arg($cmd)) {
-                    $rc = $self->$cmd( $arg1 );
-                }
-                else {
-                    $rc = $self->$cmd( $arg1, $arg2 );
-                }
-            };
-            die $@ if $@ and $@ =~ /Can't locate object method/;
-            diag($@) if $@;
-            if ($self->no_return($cmd)) {
-                $rc = ok( 1, "$name... no return value" );
-            }
-            else {
-                $rc = ok( $rc, $name );
-            }
-            return $rc;
-        };
-    }
- 
-    # jump directly to the new subroutine, avoiding an extra frame stack
-    if ($sub) {
-        no strict 'refs';
-        *{$AUTOLOAD} = $sub;
-        goto &$AUTOLOAD;
+sub _check_method {
+    my $self           = shift;
+    my $method         = shift;
+    my $method_to_test = shift;
+    $method = "get_$method";
+    my @args = @_;
+    my $rv;
+    try {
+        my $num_of_args = $self->has_args($method);
+        my @r_args = splice (@args,0,$num_of_args);
+        $rv = $self->$method(@r_args);
     }
-    else {
-        # try to pass through to Selenium::Remote::WebElement
-        my $sel = 'Selenium::Remote::WebElement';
-        my $sub = "${sel}::${name}";
-        goto &$sub if exists &$sub;
-        my ($package, $filename, $line) = caller;
-        die qq(Can't locate object method "$name" via package ")
-            . __PACKAGE__
-            . qq(" (also tried "$sel") at $filename line $line\n);
+    catch {
+        $self->croak($_);
+    };
+    # +2 because of the delegation on _builder
+    local $Test::Builder::Level = $Test::Builder::Level + 2;
+    return $self->$method_to_test( $rv, @args );
+}
+
+sub _check_ok {
+    my $self = shift;
+    my $meth = shift;
+    my $test_name = pop // $meth;
+    my $rv;
+    try {
+        $rv = $self->$meth(@_);
     }
+    catch {
+        $self->croak($_);
+    };
+
+    # +2 because of the delegation on _builder
+    local $Test::Builder::Level = $Test::Builder::Level + 2;
+    return $self->ok($rv,$test_name,@_);
+}
+
+sub clear_ok {
+    my $self = shift;
+    return $self->_check_ok('clear',@_);
+}
+
+sub click_ok {
+    my $self = shift;
+    return $self->_check_ok('click',@_);
+}
+
+sub submit_ok {
+    my $self = shift;
+    return $self->_check_ok('submit',@_);
+}
+
+sub is_selected_ok {
+    my $self = shift;
+    return $self->_check_ok('is_selected',@_);
+}
+
+sub is_enabled_ok {
+    my $self = shift;
+    return $self->_check_ok('is_enabled',@_);
+}
+
+sub is_displayed_ok {
+    my $self = shift;
+    return $self->_check_ok('is_displayed',@_);
+}
+
+sub send_keys_ok {
+    my $self = shift;
+    return $self->_check_ok('send_keys',@_);
+}
+
+
+
+sub text_is {
+    my $self = shift;
+    return $self->_check_method( 'text', 'is_eq', @_ );
+}
+
+sub text_isnt {
+    my $self = shift;
+    return $self->_check_method( 'text', 'isnt_eq', @_ );
+}
+
+sub text_like {
+    my $self = shift;
+    return $self->_check_method( 'text', 'like', @_ );
+}
+
+sub text_unlike {
+    my $self = shift;
+    return $self->_check_method( 'text', 'unlike', @_ );
+}
+
+sub tag_name_is {
+    my $self = shift;
+    return $self->_check_method( 'tag_name', 'is_eq', @_ );
+}
+
+sub tag_name_isnt {
+    my $self = shift;
+    return $self->_check_method( 'tag_name', 'isnt_eq', @_ );
+}
+
+sub tag_name_like {
+    my $self = shift;
+    return $self->_check_method( 'tag_name', 'like', @_ );
+}
+
+sub tag_name_unlike {
+    my $self = shift;
+    return $self->_check_method( 'tag_name', 'unlike', @_ );
+}
+
+sub value_is {
+    my $self = shift;
+    return $self->_check_method( 'value', 'is_eq', @_ );
+}
+
+sub value_isnt {
+    my $self = shift;
+    return $self->_check_method( 'value', 'isnt_eq', @_ );
+}
+
+sub value_like {
+    my $self = shift;
+    return $self->_check_method( 'value', 'like', @_ );
+}
+
+sub value_unlike {
+    my $self = shift;
+    return $self->_check_method( 'value', 'unlike', @_ );
+}
+
+sub attribute_is {
+    my $self = shift;
+    return $self->_check_method( 'attribute', 'is_eq', @_ );
+}
+
+sub attribute_isnt {
+    my $self = shift;
+    return $self->_check_method( 'attribute', 'isnt_eq', @_ );
+}
+
+sub attribute_like {
+    my $self = shift;
+    return $self->_check_method( 'attribute', 'like', @_ );
+}
+
+sub attribute_unlike {
+    my $self = shift;
+    return $self->_check_method( 'attribute', 'unlike', @_ );
 }
 
 1;

+ 186 - 171
t/01-driver.t

@@ -6,35 +6,32 @@ use Net::Ping;
 use Data::Dumper;
 
 BEGIN {
-   unless (use_ok( 'Selenium::Remote::Driver'))
-   {
-      BAIL_OUT ("Couldn't load Driver");
-      exit;
-   }
-
-   if (defined $ENV{'WD_MOCKING_RECORD'} && ($ENV{'WD_MOCKING_RECORD'}==1))
-   {
-      use t::lib::MockSeleniumWebDriver;
-      my $p = Net::Ping->new("tcp", 2);
-      $p->port_number(4444);
-      unless ($p->ping('localhost')) {
-         plan skip_all => "Selenium server is not running on localhost:4444";
-         exit;
-      }
-      warn "\n\nRecording...\n\n";
-   }
+    unless (use_ok( 'Selenium::Remote::Driver'))
+    {
+        BAIL_OUT ("Couldn't load Driver");
+        exit;
+    }
+
+    if (defined $ENV{'WD_MOCKING_RECORD'} && ($ENV{'WD_MOCKING_RECORD'}==1)) {
+        use t::lib::MockSeleniumWebDriver;
+        my $p = Net::Ping->new("tcp", 2);
+        $p->port_number(4444);
+        unless ($p->ping('localhost')) {
+            plan skip_all => "Selenium server is not running on localhost:4444";
+            exit;
+        }
+        warn "\n\nRecording...\n\n";
+    }
 }
 
 my $record = (defined $ENV{'WD_MOCKING_RECORD'} && ($ENV{'WD_MOCKING_RECORD'}==1))?1:0;
 my $os  = $^O;
-if ($os =~ m/(aix|freebsd|openbsd|sunos|solaris)/)
-{
-   $os = 'linux';
+if ($os =~ m/(aix|freebsd|openbsd|sunos|solaris)/) {
+    $os = 'linux';
 }
 my $mock_file = "01-driver-mock-$os.json";
-if (!$record && !(-e "t/mock-recordings/$mock_file"))
-{
-   plan skip_all => "Mocking of tests is not been enabled for this platform";
+if (!$record && !(-e "t/mock-recordings/$mock_file")) {
+    plan skip_all => "Mocking of tests is not been enabled for this platform";
 }
 t::lib::MockSeleniumWebDriver::register($record,"t/mock-recordings/$mock_file");
 
@@ -43,193 +40,211 @@ my $website = 'http://localhost:63636';
 my $ret;
 
 CHECK_DRIVER: {
-                ok(defined $driver, 'Object loaded fine...');
-                ok($driver->isa('Selenium::Remote::Driver'), '...and of right type');
-                ok(defined $driver->{'session_id'}, 'Established session on remote server');
-                $ret = $driver->get_capabilities;
-                is($ret->{'browserName'}, 'firefox', 'Right capabilities');
-                my $status = $driver->status;
-                ok($status->{build}->{version},"Got status build.version");
-                ok($status->{build}->{revision},"Got status build.revision");
-                ok($status->{build}->{time},"Got status build.time");
-              }
+    ok(defined $driver, 'Object loaded fine...');
+    ok($driver->isa('Selenium::Remote::Driver'), '...and of right type');
+    ok(defined $driver->{'session_id'}, 'Established session on remote server');
+    $ret = $driver->get_capabilities;
+    is($ret->{'browserName'}, 'firefox', 'Right capabilities');
+    my $status = $driver->status;
+    ok($status->{build}->{version},"Got status build.version");
+    ok($status->{build}->{revision},"Got status build.revision");
+    ok($status->{build}->{time},"Got status build.time");
+}
 
 IME: {
-    SKIP: {
-    eval {$driver->available_engines;};
-    if($@) {
-      skip "ime not available on this system",3;
-    }
+  SKIP: {
+        eval {$driver->available_engines;};
+        if ($@) {
+            skip "ime not available on this system",3;
+        }
     };
 }
 
 LOAD_PAGE: {
-                $driver->get("$website/index.html");
-                pass('Loaded home page');
-                $ret = $driver->get_title();
-                is($ret, 'Hello WebDriver', 'Got the title');
-                $ret = $driver->get_current_url();
-                ok($ret =~ m/$website/i, 'Got proper URL');
-           }
+    $driver->get("$website/index.html");
+    pass('Loaded home page');
+    $ret = $driver->get_title();
+    is($ret, 'Hello WebDriver', 'Got the title');
+    $ret = $driver->get_current_url();
+    ok($ret =~ m/$website/i, 'Got proper URL');
+}
 
 WINDOW: {
-            $ret = $driver->get_current_window_handle();
-            ok($ret =~ m/^{.*}$/, 'Proper window handle received');
-            $ret = $driver->get_window_handles();
-            is(ref $ret, 'ARRAY', 'Received all window handles');
-            $ret = $driver->set_window_position(100,100);
-            is($ret, 1, 'Set the window position to 100, 100');
-            $ret = $driver->get_window_position();
-            is ($ret->{'x'}, 100, 'Got the right X Co-ordinate');
-            is ($ret->{'y'}, 100, 'Got the right Y Co-ordinate');
-            $ret = $driver->set_window_size(640, 480);
-            is($ret, 1, 'Set the window size to 640x480');
-            $ret = $driver->get_window_size();
-            is ($ret->{'height'}, 640, 'Got the right height');
-            is ($ret->{'width'}, 480, 'Got the right width');
-            $ret = $driver->get_page_source();
-            ok($ret =~ m/^<html/i, 'Received page source');
-            eval {$driver->set_implicit_wait_timeout(20001);};
-            ok(!$@,"Set implicit wait timeout");
-            eval {$driver->set_implicit_wait_timeout(0);};
-            ok(!$@,"Reset implicit wait timeout");
-            $ret = $driver->get("$website/frameset.html");
-            $ret = $driver->switch_to_frame('second');
-        }
+    $ret = $driver->get_current_window_handle();
+    ok($ret =~ m/^{.*}$/, 'Proper window handle received');
+    $ret = $driver->get_window_handles();
+    is(ref $ret, 'ARRAY', 'Received all window handles');
+    $ret = $driver->set_window_position(100,100);
+    is($ret, 1, 'Set the window position to 100, 100');
+    $ret = $driver->get_window_position();
+    is ($ret->{'x'}, 100, 'Got the right X Co-ordinate');
+    is ($ret->{'y'}, 100, 'Got the right Y Co-ordinate');
+    $ret = $driver->set_window_size(640, 480);
+    is($ret, 1, 'Set the window size to 640x480');
+    $ret = $driver->get_window_size();
+    is ($ret->{'height'}, 640, 'Got the right height');
+    is ($ret->{'width'}, 480, 'Got the right width');
+    $ret = $driver->get_page_source();
+    ok($ret =~ m/^<html/i, 'Received page source');
+    eval {$driver->set_implicit_wait_timeout(20001);};
+    ok(!$@,"Set implicit wait timeout");
+    eval {$driver->set_implicit_wait_timeout(0);};
+    ok(!$@,"Reset implicit wait timeout");
+    $ret = $driver->get("$website/frameset.html");
+    $ret = $driver->switch_to_frame('second');
+}
 
 COOKIES: {
-            $driver->get("$website/cookies.html");
-            $ret = $driver->get_all_cookies();
-            is(@{$ret}, 2, 'Got 2 cookies');
-            $ret = $driver->delete_all_cookies();
-            pass('Deleting cookies...');
-            $ret = $driver->get_all_cookies();
-            is(@{$ret}, 0, 'Deleted all cookies.');
-            $ret = $driver->add_cookie('foo', 'bar', '/', 'localhost', 0);
-            pass('Adding cookie foo...');
-            $ret = $driver->get_all_cookies();
-            is(@{$ret}, 1, 'foo cookie added.');
-            is($ret->[0]{'secure'}, 0, 'foo cookie insecure.');
-            $ret = $driver->delete_cookie_named('foo');
-            pass('Deleting cookie foo...');
-            $ret = $driver->get_all_cookies();
-            is(@{$ret}, 0, 'foo cookie deleted.');
-            $ret = $driver->delete_all_cookies();
-         }
+    $driver->get("$website/cookies.html");
+    $ret = $driver->get_all_cookies();
+    is(@{$ret}, 2, 'Got 2 cookies');
+    $ret = $driver->delete_all_cookies();
+    pass('Deleting cookies...');
+    $ret = $driver->get_all_cookies();
+    is(@{$ret}, 0, 'Deleted all cookies.');
+    $ret = $driver->add_cookie('foo', 'bar', '/', 'localhost', 0);
+    pass('Adding cookie foo...');
+    $ret = $driver->get_all_cookies();
+    is(@{$ret}, 1, 'foo cookie added.');
+    is($ret->[0]{'secure'}, 0, 'foo cookie insecure.');
+    $ret = $driver->delete_cookie_named('foo');
+    pass('Deleting cookie foo...');
+    $ret = $driver->get_all_cookies();
+    is(@{$ret}, 0, 'foo cookie deleted.');
+    $ret = $driver->delete_all_cookies();
+}
 
 MOVE: {
-        $driver->get("$website/index.html");
-        $driver->get("$website/formPage.html");
-        $ret = $driver->go_back();
-        pass('Clicked Back...');
-        $ret = $driver->get_title();
-        is($ret, 'Hello WebDriver', 'Got the right title');
-        $ret = $driver->go_forward();
-        pass('Clicked Forward...');
-        $ret = $driver->get_title();
-        is($ret, 'We Leave From Here', 'Got the right title');
-        $ret = $driver->refresh();
-        pass('Clicked Refresh...');
-        $ret = $driver->get_title();
-        is($ret, 'We Leave From Here', 'Got the right title');
-      }
+    $driver->get("$website/index.html");
+    $driver->get("$website/formPage.html");
+    $ret = $driver->go_back();
+    pass('Clicked Back...');
+    $ret = $driver->get_title();
+    is($ret, 'Hello WebDriver', 'Got the right title');
+    $ret = $driver->go_forward();
+    pass('Clicked Forward...');
+    $ret = $driver->get_title();
+    is($ret, 'We Leave From Here', 'Got the right title');
+    $ret = $driver->refresh();
+    pass('Clicked Refresh...');
+    $ret = $driver->get_title();
+    is($ret, 'We Leave From Here', 'Got the right title');
+}
 
 FIND: {
-        my $elem = $driver->find_element("//input[\@id='checky']");
-        ok($elem->isa('Selenium::Remote::WebElement'), 'Got WebElement via Xpath');
-        $elem = $driver->find_element('checky', 'id');
-        ok($elem->isa('Selenium::Remote::WebElement'), 'Got WebElement via Id');
-        $elem = $driver->find_element('checky', 'name');
-        ok($elem->isa('Selenium::Remote::WebElement'), 'Got WebElement via Name');
-
-        $elem = $driver->find_element('multi', 'id');
-        $elem = $driver->find_child_element($elem, "option");
-        ok($elem->isa('Selenium::Remote::WebElement'), 'Got child WebElement...');
-        $ret = $elem->get_value();
-        is($ret, 'Eggs', '...right child WebElement');
-        $ret = $driver->find_child_elements($elem, "//option[\@selected='selected']");
-        is(@{$ret}, 4, 'Got 4 WebElements');
-        my $expected_err = "An element could not be located on the page using the "
-         . "given search parameters: "
-         . "element_that_doesnt_exist,id"
-        # the following needs to always be right before the eval
-         . " at " . __FILE__ . " line " . (__LINE__+1);
-        eval { $driver->find_element("element_that_doesnt_exist","id"); };
-        chomp $@;
-        is($@,$expected_err.".","find_element croaks properly");
-      }
+    my $elem = $driver->find_element("//input[\@id='checky']");
+    ok($elem->isa('Selenium::Remote::WebElement'), 'Got WebElement via Xpath');
+    $elem = $driver->find_element('checky', 'id');
+    ok($elem->isa('Selenium::Remote::WebElement'), 'Got WebElement via Id');
+    $elem = $driver->find_element('checky', 'name');
+    ok($elem->isa('Selenium::Remote::WebElement'), 'Got WebElement via Name');
+
+    $elem = $driver->find_element('multi', 'id');
+    $elem = $driver->find_child_element($elem, "option");
+    ok($elem->isa('Selenium::Remote::WebElement'), 'Got child WebElement...');
+    $ret = $elem->get_value();
+    is($ret, 'Eggs', '...right child WebElement');
+    $ret = $driver->find_child_elements($elem, "//option[\@selected='selected']");
+    is(@{$ret}, 4, 'Got 4 WebElements');
+    my $expected_err = "An element could not be located on the page using the "
+    . "given search parameters: "
+    . "element_that_doesnt_exist,id"
+    # the following needs to always be right before the eval
+    . " at " . __FILE__ . " line " . (__LINE__+1);
+    eval { $driver->find_element("element_that_doesnt_exist","id"); };
+    chomp $@;
+    is($@,$expected_err.".","find_element croaks properly");
+}
 
 EXECUTE: {
-        my $script = q{
+    my $script = q{
           var arg1 = arguments[0];
           var elem = window.document.getElementById(arg1);
           return elem;
         };
-        my $elem = $driver->execute_script($script,'checky');
-        ok($elem->isa('Selenium::Remote::WebElement'), 'Executed script');
-        is($elem->get_attribute('id'),'checky','Execute found proper element');
-        $script = q{
+    my $elem = $driver->execute_script($script,'checky');
+    ok($elem->isa('Selenium::Remote::WebElement'), 'Executed script');
+    is($elem->get_attribute('id'),'checky','Execute found proper element');
+    $script = q{
           var links = window.document.links
           var length = links.length
           var results = new Array(length)
           while(length--) results[length] = links[length];
           return results;
         };
-        $elem = $driver->execute_script($script);
-        ok($elem, 'Got something back from execute_script');
-        isa_ok($elem, 'ARRAY', 'What we got back is an ARRAY ref');
-        ok(scalar(@$elem), 'There are elements in our array ref');
-        foreach my $element (@$elem) {
-            isa_ok($element, 'Selenium::Remote::WebElement', 'Element was converted to a WebElement object');
-        }
-        $script = q{
+    $elem = $driver->execute_script($script);
+    ok($elem, 'Got something back from execute_script');
+    isa_ok($elem, 'ARRAY', 'What we got back is an ARRAY ref');
+    ok(scalar(@$elem), 'There are elements in our array ref');
+    foreach my $element (@$elem) {
+        isa_ok($element, 'Selenium::Remote::WebElement', 'Element was converted to a WebElement object');
+    }
+    $script = q{
           var arg1 = arguments[0];
           var callback = arguments[arguments.length-1];
           var elem = window.document.getElementById(arg1);
           callback(elem);
         };
-        $elem = $driver->execute_async_script($script,'multi');
-        ok($elem->isa('Selenium::Remote::WebElement'),'Executed async script');
-        is($elem->get_attribute('id'),'multi','Async found proper element');
+    $elem = $driver->execute_async_script($script,'multi');
+    ok($elem->isa('Selenium::Remote::WebElement'),'Executed async script');
+    is($elem->get_attribute('id'),'multi','Async found proper element');
 }
 
 ALERT: {
-        $driver->get("$website/alerts.html");
-        $driver->find_element("alert",'id')->click;
-        is($driver->get_alert_text,'cheese','alert text match');
-        eval {$driver->dismiss_alert;};
-        ok(!$@,"dismissed alert");
-        $driver->find_element("prompt",'id')->click;
-        is($driver->get_alert_text,'Enter your name','prompt text match');
-        $driver->send_keys_to_prompt("Larry Wall");
-        eval {$driver->accept_alert;};
-        ok(!$@,"accepted prompt");
-        is($driver->get_alert_text,'Larry Wall','keys sent to prompt');
-        $driver->dismiss_alert;
-        $driver->find_element("confirm",'id')->click;
-        is($driver->get_alert_text,"Are you sure?",'confirm text match');
-        eval {$driver->dismiss_alert;};
-        ok(!$@,"dismissed confirm");
-        is($driver->get_alert_text,'false',"dismissed confirmed correct");
-        $driver->accept_alert;
-        $driver->find_element("confirm",'id')->click;
-        eval {$driver->accept_alert;};
-        ok(!$@,"accepted confirm");
-        is($driver->get_alert_text,'true',"accept confirm correct");
-        $driver->accept_alert;
+    $driver->get("$website/alerts.html");
+    $driver->find_element("alert",'id')->click;
+    is($driver->get_alert_text,'cheese','alert text match');
+    eval {$driver->dismiss_alert;};
+    ok(!$@,"dismissed alert");
+    $driver->find_element("prompt",'id')->click;
+    is($driver->get_alert_text,'Enter your name','prompt text match');
+    $driver->send_keys_to_prompt("Larry Wall");
+    eval {$driver->accept_alert;};
+    ok(!$@,"accepted prompt");
+    is($driver->get_alert_text,'Larry Wall','keys sent to prompt');
+    $driver->dismiss_alert;
+    $driver->find_element("confirm",'id')->click;
+    is($driver->get_alert_text,"Are you sure?",'confirm text match');
+    eval {$driver->dismiss_alert;};
+    ok(!$@,"dismissed confirm");
+    is($driver->get_alert_text,'false',"dismissed confirmed correct");
+    $driver->accept_alert;
+    $driver->find_element("confirm",'id')->click;
+    eval {$driver->accept_alert;};
+    ok(!$@,"accepted confirm");
+    is($driver->get_alert_text,'true',"accept confirm correct");
+    $driver->accept_alert;
 }
 
 PAUSE: {
-       my $starttime=time();
-       $driver->pause();
-       my $endtime=time();
-       ok($starttime <= $endtime-1,"starttime <= endtime+1"); # Slept at least 1 second
-       ok($starttime >= $endtime-2,"starttime >= endtime-2"); # Slept at most 2 seconds
+    my $starttime=time();
+    $driver->pause();
+    my $endtime=time();
+    ok($starttime <= $endtime-1,"starttime <= endtime+1"); # Slept at least 1 second
+    ok($starttime >= $endtime-2,"starttime >= endtime-2"); # Slept at most 2 seconds
+}
+
+AUTO_CLOSE: {
+    my $stayOpen = Selenium::Remote::Driver->new(
+        browser_name => 'firefox',
+        auto_close => 0
+    );
+
+    $stayOpen->DESTROY();
+    ok(defined $stayOpen->{'session_id'}, 'auto close in init hash is respected');
+    $stayOpen->auto_close(1);
+    $stayOpen->DESTROY();
+    ok(!defined $stayOpen->{'session_id'}, 'true for auto close is still respected');
+
+    $driver->auto_close(0);
+    $driver->DESTROY();
+    ok(defined $driver->{'session_id'}, 'changing autoclose on the fly keeps the session open');
+    $driver->auto_close(1);
 }
 
 QUIT: {
-        $ret = $driver->quit();
-        ok((not defined $driver->{'session_id'}), 'Killed the remote session');
-      }
+    $ret = $driver->quit();
+    ok((not defined $driver->{'session_id'}), 'Killed the remote session');
+}
 
 done_testing;

+ 1 - 1
t/03-spec-coverage.t

@@ -63,7 +63,7 @@ for my $line (@lines) {
     push @methods, $method;
   }
 }
-my $commands = Selenium::Remote::Commands->new;
+my $commands = Selenium::Remote::Commands->new->get_cmds;
 SOURCE_COMMAND: for my $method_source (@methods) {
   my $command = "$method_source->{method} $method_source->{path}";
   my $msg     = "Looking for '$command'";

+ 2 - 1
t/04-commands-implemented.t

@@ -2,6 +2,7 @@
 use strict;
 use warnings;
 
+# TODO: find another way to do this checking, this is so fragile
 use Selenium::Remote::Commands;
 use Test::More;
 
@@ -9,7 +10,7 @@ unless($ENV{RELEASE_TESTING}) {
   plan(skip_all=>"Author tests not required for installation.");
 }
 
-my $comm = Selenium::Remote::Commands->new;
+my $comm = Selenium::Remote::Commands->new->get_cmds;
 for my $command (keys %{$comm}) {
   my $found_command = 0;
   for my $file (

+ 1 - 1
t/Test-Selenium-Remote-Driver-google.t

@@ -8,7 +8,7 @@ unless ($host and $port) {
     exit 0;
 }
 
-# Try to find 
+# Try to find
 my $t = Test::Selenium::Remote::Driver->new(
     remote_server_addr => $host, port => $port,
 );

+ 4 - 4
t/Test-Selenium-Remote-Driver.t

@@ -41,7 +41,7 @@ my $element = Test::Selenium::Remote::WebElement->new(
 {
     $successful_driver->mock('find_element', sub { $element } );
     check_tests(
-      sub { 
+      sub {
           my $rc = $successful_driver->find_element_ok('q', 'find_element_ok works');
           is($rc,1,'returns true');
       },
@@ -61,7 +61,7 @@ my $element = Test::Selenium::Remote::WebElement->new(
 
     $successful_driver->mock('find_element', sub { 0 } );
     check_tests(
-      sub { 
+      sub {
           my $rc = $successful_driver->find_element_ok('q', 'find_element_ok works, falsey test');
           is($rc,0,'returns false');
       },
@@ -84,7 +84,7 @@ my $element = Test::Selenium::Remote::WebElement->new(
 {
     $successful_driver->mock('find_element', sub { die } );
     check_tests(
-      sub { 
+      sub {
           my $rc = $successful_driver->find_no_element_ok('BOOM', 'find_no_element_ok works, expecting to find nothing.');
           is($rc,1,'returns true');
       },
@@ -96,7 +96,7 @@ my $element = Test::Selenium::Remote::WebElement->new(
           },
           {
             ok => 1,
-            name => "returns false",
+            name => "returns true",
             diag => "",
           },
       ]

+ 153 - 105
t/Test-Selenium-Remote-WebElement.t

@@ -7,30 +7,32 @@ use Test::Selenium::Remote::WebElement;
 
 # Start off by faking a bunch of Selenium::Remote::WebElement calls succeeding
 my $successful_element = Test::Selenium::Remote::WebElement->new;
-$successful_element =  Test::MockObject::Extends->new( $successful_element );
-
-$successful_element->set_true(qw/
-        clear
-        click
-        submit
-        is_selected
-        is_enabled
-        is_displayed
-        send_keys
-        /);
-
-$successful_element->set_list('get_tag_name','iframe');
-$successful_element->set_list('get_value','my_value');
-$successful_element->set_list('get_text','my_text');
+$successful_element = Test::MockObject::Extends->new($successful_element);
+
+$successful_element->set_true(
+    qw/
+      clear
+      click
+      submit
+      is_selected
+      is_enabled
+      is_displayed
+      send_keys
+      /
+);
+
+$successful_element->mock( 'get_tag_name', sub {'iframe'} );
+$successful_element->mock( 'get_value',    sub {'my_value'} );
+$successful_element->mock( 'get_text',     sub {"my_text\nis fantastic"} );
 
 # Given input 'foo' to 'get_attribute', return 'my_foo';
-$successful_element->mock('get_attribute',sub { 'my_'.$_[1] } );
+$successful_element->mock( 'get_attribute', sub { 'my_' . $_[1] } );
 
 check_test(
   sub { $successful_element->clear_ok },
   {
     ok => 1,
-    name => "clear... no return value",
+    name => "clear",
     diag => "",
   }
 );
@@ -39,7 +41,7 @@ check_test(
   sub { $successful_element->clear_ok('test_name') },
   {
     ok => 1,
-    name => "test_name... no return value",
+    name => "test_name",
     diag => "",
   }
 );
@@ -48,7 +50,7 @@ check_test(
   sub { $successful_element->click_ok },
   {
     ok => 1,
-    name => "click... no return value",
+    name => "click",
     diag => "",
   }
 );
@@ -57,7 +59,7 @@ check_test(
   sub { $successful_element->submit_ok },
   {
     ok => 1,
-    name => "submit... no return value",
+    name => "submit",
     diag => "",
   }
 );
@@ -93,7 +95,7 @@ check_test(
   sub { $successful_element->send_keys_ok('Hello World', 'I sent keys') },
   {
     ok => 1,
-    name => "I sent keys... no return value",
+    name => "I sent keys",
     diag => "",
   }
 );
@@ -101,136 +103,182 @@ check_test(
 # tag_name_*
 {
     check_test(
-      sub { $successful_element->tag_name_is('iframe','Got an iframe tag?') },
-      {
-        ok => 1,
-        name => "Got an iframe tag?",
-        diag => "",
-      }
+        sub {
+            $successful_element->tag_name_is( 'iframe', 'Got an iframe tag?' );
+        },
+        {   ok   => 1,
+            name => "Got an iframe tag?",
+            diag => "",
+        }
     );
 
     check_test(
-      sub { $successful_element->tag_name_isnt('BOOM','Not BOOM.') },
-      {
-        ok => 1,
-        name => "Not BOOM.",
-        diag => "",
-      }
+        sub { $successful_element->tag_name_isnt( 'BOOM', 'Not BOOM.' ) },
+        {   ok   => 1,
+            name => "Not BOOM.",
+            diag => "",
+        }
     );
 
     check_test(
-      sub { $successful_element->tag_name_like(qr/frame/,'Matches iframe tag?') },
-      {
-        ok => 1,
-        name => "Matches iframe tag?",
-        diag => "",
-      }
+        sub {
+            $successful_element->tag_name_like( qr/frame/,
+                'Matches iframe tag?' );
+        },
+        {   ok   => 1,
+            name => "Matches iframe tag?",
+            diag => "",
+        }
     );
 
     check_test(
-      sub { $successful_element->tag_name_unlike(qr/BOOM/,"tag_name doesn't match BOOM") },
-      {
-        ok => 1,
-        name => "tag_name doesn't match BOOM",
-        diag => "",
-      }
+        sub {
+            $successful_element->tag_name_unlike( qr/BOOM/,
+                "tag_name doesn't match BOOM" );
+        },
+        {   ok   => 1,
+            name => "tag_name doesn't match BOOM",
+            diag => "",
+        }
     );
 }
+
 # value_*
 {
     check_test(
-      sub { $successful_element->value_is('my_value','Got an my_value value?') },
-      {
-        ok => 1,
-        name => "Got an my_value value?",
-        diag => "",
-      }
+        sub {
+            $successful_element->value_is( 'my_value',
+                'Got an my_value value?' );
+        },
+        {   ok   => 1,
+            name => "Got an my_value value?",
+            diag => "",
+        }
     );
 
     check_test(
-      sub { $successful_element->value_isnt('BOOM','Not BOOM.') },
-      {
-        ok => 1,
-        name => "Not BOOM.",
-        diag => "",
-      }
+        sub { $successful_element->value_isnt( 'BOOM', 'Not BOOM.' ) },
+        {   ok   => 1,
+            name => "Not BOOM.",
+            diag => "",
+        }
     );
 
     check_test(
-      sub { $successful_element->value_like(qr/val/,'Matches my_value value?') },
-      {
-        ok => 1,
-        name => "Matches my_value value?",
-        diag => "",
-      }
+        sub {
+            $successful_element->value_like( qr/val/,
+                'Matches my_value value?' );
+        },
+        {   ok   => 1,
+            name => "Matches my_value value?",
+            diag => "",
+        }
     );
 
     check_test(
-      sub { $successful_element->value_unlike(qr/BOOM/,"value doesn't match BOOM") },
-      {
-        ok => 1,
-        name => "value doesn't match BOOM",
-        diag => "",
-      }
+        sub {
+            $successful_element->value_unlike( qr/BOOM/,
+                "value doesn't match BOOM" );
+        },
+        {   ok   => 1,
+            name => "value doesn't match BOOM",
+            diag => "",
+        }
     );
 }
 
 # text_*
 {
     check_test(
-      sub { $successful_element->text_is('my_text','Got an my_text value?') },
-      {
-        ok => 1,
-        name => "Got an my_text value?",
-        diag => "",
-      }
+        sub {
+            $successful_element->text_is( "my_text\nis fantastic",
+                'Got an my_text value?' );
+        },
+        {   ok   => 1,
+            name => "Got an my_text value?",
+            diag => "",
+        }
     );
 
     check_test(
-      sub { $successful_element->text_isnt('BOOM','Not BOOM.') },
-      {
-        ok => 1,
-        name => "Not BOOM.",
-        diag => "",
-      }
+        sub { $successful_element->text_isnt( 'BOOM', 'Not BOOM.' ) },
+        {   ok   => 1,
+            name => "Not BOOM.",
+            diag => "",
+        }
     );
 
     check_test(
-      sub { $successful_element->text_like(qr/tex/,'Matches my_text value?') },
-      {
-        ok => 1,
-        name => "Matches my_text value?",
-        diag => "",
-      }
+        sub {
+            $successful_element->text_like( qr/tex/,
+                'Matches my_text value?' );
+        },
+        {   ok   => 1,
+            name => "Matches my_text value?",
+            diag => "",
+        }
     );
 
     check_test(
-      sub { $successful_element->text_unlike(qr/BOOM/,"text doesn't match BOOM") },
-      {
-        ok => 1,
-        name => "text doesn't match BOOM",
-        diag => "",
-      }
+        sub {
+            $successful_element->text_unlike( qr/BOOM/,
+                "text doesn't match BOOM" );
+        },
+        {   ok   => 1,
+            name => "text doesn't match BOOM",
+            diag => "",
+        }
     );
+
 }
+{ 
+    check_test(
+        sub {
+            $successful_element->attribute_is( 'foo', 'my_foo',
+                'attribute_is matched' );
+        },
+        {   ok   => 1,
+            name => "attribute_is matched",
+            diag => "",
+        }
+    );
 
-#  attribute_is($attr_name,$match_str,$test_name);
-#  attribute_isnt($attr_name,$match_str,$test_name);
-#  attribute_like($attr_name,$match_re,$test_name);
-#  attribute_unlike($attr_name,$match_re,$test_name);
-{
-    local $TODO = 'not implemented yet.';
     check_test(
-      sub { $successful_element->attribute_is('foo', 'my_foo', 'attribute_is matched') },
-      {
-        ok => 1,
-        name => "attribute_is matched",
-        diag => "",
-      }
+        sub {
+            $successful_element->attribute_isnt( 'foo', 'not_foo',
+                'attribute_is not_foo' );
+        },
+        {   ok   => 1,
+            name => "attribute_is not_foo",
+            diag => "",
+        }
     );
 
+    check_test(
+        sub {
+            $successful_element->attribute_like( 'foo',qr/foo/,
+                'Matches my_attribute' );
+        },
+        {   ok   => 1,
+            name => "Matches my_attribute",
+            diag => "",
+        }
+    );
+
+    check_test(
+        sub {
+            $successful_element->attribute_unlike( 'bar',qr/foo/,
+                "Attribute does not match foo" );
+        },
+        {   ok   => 1,
+            name => "Attribute does not match foo",
+            diag => "",
+        }
+    );
 }
 
+
+
 #  css_attribute_is($attr_name,$match_str,$test_name);
 #  css_attribute_isnt($attr_name,$match_str,$test_name);
 #  css_attribute_like($attr_name,$match_re,$test_name);

+ 9 - 2
t/bin/generate-recordings.pl

@@ -9,8 +9,15 @@ unless (-d "t" && -f "dist.ini" && -f "t/01-driver.t" && -f "t/02-webelement.t")
 }
 
 startServer();
+if ($^O eq 'linux') {
+    print "Headless and need a webdriver server started? Try\n\n\tDISPLAY=:1 xvfb-run --auto-servernum java -jar /usr/lib/node_modules/protractor/selenium/selenium-server-standalone-2.40.0.jar\n\n";
+}
+
+my $export = $^O eq 'MSWin32' ? 'set' : 'export';
+my $srdLib = glob('Selenium-Remote-Driver*/lib');
+
 print `dzil build`;
-print `export WD_MOCKING_RECORD=1 && perl -I"Selenium-Remote-Driver/lib" -w t/01-driver.t & perl -I"Selenium-Remote-Driver/lib" -w t/02-webelement.t & wait`;
+print `$export WD_MOCKING_RECORD=1 && perl -I$srdLib t/01-driver.t && perl -I$srdLib t/02-webelement.t`;
 killServer();
 
 sub startServer {
@@ -26,6 +33,6 @@ sub killServer {
         system("taskkill /FI \"WINDOWTITLE eq TEMP_HTTP_SERVER\"");
     }
     else {
-        `ps aux | grep http-server\.pl | grep perl | awk '{print \$2}' | xargs kill`;
+        `ps aux | grep [h]ttp-server\.pl  | awk '{print \$2}' | xargs kill`;
     }
 }

+ 14 - 2
t/lib/MockSeleniumWebDriver.pm

@@ -2,7 +2,7 @@ package t::lib::MockSeleniumWebDriver;
 use strict;
 use warnings;
 
-use LWP::Protocol::PSGI;
+use LWP::Protocol::PSGI 0.04;
 use JSON;
 
 our $MockSeleniumWebDriverObj;
@@ -57,9 +57,14 @@ sub psgi_app {
   }
   my $req_index = \$self->{req_index};
   if (!$self->{record}) {
+    my $expected = $self->{req_resp}->[$$req_index]->{request}->{content};
+    $expected = $expected eq "" ? $expected : decode_json($expected);
+    my $actual = $content eq "" ? $content : decode_json($content);
+
     if (  $self->{req_resp}->[$$req_index]->{request}->{verb} eq $env->{REQUEST_METHOD}
       and $self->{req_resp}->[$$req_index]->{request}->{uri} eq $uri
-      and $self->{req_resp}->[$$req_index]->{request}->{content} eq $content) {
+      and (   $self->{req_resp}->[$$req_index]->{request}->{content} eq $content
+           or deeply_equal($expected, $actual)))  {
       return $self->{req_resp}->[$$req_index++]->{response};
     } else {
       die
@@ -92,6 +97,13 @@ sub psgi_app {
   }
 }
 
+sub deeply_equal {
+  my ( $a_ref, $b_ref ) = @_;
+
+  local $Storable::canonical = 1;
+  return Storable::freeze( $a_ref ) eq Storable::freeze( $b_ref );
+}
+
 sub DESTROY {
   my ($self) = @_;
   if($self->{record}) {

File diff suppressed because it is too large
+ 0 - 0
t/mock-recordings/01-driver-mock-MSWin32.json


File diff suppressed because it is too large
+ 0 - 0
t/mock-recordings/01-driver-mock-darwin.json


File diff suppressed because it is too large
+ 0 - 0
t/mock-recordings/01-driver-mock-linux.json


File diff suppressed because it is too large
+ 0 - 0
t/mock-recordings/02-webelement-mock-MSWin32.json


File diff suppressed because it is too large
+ 0 - 0
t/mock-recordings/02-webelement-mock-darwin.json


File diff suppressed because it is too large
+ 0 - 0
t/mock-recordings/02-webelement-mock-linux.json


Some files were not shown because too many files changed in this diff