Spec.pm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. package Selenium::Remote::Spec;
  2. use strict;
  3. use warnings;
  4. # ABSTRACT: Implement commands for Selenium::Remote::Driver
  5. =head1 DESCRIPTION
  6. Defines all the HTTP endpoints available to execute on a selenium server.
  7. If you have either a customized Selenium Server, or want new features
  8. you should update the _cmds hash.
  9. =for Pod::Coverage *EVERYTHING*
  10. =cut
  11. use Carp qw{croak};
  12. use List::Util qw{any};
  13. use Moo;
  14. extends 'Selenium::Remote::Commands';
  15. #Ripped from the headlines: https://w3c.github.io/webdriver/webdriver-spec.html
  16. #then add 2 params for our use
  17. #Method URI Template no_content_success internal_name Command
  18. our $spec = qq{
  19. POST session 0 newSession New Session
  20. POST session 0 getCapabilities Get Capabilities (v2->v3 shim)
  21. DELETE session/:sessionId 1 quit Delete Session
  22. GET status 0 status Status
  23. GET session/:sessionId/timeouts 0 getTimeouts Get Timeouts
  24. POST session/:sessionId/timeouts 1 setTimeout Set Page Load timeout (v2->v3 shim)
  25. POST session/:sessionId/timeouts/async_script 1 setAsyncScriptTimeout Set Async script timeout (v2->v3 shim)
  26. POST session/:sessionId/timeouts/implicit_wait 1 setImplicitWaitTimeout Set Implicit wait timeout (v2->v3 shim)
  27. POST session/:sessionId/url 1 get Navigate To
  28. GET session/:sessionId/url 0 getCurrentUrl Get Current URL
  29. POST session/:sessionId/back 1 goBack Back
  30. POST session/:sessionId/forward 1 goForward Forward
  31. POST session/:sessionId/refresh 1 refresh Refresh
  32. GET session/:sessionId/title 0 getTitle Get Title
  33. GET session/:sessionId/window 0 getCurrentWindowHandle Get Currently Focused Window Handle
  34. DELETE session/:sessionId/window 1 close Close Currently Focused Window
  35. POST session/:sessionId/window 1 switchToWindow Switch To Window
  36. GET session/:sessionId/window/handles 0 getWindowHandles Get Window Handles
  37. POST session/:sessionId/frame 1 switchToFrame Switch To Frame
  38. POST session/:sessionId/frame/parent 1 switchToParentFrame Switch To Parent Frame
  39. GET session/:sessionId/window/rect 0 getWindowRect Get Window Size/Position (v2->v3 shim)
  40. POST session/:sessionId/window/rect 1 setWindowRect Set Window Size/Position (v2->v3 shim)
  41. POST session/:sessionId/window/maximize 1 maximizeWindow Maximize Window
  42. POST session/:sessionId/window/minimize 1 minimizeWindow Minimize Window
  43. POST session/:sessionId/window/fullscreen 1 fullscreenWindow Fullscreen Window
  44. GET session/:sessionId/element/active 0 getActiveElement Get Active Element
  45. POST session/:sessionId/element 0 findElement Find Element
  46. POST session/:sessionId/elements 0 findElements Find Elements
  47. POST session/:sessionId/element/:id/element 0 findChildElement Find Element From Element
  48. POST session/:sessionId/element/:id/elements 0 findChildElements Find Elements From Element
  49. GET session/:sessionId/element/:id/selected 0 isElementSelected Is Element Selected
  50. GET session/:sessionId/element/:id/attribute/:name 0 getElementAttribute Get Element Attribute
  51. GET session/:sessionId/element/:id/property/:name 0 getElementProperty Get Element Property
  52. GET session/:sessionId/element/:id/css/:propertyName 0 getElementValueOfCssProperty Get Element CSS Value
  53. GET session/:sessionId/element/:id/text 0 getElementText Get Element Text
  54. GET session/:sessionId/element/:id/name 0 getElementTagName Get Element Tag Name
  55. GET session/:sessionId/element/:id/rect 0 getElementRect Get Element Rect
  56. GET session/:sessionId/element/:id/enabled 0 isElementEnabled Is Element Enabled
  57. POST session/:sessionId/element/:id/click 1 clickElement Element Click
  58. POST session/:sessionId/element/:id/clear 1 clearElement Element Clear
  59. POST session/:sessionId/element/:id/value 1 sendKeysToElement Element Send Keys
  60. GET session/:sessionId/source 0 getPageSource Get Page Source
  61. POST session/:sessionId/execute/sync 0 executeScript Execute Script
  62. POST session/:sessionId/execute/async 0 executeAsyncScript Execute Async Script
  63. GET session/:sessionId/cookie 0 getAllCookies Get All Cookies
  64. GET session/:sessionId/cookie/:name 0 getCookieNamed Get Named Cookie
  65. POST session/:sessionId/cookie 1 addCookie Add Cookie
  66. DELETE session/:sessionId/cookie/:name 1 deleteCookieNamed Delete Cookie
  67. DELETE session/:sessionId/cookie 1 deleteAllCookies Delete All Cookies
  68. POST session/:sessionId/actions 1 generalAction Perform Actions
  69. DELETE session/:sessionId/actions 1 releaseGeneralAction Release Actions
  70. POST session/:sessionId/alert/dismiss 1 dismissAlert Dismiss Alert
  71. POST session/:sessionId/alert/accept 1 acceptAlert Accept Alert
  72. GET session/:sessionId/alert/text 0 getAlertText Get Alert Text
  73. POST session/:sessionId/alert/text 1 sendKeysToPrompt Send Alert Text
  74. GET session/:sessionId/screenshot 0 screenshot Take Screenshot
  75. GET session/:sessionId/moz/screenshot/full 0 mozScreenshotFull Take Full Screenshot
  76. GET session/:sessionId/element/:id/screenshot 0 elementScreenshot Take Element Screenshot
  77. };
  78. our $spec_parsed;
  79. sub get_spec {
  80. return $spec_parsed if $spec_parsed;
  81. my @split = split( /\n/, $spec );
  82. foreach my $line (@split) {
  83. next unless $line;
  84. my ( $method, $uri, $nc_success, $key, @description ) =
  85. split( / +/, $line );
  86. $spec_parsed->{$key} = {
  87. method => $method,
  88. url => $uri,
  89. no_content_success => int($nc_success)
  90. , #XXX this *should* always be 0, but specs lie
  91. description => join( ' ', @description ),
  92. };
  93. }
  94. return $spec_parsed;
  95. }
  96. has '_cmds' => (
  97. is => 'lazy',
  98. reader => 'get_cmds',
  99. builder => \&get_spec,
  100. );
  101. =head1 Webdriver 3 capabilities
  102. WD3 giveth and taketh away some caps. Here's all you get:
  103. Browser name: "browserName" string Identifies the user agent.
  104. Browser version: "browserVersion" string Identifies the version of the user agent.
  105. Platform name: "platformName" string Identifies the operating system of the endpoint node.
  106. Accept insecure TLS certificates: "acceptInsecureCerts" boolean Indicates whether untrusted and self-signed TLS certificates are implicitly trusted on navigation for the duration of the session.
  107. Proxy configuration: "proxy" JSON Defines the current session’s proxy configuration.
  108. New Stuff:
  109. Page load strategy: "pageLoadStrategy" string Defines the current session’s page load strategy.
  110. Window dimensioning/positioning: "setWindowRect" boolean Indicates whether the remote end supports all of the commands in Resizing and Positioning Windows.
  111. Session timeouts configuration: "timeouts" JSON Describes the timeouts imposed on certain session operations.
  112. Unhandled prompt behavior: "unhandledPromptBehavior" string Describes the current session’s user prompt handler.
  113. =cut
  114. has '_caps' => (
  115. is => 'lazy',
  116. reader => 'get_caps',
  117. builder => sub {
  118. return [
  119. 'browserName', 'acceptInsecureCerts',
  120. 'browserVersion', 'platformName',
  121. 'proxy', 'pageLoadStrategy',
  122. 'setWindowRect', 'timeouts',
  123. 'unhandledPromptBehavior', 'moz:firefoxOptions',
  124. 'goog:chromeOptions', 'goog:loggingPrefs',
  125. ];
  126. }
  127. );
  128. has '_caps_map' => (
  129. is => 'lazy',
  130. reader => 'get_caps_map',
  131. builder => sub {
  132. return {
  133. browserName => 'browserName',
  134. acceptSslCerts => 'acceptInsecureCerts',
  135. version => 'browserVersion',
  136. platform => 'platformName',
  137. proxy => 'proxy',
  138. };
  139. }
  140. );
  141. sub get_params {
  142. my ( $self, $args ) = @_;
  143. if ( !( defined $args->{'session_id'} ) ) {
  144. return;
  145. }
  146. #Allow fall-back in the event the command passed doesn't exist
  147. return unless $self->get_cmds()->{ $args->{command} };
  148. my $url = $self->get_url( $args->{command} );
  149. my $data = {};
  150. # Do the var substitutions.
  151. $url =~ s/:sessionId/$args->{'session_id'}/;
  152. $url =~ s/:id/$args->{'id'}/;
  153. $url =~ s/:name/$args->{'name'}/;
  154. $url =~ s/:propertyName/$args->{'property_name'}/;
  155. $url =~ s/:other/$args->{'other'}/;
  156. $url =~ s/:windowHandle/$args->{'window_handle'}/;
  157. $data->{'method'} = $self->get_method( $args->{command} );
  158. $data->{'no_content_success'} =
  159. $self->get_no_content_success( $args->{command} );
  160. $data->{'url'} = $url;
  161. #URL & data polyfills for the way selenium2 used to do things, etc
  162. $data->{payload} = {};
  163. if ( $args->{type} ) {
  164. $data->{payload}->{pageLoad} = $args->{ms}
  165. if $data->{url} =~ m/timeouts$/ && $args->{type} eq 'page load';
  166. $data->{payload}->{script} = $args->{ms}
  167. if $data->{url} =~ m/timeouts$/ && $args->{type} eq 'script';
  168. $data->{payload}->{implicit} = $args->{ms}
  169. if $data->{url} =~ m/timeouts$/ && $args->{type} eq 'implicit';
  170. }
  171. #finder polyfills
  172. #orig: class, class_name, css, id, link, link_text, partial_link_text, tag_name, name, xpath
  173. #new: "css selector", "link text", "partial link text", "tag name", "xpath"
  174. #map: class, class_name, id, name, link = 'css selector'
  175. if ( $args->{using} && $args->{value} ) {
  176. $data->{payload}->{using} = 'css selector'
  177. if grep { $args->{using} eq $_ } ( 'id', 'class name', 'name' );
  178. $data->{payload}->{value} = "[id='$args->{value}']"
  179. if $args->{using} eq 'id';
  180. $data->{payload}->{value} = ".$args->{value}"
  181. if $args->{using} eq 'class name';
  182. $data->{payload}->{value} = "[name='$args->{value}']"
  183. if $args->{using} eq 'name';
  184. }
  185. if ( $data->{url} =~ s/timeouts\/async_script$/timeouts/g ) {
  186. $data->{payload}->{script} = $args->{ms};
  187. $data->{payload}->{type} = 'script'; #XXX chrome doesn't follow the spec
  188. }
  189. if ( $data->{url} =~ s/timeouts\/implicit_wait$/timeouts/g ) {
  190. $data->{payload}->{implicit} = $args->{ms};
  191. $data->{payload}->{type} =
  192. 'implicit'; #XXX chrome doesn't follow the spec
  193. }
  194. $data->{payload}->{value} = $args->{text}
  195. if $args->{text} && $args->{command} ne 'sendKeysToElement';
  196. $data->{payload}->{handle} = $args->{window_handle}
  197. if grep { $args->{command} eq $_ }
  198. qw{fullscreenWindow minimizeWindow maximizeWindow};
  199. return $data;
  200. }
  201. sub parse_response {
  202. my ( $self, undef, $resp ) = @_;
  203. if ( ref($resp) eq 'HASH' ) {
  204. if ( $resp->{cmd_status} && $resp->{cmd_status} eq 'OK' ) {
  205. return $resp->{cmd_return};
  206. }
  207. my $msg = "Error while executing command";
  208. if ( ref $resp->{cmd_return} eq 'HASH' ) {
  209. $msg .= ": $resp->{cmd_return}{error}"
  210. if $resp->{cmd_return}{error};
  211. $msg .= ": $resp->{cmd_return}{message}"
  212. if $resp->{cmd_return}{message};
  213. }
  214. else {
  215. $msg .= ": $resp->{cmd_return}";
  216. }
  217. croak $msg;
  218. }
  219. return $resp;
  220. }
  221. #Utility
  222. sub get_spec_differences {
  223. my $v2_spec = Selenium::Remote::Commands->new()->get_cmds();
  224. my $v3_spec = Selenium::Remote::Spec->new()->get_cmds();
  225. foreach my $key ( keys(%$v2_spec) ) {
  226. print "v2 $key NOT present in v3 spec!!!\n"
  227. unless any { $_ eq $key } keys(%$v3_spec);
  228. }
  229. foreach my $key ( keys(%$v3_spec) ) {
  230. print "v3 $key NOT present in v2 spec!!!\n"
  231. unless any { $_ eq $key } keys(%$v2_spec);
  232. }
  233. }
  234. 1;
  235. __END__