selenium-webdriver 2.53.4 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +363 -10
  3. data/LICENSE +1 -1
  4. data/README.md +2 -3
  5. data/lib/selenium-webdriver.rb +0 -2
  6. data/lib/selenium/server.rb +69 -70
  7. data/lib/selenium/webdriver.rb +32 -23
  8. data/lib/selenium/webdriver/atoms.rb +18 -0
  9. data/lib/selenium/webdriver/atoms/getAttribute.js +8 -0
  10. data/lib/selenium/webdriver/chrome.rb +8 -6
  11. data/lib/selenium/webdriver/chrome/driver.rb +112 -0
  12. data/lib/selenium/webdriver/chrome/options.rb +168 -0
  13. data/lib/selenium/webdriver/chrome/profile.rb +17 -17
  14. data/lib/selenium/webdriver/chrome/service.rb +22 -89
  15. data/lib/selenium/webdriver/common.rb +13 -6
  16. data/lib/selenium/webdriver/common/action_builder.rb +49 -57
  17. data/lib/selenium/webdriver/common/alert.rb +5 -15
  18. data/lib/selenium/webdriver/common/bridge_helper.rb +10 -17
  19. data/lib/selenium/webdriver/common/driver.rb +53 -68
  20. data/lib/selenium/webdriver/common/driver_extensions/{has_input_devices.rb → has_addons.rb} +13 -23
  21. data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +4 -8
  22. data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +4 -7
  23. data/lib/selenium/webdriver/common/driver_extensions/has_remote_status.rb +0 -4
  24. data/lib/selenium/webdriver/common/driver_extensions/has_session_id.rb +0 -4
  25. data/lib/selenium/webdriver/common/driver_extensions/has_touch_screen.rb +1 -5
  26. data/lib/selenium/webdriver/common/driver_extensions/has_web_storage.rb +0 -5
  27. data/lib/selenium/webdriver/common/driver_extensions/rotatable.rb +4 -9
  28. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +7 -7
  29. data/lib/selenium/webdriver/common/driver_extensions/uploads_files.rb +2 -7
  30. data/lib/selenium/webdriver/common/element.rb +57 -39
  31. data/lib/selenium/webdriver/common/error.rb +204 -106
  32. data/lib/selenium/webdriver/common/file_reaper.rb +3 -11
  33. data/lib/selenium/webdriver/common/html5/local_storage.rb +6 -10
  34. data/lib/selenium/webdriver/common/html5/session_storage.rb +6 -10
  35. data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +7 -18
  36. data/lib/selenium/webdriver/{safari/options.rb → common/interactions/input_device.rb} +20 -31
  37. data/lib/selenium/webdriver/common/interactions/interaction.rb +50 -0
  38. data/lib/selenium/webdriver/{safari/browser.rb → common/interactions/interactions.rb} +16 -15
  39. data/lib/selenium/webdriver/common/interactions/key_actions.rb +143 -0
  40. data/lib/selenium/webdriver/common/interactions/key_input.rb +62 -0
  41. data/lib/selenium/webdriver/{android.rb → common/interactions/none_input.rb} +11 -6
  42. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +353 -0
  43. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +132 -0
  44. data/lib/selenium/webdriver/common/keyboard.rb +7 -14
  45. data/lib/selenium/webdriver/common/keys.rb +99 -82
  46. data/lib/selenium/webdriver/common/log_entry.rb +3 -6
  47. data/lib/selenium/webdriver/common/logger.rb +140 -0
  48. data/lib/selenium/webdriver/common/logs.rb +2 -6
  49. data/lib/selenium/webdriver/common/mouse.rb +9 -14
  50. data/lib/selenium/webdriver/common/navigation.rb +2 -6
  51. data/lib/selenium/webdriver/common/options.rb +20 -23
  52. data/lib/selenium/webdriver/common/platform.rb +70 -97
  53. data/lib/selenium/webdriver/common/port_prober.rb +3 -4
  54. data/lib/selenium/webdriver/common/profile_helper.rb +6 -11
  55. data/lib/selenium/webdriver/common/proxy.rb +58 -72
  56. data/lib/selenium/webdriver/common/search_context.rb +22 -29
  57. data/lib/selenium/webdriver/common/service.rb +161 -0
  58. data/lib/selenium/webdriver/common/socket_lock.rb +6 -14
  59. data/lib/selenium/webdriver/common/socket_poller.rb +5 -12
  60. data/lib/selenium/webdriver/common/target_locator.rb +11 -15
  61. data/lib/selenium/webdriver/common/timeouts.rb +4 -8
  62. data/lib/selenium/webdriver/common/touch_action_builder.rb +2 -6
  63. data/lib/selenium/webdriver/common/touch_screen.rb +19 -23
  64. data/lib/selenium/webdriver/common/w3c_action_builder.rb +209 -0
  65. data/lib/selenium/webdriver/{phantomjs.rb → common/w3c_options.rb} +16 -14
  66. data/lib/selenium/webdriver/common/wait.rb +6 -13
  67. data/lib/selenium/webdriver/common/window.rb +48 -17
  68. data/lib/selenium/webdriver/common/zipper.rb +6 -10
  69. data/lib/selenium/webdriver/edge.rb +5 -12
  70. data/lib/selenium/webdriver/edge/bridge.rb +32 -63
  71. data/lib/selenium/webdriver/edge/driver.rb +73 -0
  72. data/lib/selenium/webdriver/edge/service.rb +18 -87
  73. data/lib/selenium/webdriver/firefox.rb +20 -11
  74. data/lib/selenium/webdriver/firefox/binary.rb +40 -56
  75. data/lib/selenium/webdriver/firefox/driver.rb +48 -0
  76. data/lib/selenium/webdriver/firefox/extension.rb +18 -8
  77. data/lib/selenium/webdriver/firefox/extension/prefs.json +3 -11
  78. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  79. data/lib/selenium/webdriver/firefox/launcher.rb +13 -22
  80. data/lib/selenium/webdriver/firefox/legacy/driver.rb +79 -0
  81. data/lib/selenium/webdriver/{iphone.rb → firefox/marionette/bridge.rb} +25 -6
  82. data/lib/selenium/webdriver/firefox/marionette/driver.rb +96 -0
  83. data/lib/selenium/webdriver/firefox/options.rb +149 -0
  84. data/lib/selenium/webdriver/firefox/profile.rb +46 -46
  85. data/lib/selenium/webdriver/firefox/profiles_ini.rb +8 -18
  86. data/lib/selenium/webdriver/firefox/service.rb +23 -83
  87. data/lib/selenium/webdriver/firefox/util.rb +0 -4
  88. data/lib/selenium/webdriver/ie.rb +4 -8
  89. data/lib/selenium/webdriver/ie/driver.rb +90 -0
  90. data/lib/selenium/webdriver/ie/options.rb +136 -0
  91. data/lib/selenium/webdriver/ie/service.rb +58 -0
  92. data/lib/selenium/webdriver/remote.rb +8 -16
  93. data/lib/selenium/webdriver/remote/bridge.rb +96 -565
  94. data/lib/selenium/webdriver/remote/capabilities.rb +76 -94
  95. data/lib/selenium/webdriver/remote/driver.rb +49 -0
  96. data/lib/selenium/webdriver/remote/http/common.rb +22 -20
  97. data/lib/selenium/webdriver/remote/http/curb.rb +9 -12
  98. data/lib/selenium/webdriver/remote/http/default.rb +54 -41
  99. data/lib/selenium/webdriver/remote/http/persistent.rb +9 -8
  100. data/lib/selenium/webdriver/remote/oss/bridge.rb +586 -0
  101. data/lib/selenium/webdriver/remote/oss/commands.rb +221 -0
  102. data/lib/selenium/webdriver/remote/response.rb +39 -27
  103. data/lib/selenium/webdriver/remote/server_error.rb +1 -5
  104. data/lib/selenium/webdriver/remote/w3c/bridge.rb +573 -0
  105. data/lib/selenium/webdriver/remote/w3c/capabilities.rb +290 -0
  106. data/lib/selenium/webdriver/remote/w3c/commands.rb +148 -0
  107. data/lib/selenium/webdriver/safari.rb +20 -29
  108. data/lib/selenium/webdriver/{firefox/w3c_bridge.rb → safari/driver.rb} +21 -30
  109. data/lib/selenium/webdriver/safari/service.rb +57 -0
  110. data/lib/selenium/webdriver/support.rb +1 -2
  111. data/lib/selenium/webdriver/support/abstract_event_listener.rb +17 -4
  112. data/lib/selenium/webdriver/support/block_event_listener.rb +1 -5
  113. data/lib/selenium/webdriver/support/color.rb +57 -42
  114. data/lib/selenium/webdriver/support/escaper.rb +41 -0
  115. data/lib/selenium/webdriver/support/event_firing_bridge.rb +36 -40
  116. data/lib/selenium/webdriver/support/select.rb +33 -86
  117. data/selenium-webdriver.gemspec +22 -25
  118. metadata +254 -261
  119. data/lib/selenium-client.rb +0 -21
  120. data/lib/selenium/client.rb +0 -57
  121. data/lib/selenium/client/base.rb +0 -151
  122. data/lib/selenium/client/driver.rb +0 -29
  123. data/lib/selenium/client/errors.rb +0 -28
  124. data/lib/selenium/client/extensions.rb +0 -132
  125. data/lib/selenium/client/idiomatic.rb +0 -507
  126. data/lib/selenium/client/javascript_expression_builder.rb +0 -135
  127. data/lib/selenium/client/javascript_frameworks/jquery.rb +0 -32
  128. data/lib/selenium/client/javascript_frameworks/prototype.rb +0 -32
  129. data/lib/selenium/client/legacy_driver.rb +0 -1722
  130. data/lib/selenium/client/protocol.rb +0 -123
  131. data/lib/selenium/client/selenium_helper.rb +0 -49
  132. data/lib/selenium/rake/server_task.rb +0 -176
  133. data/lib/selenium/webdriver/android/bridge.rb +0 -68
  134. data/lib/selenium/webdriver/chrome/bridge.rb +0 -139
  135. data/lib/selenium/webdriver/common/core_ext/base64.rb +0 -28
  136. data/lib/selenium/webdriver/common/core_ext/dir.rb +0 -61
  137. data/lib/selenium/webdriver/common/html5/location.rb +0 -19
  138. data/lib/selenium/webdriver/common/w3c_error.rb +0 -194
  139. data/lib/selenium/webdriver/edge/legacy_support.rb +0 -117
  140. data/lib/selenium/webdriver/firefox/bridge.rb +0 -89
  141. data/lib/selenium/webdriver/ie/bridge.rb +0 -88
  142. data/lib/selenium/webdriver/ie/server.rb +0 -133
  143. data/lib/selenium/webdriver/iphone/bridge.rb +0 -64
  144. data/lib/selenium/webdriver/phantomjs/bridge.rb +0 -78
  145. data/lib/selenium/webdriver/phantomjs/service.rb +0 -130
  146. data/lib/selenium/webdriver/remote/commands.rb +0 -211
  147. data/lib/selenium/webdriver/remote/w3c_bridge.rb +0 -668
  148. data/lib/selenium/webdriver/remote/w3c_capabilities.rb +0 -236
  149. data/lib/selenium/webdriver/remote/w3c_commands.rb +0 -132
  150. data/lib/selenium/webdriver/safari/bridge.rb +0 -135
  151. data/lib/selenium/webdriver/safari/resources/client.js +0 -7255
  152. data/lib/selenium/webdriver/safari/server.rb +0 -187
@@ -0,0 +1,221 @@
1
+ # Licensed to the Software Freedom Conservancy (SFC) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The SFC licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ module Selenium
19
+ module WebDriver
20
+ module Remote
21
+ module OSS
22
+
23
+ #
24
+ # https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#command-reference
25
+ # @api private
26
+ #
27
+
28
+ class Bridge
29
+
30
+ COMMANDS = {
31
+ get_capabilities: [:get, 'session/:session_id'.freeze],
32
+ status: [:get, 'status'.freeze],
33
+
34
+ #
35
+ # basic driver
36
+ #
37
+
38
+ get_current_url: [:get, 'session/:session_id/url'.freeze],
39
+ get: [:post, 'session/:session_id/url'.freeze],
40
+ go_forward: [:post, 'session/:session_id/forward'.freeze],
41
+ go_back: [:post, 'session/:session_id/back'.freeze],
42
+ refresh: [:post, 'session/:session_id/refresh'.freeze],
43
+ quit: [:delete, 'session/:session_id'.freeze],
44
+ close: [:delete, 'session/:session_id/window'.freeze],
45
+ get_page_source: [:get, 'session/:session_id/source'.freeze],
46
+ get_title: [:get, 'session/:session_id/title'.freeze],
47
+ find_element: [:post, 'session/:session_id/element'.freeze],
48
+ find_elements: [:post, 'session/:session_id/elements'.freeze],
49
+ get_active_element: [:post, 'session/:session_id/element/active'.freeze],
50
+
51
+ #
52
+ # window handling
53
+ #
54
+
55
+ get_current_window_handle: [:get, 'session/:session_id/window_handle'.freeze],
56
+ get_window_handles: [:get, 'session/:session_id/window_handles'.freeze],
57
+ set_window_size: [:post, 'session/:session_id/window/:window_handle/size'.freeze],
58
+ set_window_position: [:post, 'session/:session_id/window/:window_handle/position'.freeze],
59
+ get_window_size: [:get, 'session/:session_id/window/:window_handle/size'.freeze],
60
+ get_window_position: [:get, 'session/:session_id/window/:window_handle/position'.freeze],
61
+ maximize_window: [:post, 'session/:session_id/window/:window_handle/maximize'.freeze],
62
+
63
+ #
64
+ # script execution
65
+ #
66
+
67
+ execute_script: [:post, 'session/:session_id/execute'.freeze],
68
+ execute_async_script: [:post, 'session/:session_id/execute_async'.freeze],
69
+
70
+ #
71
+ # screenshot
72
+ #
73
+
74
+ screenshot: [:get, 'session/:session_id/screenshot'.freeze],
75
+
76
+ #
77
+ # alerts
78
+ #
79
+
80
+ dismiss_alert: [:post, 'session/:session_id/dismiss_alert'.freeze],
81
+ accept_alert: [:post, 'session/:session_id/accept_alert'.freeze],
82
+ get_alert_text: [:get, 'session/:session_id/alert_text'.freeze],
83
+ set_alert_value: [:post, 'session/:session_id/alert_text'.freeze],
84
+ set_authentication: [:post, 'session/:session_id/alert/credentials'.freeze],
85
+
86
+ #
87
+ # target locator
88
+ #
89
+
90
+ switch_to_frame: [:post, 'session/:session_id/frame'.freeze],
91
+ switch_to_parent_frame: [:post, 'session/:session_id/frame/parent'.freeze],
92
+ switch_to_window: [:post, 'session/:session_id/window'.freeze],
93
+
94
+ #
95
+ # options
96
+ #
97
+
98
+ get_cookies: [:get, 'session/:session_id/cookie'.freeze],
99
+ add_cookie: [:post, 'session/:session_id/cookie'.freeze],
100
+ delete_all_cookies: [:delete, 'session/:session_id/cookie'.freeze],
101
+ delete_cookie: [:delete, 'session/:session_id/cookie/:name'.freeze],
102
+
103
+ #
104
+ # timeouts
105
+ #
106
+
107
+ implicitly_wait: [:post, 'session/:session_id/timeouts/implicit_wait'.freeze],
108
+ set_script_timeout: [:post, 'session/:session_id/timeouts/async_script'.freeze],
109
+ set_timeout: [:post, 'session/:session_id/timeouts'.freeze],
110
+
111
+ #
112
+ # element
113
+ #
114
+
115
+ describe_element: [:get, 'session/:session_id/element/:id'.freeze],
116
+ find_child_element: [:post, 'session/:session_id/element/:id/element'.freeze],
117
+ find_child_elements: [:post, 'session/:session_id/element/:id/elements'.freeze],
118
+ click_element: [:post, 'session/:session_id/element/:id/click'.freeze],
119
+ submit_element: [:post, 'session/:session_id/element/:id/submit'.freeze],
120
+ get_element_value: [:get, 'session/:session_id/element/:id/value'.freeze],
121
+ send_keys_to_element: [:post, 'session/:session_id/element/:id/value'.freeze],
122
+ upload_file: [:post, 'session/:session_id/file'.freeze],
123
+ get_element_tag_name: [:get, 'session/:session_id/element/:id/name'.freeze],
124
+ clear_element: [:post, 'session/:session_id/element/:id/clear'.freeze],
125
+ is_element_selected: [:get, 'session/:session_id/element/:id/selected'.freeze],
126
+ is_element_enabled: [:get, 'session/:session_id/element/:id/enabled'.freeze],
127
+ get_element_attribute: [:get, 'session/:session_id/element/:id/attribute/:name'.freeze],
128
+ element_equals: [:get, 'session/:session_id/element/:id/equals/:other'.freeze],
129
+ is_element_displayed: [:get, 'session/:session_id/element/:id/displayed'.freeze],
130
+ get_element_location: [:get, 'session/:session_id/element/:id/location'.freeze],
131
+ get_element_location_once_scrolled_into_view: [:get, 'session/:session_id/element/:id/location_in_view'.freeze],
132
+ get_element_size: [:get, 'session/:session_id/element/:id/size'.freeze],
133
+ drag_element: [:post, 'session/:session_id/element/:id/drag'.freeze],
134
+ get_element_value_of_css_property: [:get, 'session/:session_id/element/:id/css/:property_name'.freeze],
135
+ get_element_text: [:get, 'session/:session_id/element/:id/text'.freeze],
136
+
137
+ #
138
+ # rotatable
139
+ #
140
+
141
+ get_screen_orientation: [:get, 'session/:session_id/orientation'.freeze],
142
+ set_screen_orientation: [:post, 'session/:session_id/orientation'.freeze],
143
+
144
+ #
145
+ # interactions API
146
+ #
147
+
148
+ click: [:post, 'session/:session_id/click'.freeze],
149
+ double_click: [:post, 'session/:session_id/doubleclick'.freeze],
150
+ mouse_down: [:post, 'session/:session_id/buttondown'.freeze],
151
+ mouse_up: [:post, 'session/:session_id/buttonup'.freeze],
152
+ mouse_move_to: [:post, 'session/:session_id/moveto'.freeze],
153
+ send_modifier_key_to_active_element: [:post, 'session/:session_id/modifier'.freeze],
154
+ send_keys_to_active_element: [:post, 'session/:session_id/keys'.freeze],
155
+
156
+ #
157
+ # html 5
158
+ #
159
+
160
+ execute_sql: [:post, 'session/:session_id/execute_sql'.freeze],
161
+
162
+ get_location: [:get, 'session/:session_id/location'.freeze],
163
+ set_location: [:post, 'session/:session_id/location'.freeze],
164
+
165
+ get_app_cache: [:get, 'session/:session_id/application_cache'.freeze],
166
+ get_app_cache_status: [:get, 'session/:session_id/application_cache/status'.freeze],
167
+ clear_app_cache: [:delete, 'session/:session_id/application_cache/clear'.freeze],
168
+
169
+ get_network_connection: [:get, 'session/:session_id/network_connection'.freeze],
170
+ set_network_connection: [:post, 'session/:session_id/network_connection'.freeze],
171
+
172
+ get_local_storage_item: [:get, 'session/:session_id/local_storage/key/:key'.freeze],
173
+ remove_local_storage_item: [:delete, 'session/:session_id/local_storage/key/:key'.freeze],
174
+ get_local_storage_keys: [:get, 'session/:session_id/local_storage'.freeze],
175
+ set_local_storage_item: [:post, 'session/:session_id/local_storage'.freeze],
176
+ clear_local_storage: [:delete, 'session/:session_id/local_storage'.freeze],
177
+ get_local_storage_size: [:get, 'session/:session_id/local_storage/size'.freeze],
178
+
179
+ get_session_storage_item: [:get, 'session/:session_id/session_storage/key/:key'.freeze],
180
+ remove_session_storage_item: [:delete, 'session/:session_id/session_storage/key/:key'.freeze],
181
+ get_session_storage_keys: [:get, 'session/:session_id/session_storage'.freeze],
182
+ set_session_storage_item: [:post, 'session/:session_id/session_storage'.freeze],
183
+ clear_session_storage: [:delete, 'session/:session_id/session_storage'.freeze],
184
+ get_session_storage_size: [:get, 'session/:session_id/session_storage/size'.freeze],
185
+
186
+ #
187
+ # ime
188
+ #
189
+
190
+ ime_get_available_engines: [:get, 'session/:session_id/ime/available_engines'.freeze],
191
+ ime_get_active_engine: [:get, 'session/:session_id/ime/active_engine'.freeze],
192
+ ime_is_activated: [:get, 'session/:session_id/ime/activated'.freeze],
193
+ ime_deactivate: [:post, 'session/:session_id/ime/deactivate'.freeze],
194
+ ime_activate_engine: [:post, 'session/:session_id/ime/activate'.freeze],
195
+
196
+ #
197
+ # touch
198
+ #
199
+
200
+ touch_single_tap: [:post, 'session/:session_id/touch/click'.freeze],
201
+ touch_double_tap: [:post, 'session/:session_id/touch/doubleclick'.freeze],
202
+ touch_long_press: [:post, 'session/:session_id/touch/longclick'.freeze],
203
+ touch_down: [:post, 'session/:session_id/touch/down'.freeze],
204
+ touch_up: [:post, 'session/:session_id/touch/up'.freeze],
205
+ touch_move: [:post, 'session/:session_id/touch/move'.freeze],
206
+ touch_scroll: [:post, 'session/:session_id/touch/scroll'.freeze],
207
+ touch_flick: [:post, 'session/:session_id/touch/flick'.freeze],
208
+
209
+ #
210
+ # logs
211
+ #
212
+
213
+ get_available_log_types: [:get, 'session/:session_id/log/types'.freeze],
214
+ get_log: [:post, 'session/:session_id/log'.freeze]
215
+ }.freeze
216
+
217
+ end # Bridge
218
+ end # OSS
219
+ end # Remote
220
+ end # WebDriver
221
+ end # Selenium
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
- #
3
1
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
2
  # or more contributor license agreements. See the NOTICE file
5
3
  # distributed with this work for additional information
@@ -20,9 +18,9 @@
20
18
  module Selenium
21
19
  module WebDriver
22
20
  module Remote
23
-
24
21
  # @api private
25
22
  class Response
23
+ STACKTRACE_KEY = 'stackTrace'.freeze
26
24
 
27
25
  attr_reader :code, :payload
28
26
  attr_writer :payload
@@ -49,9 +47,10 @@ module Selenium
49
47
 
50
48
  case val
51
49
  when Hash
52
- msg = val['message'] or return "unknown error"
53
- msg << ": #{val['alert']['text'].inspect}" if val['alert'].kind_of?(Hash) && val['alert']['text']
54
- msg << " (#{ val['class'] })" if val['class']
50
+ msg = val['message']
51
+ return 'unknown error' unless msg
52
+ msg << ": #{val['alert']['text'].inspect}" if val['alert'].is_a?(Hash) && val['alert']['text']
53
+ msg << " (#{val['class']})" if val['class']
55
54
  msg
56
55
  when String
57
56
  val
@@ -67,49 +66,62 @@ module Selenium
67
66
  private
68
67
 
69
68
  def assert_ok
70
- if e = error()
71
- raise e
72
- elsif @code.nil? || @code >= 400
73
- raise Error::ServerError, self
74
- end
69
+ e = error
70
+ raise e if e
71
+ return unless @code.nil? || @code >= 400
72
+ raise Error::ServerError, self
75
73
  end
76
74
 
77
75
  def add_backtrace(ex)
78
- unless value.kind_of?(Hash) && value['stackTrace']
79
- return
80
- end
76
+ return unless error_payload.is_a?(Hash)
81
77
 
82
- server_trace = value['stackTrace']
78
+ server_trace = error_payload[STACKTRACE_KEY] ||
79
+ error_payload[STACKTRACE_KEY.downcase] ||
80
+ error_payload['value'][STACKTRACE_KEY]
81
+ return unless server_trace
83
82
 
84
- backtrace = server_trace.map do |frame|
85
- next unless frame.kind_of?(Hash)
83
+ backtrace = case server_trace
84
+ when Array
85
+ backtrace_from_remote(server_trace)
86
+ when String
87
+ server_trace.split("\n")
88
+ end
89
+
90
+ ex.set_backtrace(backtrace + ex.backtrace)
91
+ end
92
+
93
+ def backtrace_from_remote(server_trace)
94
+ server_trace.map do |frame|
95
+ next unless frame.is_a?(Hash)
86
96
 
87
97
  file = frame['fileName']
88
98
  line = frame['lineNumber']
89
99
  meth = frame['methodName']
90
100
 
91
- if class_name = frame['className']
92
- file = "#{class_name}(#{file})"
93
- end
101
+ class_name = frame['className']
102
+ file = "#{class_name}(#{file})" if class_name
94
103
 
95
- if meth.nil? || meth.empty?
96
- meth = 'unknown'
97
- end
104
+ meth = 'unknown' if meth.nil? || meth.empty?
98
105
 
99
106
  "[remote server] #{file}:#{line}:in `#{meth}'"
100
107
  end.compact
108
+ end
101
109
 
102
- ex.set_backtrace(backtrace + ex.backtrace)
110
+ def error_payload
111
+ # Even errors are wrapped in 'value' for w3c
112
+ # Grab 'value' key for error, leave original payload alone and let the bridge process
113
+ @error_payload ||= !@payload.key?('sessionId') ? @payload['value'] : @payload
103
114
  end
104
115
 
105
116
  def status
106
- @payload['status'] || @payload['error']
117
+ return unless error_payload.is_a? Hash
118
+ @status ||= error_payload['status'] || error_payload['error']
107
119
  end
108
120
 
109
121
  def value
110
- @payload['value'] || @payload['message']
122
+ return unless error_payload.is_a? Hash
123
+ @value ||= error_payload['value'] || error_payload['message']
111
124
  end
112
-
113
125
  end # Response
114
126
  end # Remote
115
127
  end # WebDriver
@@ -1,5 +1,3 @@
1
- # encoding: utf-8
2
- #
3
1
  # Licensed to the Software Freedom Conservancy (SFC) under one
4
2
  # or more contributor license agreements. See the NOTICE file
5
3
  # distributed with this work for additional information
@@ -21,15 +19,13 @@ module Selenium
21
19
  module WebDriver
22
20
  module Error
23
21
  class ServerError < StandardError
24
-
25
22
  def initialize(response)
26
- if response.kind_of? String
23
+ if response.is_a? String
27
24
  super(response)
28
25
  else
29
26
  super("status code #{response.code}")
30
27
  end
31
28
  end
32
-
33
29
  end # ServerError
34
30
  end # Error
35
31
  end # WebDriver
@@ -0,0 +1,573 @@
1
+ # Licensed to the Software Freedom Conservancy (SFC) under one
2
+ # or more contributor license agreements. See the NOTICE file
3
+ # distributed with this work for additional information
4
+ # regarding copyright ownership. The SFC licenses this file
5
+ # to you under the Apache License, Version 2.0 (the
6
+ # "License"); you may not use this file except in compliance
7
+ # with the License. You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ require 'json'
19
+
20
+ module Selenium
21
+ module WebDriver
22
+ module Remote
23
+ module W3C
24
+
25
+ #
26
+ # Low level bridge to the remote server implementing JSON wire
27
+ # protocol (W3C dialect), through which the rest of the API works.
28
+ # @api private
29
+ #
30
+
31
+ class Bridge < Remote::Bridge
32
+
33
+ def initialize(capabilities, session_id, **opts)
34
+ @capabilities = capabilities
35
+ @session_id = session_id
36
+ super(opts)
37
+ end
38
+
39
+ def dialect
40
+ :w3c
41
+ end
42
+
43
+ def commands(command)
44
+ case command
45
+ when :status, :is_element_displayed
46
+ Remote::OSS::Bridge::COMMANDS[command]
47
+ else
48
+ COMMANDS[command]
49
+ end
50
+ end
51
+
52
+ def status
53
+ execute :status
54
+ end
55
+
56
+ def get(url)
57
+ execute :get, {}, {url: url}
58
+ end
59
+
60
+ def implicit_wait_timeout=(milliseconds)
61
+ timeout('implicit', milliseconds)
62
+ end
63
+
64
+ def script_timeout=(milliseconds)
65
+ timeout('script', milliseconds)
66
+ end
67
+
68
+ def timeout(type, milliseconds)
69
+ type = 'pageLoad' if type == 'page load'
70
+ execute :set_timeout, {}, {type => milliseconds}
71
+ end
72
+
73
+ #
74
+ # alerts
75
+ #
76
+
77
+ def accept_alert
78
+ execute :accept_alert
79
+ end
80
+
81
+ def dismiss_alert
82
+ execute :dismiss_alert
83
+ end
84
+
85
+ def alert=(keys)
86
+ execute :send_alert_text, {}, {value: keys.split(//), text: keys}
87
+ end
88
+
89
+ def alert_text
90
+ execute :get_alert_text
91
+ end
92
+
93
+ #
94
+ # navigation
95
+ #
96
+
97
+ def go_back
98
+ execute :back
99
+ end
100
+
101
+ def go_forward
102
+ execute :forward
103
+ end
104
+
105
+ def url
106
+ execute :get_current_url
107
+ end
108
+
109
+ def title
110
+ execute :get_title
111
+ end
112
+
113
+ def page_source
114
+ execute_script('var source = document.documentElement.outerHTML;' \
115
+ 'if (!source) { source = new XMLSerializer().serializeToString(document); }' \
116
+ 'return source;')
117
+ end
118
+
119
+ def switch_to_window(name)
120
+ execute :switch_to_window, {}, {handle: name}
121
+ end
122
+
123
+ def switch_to_frame(id)
124
+ id = find_element_by('id', id) if id.is_a? String
125
+ execute :switch_to_frame, {}, {id: id}
126
+ end
127
+
128
+ def switch_to_parent_frame
129
+ execute :switch_to_parent_frame
130
+ end
131
+
132
+ def switch_to_default_content
133
+ switch_to_frame nil
134
+ end
135
+
136
+ QUIT_ERRORS = [IOError].freeze
137
+
138
+ def quit
139
+ execute :delete_session
140
+ http.close
141
+ rescue *QUIT_ERRORS
142
+ end
143
+
144
+ def close
145
+ execute :close_window
146
+ end
147
+
148
+ def refresh
149
+ execute :refresh
150
+ end
151
+
152
+ #
153
+ # window handling
154
+ #
155
+
156
+ def window_handles
157
+ execute :get_window_handles
158
+ end
159
+
160
+ def window_handle
161
+ execute :get_window_handle
162
+ end
163
+
164
+ def resize_window(width, height, handle = :current)
165
+ unless handle == :current
166
+ raise Error::WebDriverError, 'Switch to desired window before changing its size'
167
+ end
168
+ set_window_rect(width: width, height: height)
169
+ end
170
+
171
+ def window_size(handle = :current)
172
+ unless handle == :current
173
+ raise Error::UnsupportedOperationError, 'Switch to desired window before getting its size'
174
+ end
175
+ data = execute :get_window_rect
176
+
177
+ Dimension.new data['width'], data['height']
178
+ end
179
+
180
+ def minimize_window
181
+ execute :minimize_window
182
+ end
183
+
184
+ def maximize_window(handle = :current)
185
+ unless handle == :current
186
+ raise Error::UnsupportedOperationError, 'Switch to desired window before changing its size'
187
+ end
188
+ execute :maximize_window
189
+ end
190
+
191
+ def full_screen_window
192
+ execute :fullscreen_window
193
+ end
194
+
195
+ def reposition_window(x, y)
196
+ set_window_rect(x: x, y: y)
197
+ end
198
+
199
+ def window_position
200
+ data = execute :get_window_rect
201
+ Point.new data['x'], data['y']
202
+ end
203
+
204
+ def set_window_rect(x: nil, y: nil, width: nil, height: nil)
205
+ params = {x: x, y: y, width: width, height: height}
206
+ params.update(params) { |k, v| Integer(v) unless v.nil? }
207
+ execute :set_window_rect, {}, params
208
+ end
209
+
210
+ def window_rect
211
+ data = execute :get_window_rect
212
+ Rectangle.new data['x'], data['y'], data['width'], data['height']
213
+ end
214
+
215
+ def screenshot
216
+ execute :take_screenshot
217
+ end
218
+
219
+ #
220
+ # HTML 5
221
+ #
222
+
223
+ def local_storage_item(key, value = nil)
224
+ if value
225
+ execute_script("localStorage.setItem('#{key}', '#{value}')")
226
+ else
227
+ execute_script("return localStorage.getItem('#{key}')")
228
+ end
229
+ end
230
+
231
+ def remove_local_storage_item(key)
232
+ execute_script("localStorage.removeItem('#{key}')")
233
+ end
234
+
235
+ def local_storage_keys
236
+ execute_script('return Object.keys(localStorage)')
237
+ end
238
+
239
+ def clear_local_storage
240
+ execute_script('localStorage.clear()')
241
+ end
242
+
243
+ def local_storage_size
244
+ execute_script('return localStorage.length')
245
+ end
246
+
247
+ def session_storage_item(key, value = nil)
248
+ if value
249
+ execute_script("sessionStorage.setItem('#{key}', '#{value}')")
250
+ else
251
+ execute_script("return sessionStorage.getItem('#{key}')")
252
+ end
253
+ end
254
+
255
+ def remove_session_storage_item(key)
256
+ execute_script("sessionStorage.removeItem('#{key}')")
257
+ end
258
+
259
+ def session_storage_keys
260
+ execute_script('return Object.keys(sessionStorage)')
261
+ end
262
+
263
+ def clear_session_storage
264
+ execute_script('sessionStorage.clear()')
265
+ end
266
+
267
+ def session_storage_size
268
+ execute_script('return sessionStorage.length')
269
+ end
270
+
271
+ def location
272
+ raise Error::UnsupportedOperationError, 'The W3C standard does not currently support getting location'
273
+ end
274
+
275
+ def set_location(_lat, _lon, _alt)
276
+ raise Error::UnsupportedOperationError, 'The W3C standard does not currently support setting location'
277
+ end
278
+
279
+ def network_connection
280
+ raise Error::UnsupportedOperationError, 'The W3C standard does not currently support getting network connection'
281
+ end
282
+
283
+ def network_connection=(_type)
284
+ raise Error::UnsupportedOperationError, 'The W3C standard does not currently support setting network connection'
285
+ end
286
+
287
+ #
288
+ # javascript execution
289
+ #
290
+
291
+ def execute_script(script, *args)
292
+ result = execute :execute_script, {}, {script: script, args: args}
293
+ unwrap_script_result result
294
+ end
295
+
296
+ def execute_async_script(script, *args)
297
+ result = execute :execute_async_script, {}, {script: script, args: args}
298
+ unwrap_script_result result
299
+ end
300
+
301
+ #
302
+ # cookies
303
+ #
304
+
305
+ def options
306
+ @options ||= WebDriver::W3COptions.new(self)
307
+ end
308
+
309
+ def add_cookie(cookie)
310
+ execute :add_cookie, {}, {cookie: cookie}
311
+ end
312
+
313
+ def delete_cookie(name)
314
+ execute :delete_cookie, name: name
315
+ end
316
+
317
+ def cookie(name)
318
+ execute :get_cookie, name: name
319
+ end
320
+
321
+ def cookies
322
+ execute :get_all_cookies
323
+ end
324
+
325
+ def delete_all_cookies
326
+ execute :delete_all_cookies
327
+ end
328
+
329
+ #
330
+ # actions
331
+ #
332
+
333
+ def action(async = false)
334
+ W3CActionBuilder.new self,
335
+ Interactions.pointer(:mouse, name: 'mouse'),
336
+ Interactions.key('keyboard'),
337
+ async
338
+ end
339
+ alias_method :actions, :action
340
+
341
+ def mouse
342
+ raise Error::UnsupportedOperationError, '#mouse is no longer supported, use #action instead'
343
+ end
344
+
345
+ def keyboard
346
+ raise Error::UnsupportedOperationError, '#keyboard is no longer supported, use #action instead'
347
+ end
348
+
349
+ def send_actions(data)
350
+ execute :actions, {}, {actions: data}
351
+ end
352
+
353
+ def release_actions
354
+ execute :release_actions
355
+ end
356
+
357
+ def click_element(element)
358
+ execute :element_click, id: element
359
+ end
360
+
361
+ # TODO: - Implement file verification
362
+ def send_keys_to_element(element, keys)
363
+ # Keep .split(//) for backward compatibility for now
364
+ text = keys.join('')
365
+ execute :element_send_keys, {id: element}, {value: text.split(//), text: text}
366
+ end
367
+
368
+ def clear_element(element)
369
+ execute :element_clear, id: element
370
+ end
371
+
372
+ def submit_element(element)
373
+ form = find_element_by('xpath', "./ancestor-or-self::form", element)
374
+ execute_script("var e = arguments[0].ownerDocument.createEvent('Event');" \
375
+ "e.initEvent('submit', true, true);" \
376
+ 'if (arguments[0].dispatchEvent(e)) { arguments[0].submit() }', form.as_json)
377
+ end
378
+
379
+ def drag_element(element, right_by, down_by)
380
+ execute :drag_element, {id: element}, {x: right_by, y: down_by}
381
+ end
382
+
383
+ def touch_single_tap(element)
384
+ execute :touch_single_tap, {}, {element: element}
385
+ end
386
+
387
+ def touch_double_tap(element)
388
+ execute :touch_double_tap, {}, {element: element}
389
+ end
390
+
391
+ def touch_long_press(element)
392
+ execute :touch_long_press, {}, {element: element}
393
+ end
394
+
395
+ def touch_down(x, y)
396
+ execute :touch_down, {}, {x: x, y: y}
397
+ end
398
+
399
+ def touch_up(x, y)
400
+ execute :touch_up, {}, {x: x, y: y}
401
+ end
402
+
403
+ def touch_move(x, y)
404
+ execute :touch_move, {}, {x: x, y: y}
405
+ end
406
+
407
+ def touch_scroll(element, x, y)
408
+ if element
409
+ execute :touch_scroll, {}, {element: element,
410
+ xoffset: x,
411
+ yoffset: y}
412
+ else
413
+ execute :touch_scroll, {}, {xoffset: x, yoffset: y}
414
+ end
415
+ end
416
+
417
+ def touch_flick(xspeed, yspeed)
418
+ execute :touch_flick, {}, {xspeed: xspeed, yspeed: yspeed}
419
+ end
420
+
421
+ def touch_element_flick(element, right_by, down_by, speed)
422
+ execute :touch_flick, {}, {element: element,
423
+ xoffset: right_by,
424
+ yoffset: down_by,
425
+ speed: speed}
426
+ end
427
+
428
+ def screen_orientation=(orientation)
429
+ execute :set_screen_orientation, {}, {orientation: orientation}
430
+ end
431
+
432
+ def screen_orientation
433
+ execute :get_screen_orientation
434
+ end
435
+
436
+ #
437
+ # element properties
438
+ #
439
+
440
+ def element_tag_name(element)
441
+ execute :get_element_tag_name, id: element
442
+ end
443
+
444
+ def element_attribute(element, name)
445
+ execute_atom :getAttribute, element, name
446
+ end
447
+
448
+ def element_property(element, name)
449
+ execute :get_element_property, id: element.ref, name: name
450
+ end
451
+
452
+ def element_value(element)
453
+ element_property element, 'value'
454
+ end
455
+
456
+ def element_text(element)
457
+ execute :get_element_text, id: element
458
+ end
459
+
460
+ def element_location(element)
461
+ data = execute :get_element_rect, id: element
462
+
463
+ Point.new data['x'], data['y']
464
+ end
465
+
466
+ def element_rect(element)
467
+ data = execute :get_element_rect, id: element
468
+
469
+ Rectangle.new data['x'], data['y'], data['width'], data['height']
470
+ end
471
+
472
+ def element_location_once_scrolled_into_view(element)
473
+ send_keys_to_element(element, [''])
474
+ element_location(element)
475
+ end
476
+
477
+ def element_size(element)
478
+ data = execute :get_element_rect, id: element
479
+
480
+ Dimension.new data['width'], data['height']
481
+ end
482
+
483
+ def element_enabled?(element)
484
+ execute :is_element_enabled, id: element
485
+ end
486
+
487
+ def element_selected?(element)
488
+ execute :is_element_selected, id: element
489
+ end
490
+
491
+ def element_displayed?(element)
492
+ execute :is_element_displayed, id: element
493
+ end
494
+
495
+ def element_value_of_css_property(element, prop)
496
+ execute :get_element_css_value, id: element, property_name: prop
497
+ end
498
+
499
+ #
500
+ # finding elements
501
+ #
502
+
503
+ def active_element
504
+ Element.new self, element_id_from(execute(:get_active_element))
505
+ end
506
+
507
+ alias_method :switch_to_active_element, :active_element
508
+
509
+ def find_element_by(how, what, parent = nil)
510
+ how, what = convert_locators(how, what)
511
+
512
+ id = if parent
513
+ execute :find_child_element, {id: parent}, {using: how, value: what}
514
+ else
515
+ execute :find_element, {}, {using: how, value: what}
516
+ end
517
+ Element.new self, element_id_from(id)
518
+ end
519
+
520
+ def find_elements_by(how, what, parent = nil)
521
+ how, what = convert_locators(how, what)
522
+
523
+ ids = if parent
524
+ execute :find_child_elements, {id: parent}, {using: how, value: what}
525
+ else
526
+ execute :find_elements, {}, {using: how, value: what}
527
+ end
528
+
529
+ ids.map { |id| Element.new self, element_id_from(id) }
530
+ end
531
+
532
+ private
533
+
534
+ def execute(*)
535
+ super['value']
536
+ end
537
+
538
+ def convert_locators(how, what)
539
+ case how
540
+ when 'class name'
541
+ how = 'css selector'
542
+ what = ".#{escape_css(what)}"
543
+ when 'id'
544
+ how = 'css selector'
545
+ what = "##{escape_css(what)}"
546
+ when 'name'
547
+ how = 'css selector'
548
+ what = "*[name='#{escape_css(what)}']"
549
+ when 'tag name'
550
+ how = 'css selector'
551
+ end
552
+ [how, what]
553
+ end
554
+
555
+ ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]\(\)])/
556
+ UNICODE_CODE_POINT = 30
557
+
558
+ # Escapes invalid characters in CSS selector.
559
+ # @see https://mathiasbynens.be/notes/css-escapes
560
+ def escape_css(string)
561
+ string = string.gsub(ESCAPE_CSS_REGEXP) { |match| "\\#{match}" }
562
+ if !string.empty? && string[0] =~ /[[:digit:]]/
563
+ string = "\\#{UNICODE_CODE_POINT + Integer(string[0])} #{string[1..-1]}"
564
+ end
565
+
566
+ string
567
+ end
568
+
569
+ end # Bridge
570
+ end # W3C
571
+ end # Remote
572
+ end # WebDriver
573
+ end # Selenium