selenium-webdriver 3.0.0.beta4.0 → 4.0.0.alpha5
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.
- checksums.yaml +7 -0
- data/CHANGES +679 -0
- data/Gemfile +2 -0
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/lib/selenium-webdriver.rb +2 -2
- data/lib/selenium/server.rb +23 -16
- data/lib/selenium/webdriver.rb +43 -25
- data/lib/selenium/webdriver/atoms.rb +20 -1
- data/lib/selenium/webdriver/atoms/findElements.js +122 -0
- data/lib/selenium/webdriver/atoms/getAttribute.js +84 -11
- data/lib/selenium/webdriver/atoms/isDisplayed.js +100 -0
- data/lib/selenium/webdriver/chrome.rb +15 -20
- data/lib/selenium/webdriver/chrome/bridge.rb +29 -66
- data/lib/selenium/webdriver/{edge/service.rb → chrome/driver.rb} +19 -22
- data/lib/selenium/webdriver/chrome/options.rb +221 -0
- data/lib/selenium/webdriver/chrome/profile.rb +6 -7
- data/lib/selenium/webdriver/chrome/service.rb +20 -20
- data/lib/selenium/webdriver/common.rb +19 -11
- data/lib/selenium/webdriver/common/action_builder.rb +98 -246
- data/lib/selenium/webdriver/common/alert.rb +2 -7
- data/lib/selenium/webdriver/common/driver.rb +89 -51
- data/lib/selenium/webdriver/common/driver_extensions/downloads_files.rb +45 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_addons.rb +50 -0
- data/lib/selenium/webdriver/common/driver_extensions/{has_input_devices.rb → has_debugger.rb} +12 -25
- data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +38 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +3 -5
- data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +51 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +3 -3
- data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +51 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_remote_status.rb +2 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_session_id.rb +2 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_web_storage.rb +2 -2
- data/lib/selenium/webdriver/common/driver_extensions/rotatable.rb +4 -4
- data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +9 -3
- data/lib/selenium/webdriver/common/driver_extensions/uploads_files.rb +3 -5
- data/lib/selenium/webdriver/common/element.rb +32 -10
- data/lib/selenium/webdriver/common/error.rb +103 -121
- data/lib/selenium/webdriver/common/file_reaper.rb +3 -5
- data/lib/selenium/webdriver/common/html5/local_storage.rb +2 -2
- data/lib/selenium/webdriver/common/html5/session_storage.rb +2 -2
- data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +3 -2
- data/lib/selenium/webdriver/common/interactions/input_device.rb +54 -0
- data/lib/selenium/webdriver/common/interactions/interaction.rb +53 -0
- data/lib/selenium/webdriver/{phantomjs.rb → common/interactions/interactions.rb} +17 -20
- data/lib/selenium/webdriver/common/interactions/key_actions.rb +145 -0
- data/lib/selenium/webdriver/common/interactions/key_input.rb +66 -0
- data/lib/selenium/webdriver/common/interactions/none_input.rb +36 -0
- data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +363 -0
- data/lib/selenium/webdriver/common/interactions/pointer_input.rb +139 -0
- data/lib/selenium/webdriver/common/keys.rb +37 -15
- data/lib/selenium/webdriver/common/log_entry.rb +4 -4
- data/lib/selenium/webdriver/common/logger.rb +147 -0
- data/lib/selenium/webdriver/common/logs.rb +2 -2
- data/lib/selenium/webdriver/common/manager.rb +177 -0
- data/lib/selenium/webdriver/common/navigation.rb +2 -2
- data/lib/selenium/webdriver/common/options.rb +47 -105
- data/lib/selenium/webdriver/common/platform.rb +44 -38
- data/lib/selenium/webdriver/common/port_prober.rb +8 -19
- data/lib/selenium/webdriver/common/profile_helper.rb +13 -5
- data/lib/selenium/webdriver/common/proxy.rb +14 -8
- data/lib/selenium/webdriver/common/search_context.rb +16 -20
- data/lib/selenium/webdriver/common/service.rb +115 -30
- data/lib/selenium/webdriver/common/socket_lock.rb +12 -7
- data/lib/selenium/webdriver/common/socket_poller.rb +29 -22
- data/lib/selenium/webdriver/common/target_locator.rb +6 -6
- data/lib/selenium/webdriver/common/timeouts.rb +2 -2
- data/lib/selenium/webdriver/common/wait.rb +14 -8
- data/lib/selenium/webdriver/common/window.rb +38 -2
- data/lib/selenium/webdriver/common/zipper.rb +3 -5
- data/lib/selenium/webdriver/edge.rb +33 -20
- data/lib/selenium/webdriver/edge_chrome/bridge.rb +30 -0
- data/lib/selenium/webdriver/edge_chrome/driver.rb +38 -0
- data/lib/selenium/webdriver/{common/driver_extensions/has_touch_screen.rb → edge_chrome/options.rb} +10 -12
- data/lib/selenium/webdriver/edge_chrome/profile.rb +33 -0
- data/lib/selenium/webdriver/edge_chrome/service.rb +36 -0
- data/lib/selenium/webdriver/edge_html/driver.rb +39 -0
- data/lib/selenium/webdriver/edge_html/options.rb +91 -0
- data/lib/selenium/webdriver/edge_html/service.rb +47 -0
- data/lib/selenium/webdriver/firefox.rb +24 -29
- data/lib/selenium/webdriver/firefox/bridge.rb +15 -39
- data/lib/selenium/webdriver/firefox/{util.rb → driver.rb} +13 -19
- data/lib/selenium/webdriver/firefox/extension.rb +28 -8
- data/lib/selenium/webdriver/firefox/options.rb +162 -0
- data/lib/selenium/webdriver/firefox/profile.rb +23 -82
- data/lib/selenium/webdriver/firefox/profiles_ini.rb +5 -5
- data/lib/selenium/webdriver/firefox/service.rb +18 -37
- data/lib/selenium/webdriver/ie.rb +13 -19
- data/lib/selenium/webdriver/ie/driver.rb +40 -0
- data/lib/selenium/webdriver/ie/options.rb +118 -0
- data/lib/selenium/webdriver/ie/service.rb +20 -20
- data/lib/selenium/webdriver/remote.rb +15 -17
- data/lib/selenium/webdriver/remote/bridge.rb +281 -300
- data/lib/selenium/webdriver/remote/capabilities.rb +102 -83
- data/lib/selenium/webdriver/remote/commands.rb +132 -192
- data/lib/selenium/webdriver/remote/driver.rb +52 -0
- data/lib/selenium/webdriver/remote/http/common.rb +23 -13
- data/lib/selenium/webdriver/remote/http/curb.rb +10 -7
- data/lib/selenium/webdriver/remote/http/default.rb +52 -32
- data/lib/selenium/webdriver/remote/http/persistent.rb +8 -4
- data/lib/selenium/webdriver/remote/response.rb +32 -35
- data/lib/selenium/webdriver/remote/server_error.rb +2 -2
- data/lib/selenium/webdriver/safari.rb +24 -22
- data/lib/selenium/webdriver/safari/bridge.rb +21 -21
- data/lib/selenium/webdriver/safari/driver.rb +41 -0
- data/lib/selenium/webdriver/safari/options.rb +66 -0
- data/lib/selenium/webdriver/safari/service.rb +8 -24
- data/lib/selenium/webdriver/support.rb +3 -2
- data/lib/selenium/webdriver/support/abstract_event_listener.rb +2 -2
- data/lib/selenium/webdriver/support/block_event_listener.rb +3 -3
- data/lib/selenium/webdriver/support/color.rb +13 -13
- data/lib/selenium/webdriver/support/escaper.rb +2 -2
- data/lib/selenium/webdriver/support/event_firing_bridge.rb +4 -4
- data/lib/selenium/webdriver/support/relative_locator.rb +51 -0
- data/lib/selenium/webdriver/support/select.rb +20 -21
- data/lib/selenium/webdriver/version.rb +24 -0
- data/selenium-webdriver.gemspec +31 -17
- metadata +331 -248
- data/lib/selenium/webdriver/common/bridge_helper.rb +0 -82
- data/lib/selenium/webdriver/common/keyboard.rb +0 -69
- data/lib/selenium/webdriver/common/mouse.rb +0 -88
- data/lib/selenium/webdriver/common/touch_action_builder.rb +0 -81
- data/lib/selenium/webdriver/common/touch_screen.rb +0 -121
- data/lib/selenium/webdriver/common/w3c_error.rb +0 -191
- data/lib/selenium/webdriver/edge/bridge.rb +0 -76
- data/lib/selenium/webdriver/edge/legacy_support.rb +0 -117
- data/lib/selenium/webdriver/firefox/binary.rb +0 -186
- data/lib/selenium/webdriver/firefox/extension/prefs.json +0 -69
- data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
- data/lib/selenium/webdriver/firefox/launcher.rb +0 -114
- data/lib/selenium/webdriver/firefox/native/linux/amd64/x_ignore_nofocus.so +0 -0
- data/lib/selenium/webdriver/firefox/native/linux/x86/x_ignore_nofocus.so +0 -0
- data/lib/selenium/webdriver/firefox/w3c_bridge.rb +0 -79
- data/lib/selenium/webdriver/ie/bridge.rb +0 -76
- data/lib/selenium/webdriver/phantomjs/bridge.rb +0 -65
- data/lib/selenium/webdriver/phantomjs/service.rb +0 -66
- data/lib/selenium/webdriver/remote/w3c_bridge.rb +0 -682
- data/lib/selenium/webdriver/remote/w3c_capabilities.rb +0 -228
- data/lib/selenium/webdriver/remote/w3c_commands.rb +0 -135
|
Binary file
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
#
|
|
3
|
-
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
|
-
# or more contributor license agreements. See the NOTICE file
|
|
5
|
-
# distributed with this work for additional information
|
|
6
|
-
# regarding copyright ownership. The SFC licenses this file
|
|
7
|
-
# to you under the Apache License, Version 2.0 (the
|
|
8
|
-
# "License"); you may not use this file except in compliance
|
|
9
|
-
# with the License. You may obtain a copy of the License at
|
|
10
|
-
#
|
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
-
#
|
|
13
|
-
# Unless required by applicable law or agreed to in writing,
|
|
14
|
-
# software distributed under the License is distributed on an
|
|
15
|
-
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
16
|
-
# KIND, either express or implied. See the License for the
|
|
17
|
-
# specific language governing permissions and limitations
|
|
18
|
-
# under the License.
|
|
19
|
-
|
|
20
|
-
module Selenium
|
|
21
|
-
module WebDriver
|
|
22
|
-
module Firefox
|
|
23
|
-
# @api private
|
|
24
|
-
class Launcher
|
|
25
|
-
SOCKET_LOCK_TIMEOUT = 45
|
|
26
|
-
STABLE_CONNECTION_TIMEOUT = 60
|
|
27
|
-
|
|
28
|
-
def initialize(binary, port, profile = nil)
|
|
29
|
-
@binary = binary
|
|
30
|
-
@port = Integer(port)
|
|
31
|
-
|
|
32
|
-
raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
|
|
33
|
-
|
|
34
|
-
if profile.is_a? Profile
|
|
35
|
-
@profile = profile
|
|
36
|
-
else
|
|
37
|
-
@profile_name = profile
|
|
38
|
-
@profile = nil
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
@host = '127.0.0.1'
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def url
|
|
45
|
-
"http://#{@host}:#{@port}/hub"
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def launch
|
|
49
|
-
socket_lock.locked do
|
|
50
|
-
find_free_port
|
|
51
|
-
create_profile
|
|
52
|
-
start
|
|
53
|
-
connect_until_stable
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
self
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def quit
|
|
60
|
-
@binary.quit
|
|
61
|
-
FileReaper.reap(@profile_dir) if @profile_dir
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def find_free_port
|
|
65
|
-
@port = PortProber.above @port
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def create_profile
|
|
69
|
-
fetch_profile unless @profile
|
|
70
|
-
|
|
71
|
-
@profile.add_webdriver_extension
|
|
72
|
-
@profile.port = @port
|
|
73
|
-
|
|
74
|
-
@profile_dir = @profile.layout_on_disk
|
|
75
|
-
FileReaper << @profile_dir
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def start
|
|
79
|
-
assert_profile
|
|
80
|
-
@binary.start_with @profile, @profile_dir, '-foreground'
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def connect_until_stable
|
|
84
|
-
poller = SocketPoller.new(@host, @port, STABLE_CONNECTION_TIMEOUT)
|
|
85
|
-
|
|
86
|
-
return if poller.connected?
|
|
87
|
-
@binary.quit
|
|
88
|
-
error = "unable to obtain stable firefox connection in #{STABLE_CONNECTION_TIMEOUT} seconds (#{@host}:#{@port})"
|
|
89
|
-
raise Error::WebDriverError, error
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def fetch_profile
|
|
93
|
-
if @profile_name
|
|
94
|
-
@profile = Profile.from_name @profile_name
|
|
95
|
-
|
|
96
|
-
unless @profile
|
|
97
|
-
raise Error::WebDriverError, "unable to find profile named: #{@profile_name.inspect}"
|
|
98
|
-
end
|
|
99
|
-
else
|
|
100
|
-
@profile = Profile.new
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def assert_profile
|
|
105
|
-
raise Error::WebDriverError, 'must create_profile first' unless @profile && @profile_dir
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def socket_lock
|
|
109
|
-
@socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)
|
|
110
|
-
end
|
|
111
|
-
end # Launcher
|
|
112
|
-
end # Firefox
|
|
113
|
-
end # WebDriver
|
|
114
|
-
end # Selenium
|
|
Binary file
|
|
Binary file
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
#
|
|
3
|
-
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
|
-
# or more contributor license agreements. See the NOTICE file
|
|
5
|
-
# distributed with this work for additional information
|
|
6
|
-
# regarding copyright ownership. The SFC licenses this file
|
|
7
|
-
# to you under the Apache License, Version 2.0 (the
|
|
8
|
-
# "License"); you may not use this file except in compliance
|
|
9
|
-
# with the License. You may obtain a copy of the License at
|
|
10
|
-
#
|
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
-
#
|
|
13
|
-
# Unless required by applicable law or agreed to in writing,
|
|
14
|
-
# software distributed under the License is distributed on an
|
|
15
|
-
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
16
|
-
# KIND, either express or implied. See the License for the
|
|
17
|
-
# specific language governing permissions and limitations
|
|
18
|
-
# under the License.
|
|
19
|
-
|
|
20
|
-
module Selenium
|
|
21
|
-
module WebDriver
|
|
22
|
-
module Firefox
|
|
23
|
-
# @api private
|
|
24
|
-
class W3CBridge < Remote::W3CBridge
|
|
25
|
-
def initialize(opts = {})
|
|
26
|
-
port = opts.delete(:port) || Service::DEFAULT_PORT
|
|
27
|
-
opts[:desired_capabilities] = create_capabilities(opts)
|
|
28
|
-
service_args = opts.delete(:service_args) || {}
|
|
29
|
-
|
|
30
|
-
@service = Service.new(Firefox.driver_path, port, *extract_service_args(service_args))
|
|
31
|
-
@service.start
|
|
32
|
-
opts[:url] = @service.uri
|
|
33
|
-
|
|
34
|
-
super(opts)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def browser
|
|
38
|
-
:firefox
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def driver_extensions
|
|
42
|
-
[
|
|
43
|
-
DriverExtensions::TakesScreenshot,
|
|
44
|
-
DriverExtensions::HasInputDevices,
|
|
45
|
-
DriverExtensions::HasWebStorage
|
|
46
|
-
]
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def quit
|
|
50
|
-
super
|
|
51
|
-
ensure
|
|
52
|
-
@service.stop if @service
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
private
|
|
56
|
-
|
|
57
|
-
def create_capabilities(opts)
|
|
58
|
-
caps = opts.delete(:desired_capabilities) || Remote::W3CCapabilities.firefox
|
|
59
|
-
firefox_options_caps = caps[:firefox_options] || {}
|
|
60
|
-
caps[:firefox_options] = firefox_options_caps.merge(opts[:firefox_options] || {})
|
|
61
|
-
|
|
62
|
-
Binary.path = caps[:firefox_options][:binary] if caps[:firefox_options].key?(:binary)
|
|
63
|
-
caps
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def extract_service_args(args = {})
|
|
67
|
-
service_args = []
|
|
68
|
-
service_args << "--binary=#{args[:binary]}" if args.key?(:binary)
|
|
69
|
-
service_args << "–-log=#{args[:log]}" if args.key?(:log)
|
|
70
|
-
service_args << "–-marionette-port=#{args[:marionette_port]}" if args.key?(:marionette_port)
|
|
71
|
-
service_args << "–-host=#{args[:host]}" if args.key?(:host)
|
|
72
|
-
service_args << "–-port=#{args[:port]}" if args.key?(:port)
|
|
73
|
-
service_args
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
end # W3CBridge
|
|
77
|
-
end # Firefox
|
|
78
|
-
end # WebDriver
|
|
79
|
-
end # Selenium
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
#
|
|
3
|
-
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
|
-
# or more contributor license agreements. See the NOTICE file
|
|
5
|
-
# distributed with this work for additional information
|
|
6
|
-
# regarding copyright ownership. The SFC licenses this file
|
|
7
|
-
# to you under the Apache License, Version 2.0 (the
|
|
8
|
-
# "License"); you may not use this file except in compliance
|
|
9
|
-
# with the License. You may obtain a copy of the License at
|
|
10
|
-
#
|
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
-
#
|
|
13
|
-
# Unless required by applicable law or agreed to in writing,
|
|
14
|
-
# software distributed under the License is distributed on an
|
|
15
|
-
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
16
|
-
# KIND, either express or implied. See the License for the
|
|
17
|
-
# specific language governing permissions and limitations
|
|
18
|
-
# under the License.
|
|
19
|
-
|
|
20
|
-
module Selenium
|
|
21
|
-
module WebDriver
|
|
22
|
-
module IE
|
|
23
|
-
#
|
|
24
|
-
# @api private
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
class Bridge < Remote::Bridge
|
|
28
|
-
def initialize(opts = {})
|
|
29
|
-
port = opts.delete(:port) || Service::DEFAULT_PORT
|
|
30
|
-
service_args = opts.delete(:service_args) || {}
|
|
31
|
-
service_args = match_legacy(opts, service_args)
|
|
32
|
-
@service = Service.new(IE.driver_path, port, *extract_service_args(service_args))
|
|
33
|
-
@service.start
|
|
34
|
-
opts[:url] = @service.uri
|
|
35
|
-
|
|
36
|
-
caps = opts[:desired_capabilities] ||= Remote::Capabilities.internet_explorer
|
|
37
|
-
caps[:ignore_protected_mode_settings] = true if opts.delete(:introduce_flakiness_by_ignoring_security_domains)
|
|
38
|
-
caps[:native_events] = opts.delete(:native_events) != false
|
|
39
|
-
|
|
40
|
-
super(opts)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def browser
|
|
44
|
-
:internet_explorer
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def driver_extensions
|
|
48
|
-
[DriverExtensions::TakesScreenshot, DriverExtensions::HasInputDevices]
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def quit
|
|
52
|
-
super
|
|
53
|
-
ensure
|
|
54
|
-
@service.stop if @service
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
private
|
|
58
|
-
|
|
59
|
-
def match_legacy(opts, args)
|
|
60
|
-
args[:log_level] = opts.delete(:log_level) if opts.key?(:log_level)
|
|
61
|
-
args[:log_file] = opts.delete(:log_file) if opts.key?(:log_file)
|
|
62
|
-
args[:implementation] = opts.delete(:implementation) if opts.key?(:implementation)
|
|
63
|
-
args
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def extract_service_args(args)
|
|
67
|
-
service_args = []
|
|
68
|
-
service_args << "--log-level=#{args.delete(:log_level).to_s.upcase}" if args.key?(:log_level)
|
|
69
|
-
service_args << "--log-file=#{args.delete(:log_file)}" if args.key?(:log_file)
|
|
70
|
-
service_args << "--implementation=#{args.delete(:implementation).to_s.upcase}" if args.key?(:implementation)
|
|
71
|
-
service_args
|
|
72
|
-
end
|
|
73
|
-
end # Bridge
|
|
74
|
-
end # IE
|
|
75
|
-
end # WebDriver
|
|
76
|
-
end # Selenium
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
#
|
|
3
|
-
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
|
-
# or more contributor license agreements. See the NOTICE file
|
|
5
|
-
# distributed with this work for additional information
|
|
6
|
-
# regarding copyright ownership. The SFC licenses this file
|
|
7
|
-
# to you under the Apache License, Version 2.0 (the
|
|
8
|
-
# "License"); you may not use this file except in compliance
|
|
9
|
-
# with the License. You may obtain a copy of the License at
|
|
10
|
-
#
|
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
-
#
|
|
13
|
-
# Unless required by applicable law or agreed to in writing,
|
|
14
|
-
# software distributed under the License is distributed on an
|
|
15
|
-
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
16
|
-
# KIND, either express or implied. See the License for the
|
|
17
|
-
# specific language governing permissions and limitations
|
|
18
|
-
# under the License.
|
|
19
|
-
|
|
20
|
-
module Selenium
|
|
21
|
-
module WebDriver
|
|
22
|
-
module PhantomJS
|
|
23
|
-
#
|
|
24
|
-
# @api private
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
class Bridge < Remote::Bridge
|
|
28
|
-
def initialize(opts = {})
|
|
29
|
-
port = opts.delete(:port) || Service::DEFAULT_PORT
|
|
30
|
-
opts[:desired_capabilities] ||= Remote::Capabilities.phantomjs
|
|
31
|
-
|
|
32
|
-
unless opts.key?(:url)
|
|
33
|
-
args = opts.delete(:args) || opts[:desired_capabilities]['phantomjs.cli.args']
|
|
34
|
-
@service = Service.new(PhantomJS.path, port, *args)
|
|
35
|
-
@service.start
|
|
36
|
-
opts[:url] = @service.uri
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
super(opts)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def browser
|
|
43
|
-
:phantomjs
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def driver_extensions
|
|
47
|
-
[
|
|
48
|
-
DriverExtensions::TakesScreenshot,
|
|
49
|
-
DriverExtensions::HasInputDevices
|
|
50
|
-
]
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def capabilities
|
|
54
|
-
@capabilities ||= Remote::Capabilities.phantomjs
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def quit
|
|
58
|
-
super
|
|
59
|
-
ensure
|
|
60
|
-
@service.stop if @service
|
|
61
|
-
end
|
|
62
|
-
end # Bridge
|
|
63
|
-
end # PhantomJS
|
|
64
|
-
end # WebDriver
|
|
65
|
-
end # Selenium
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
#
|
|
3
|
-
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
|
-
# or more contributor license agreements. See the NOTICE file
|
|
5
|
-
# distributed with this work for additional information
|
|
6
|
-
# regarding copyright ownership. The SFC licenses this file
|
|
7
|
-
# to you under the Apache License, Version 2.0 (the
|
|
8
|
-
# "License"); you may not use this file except in compliance
|
|
9
|
-
# with the License. You may obtain a copy of the License at
|
|
10
|
-
#
|
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
-
#
|
|
13
|
-
# Unless required by applicable law or agreed to in writing,
|
|
14
|
-
# software distributed under the License is distributed on an
|
|
15
|
-
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
16
|
-
# KIND, either express or implied. See the License for the
|
|
17
|
-
# specific language governing permissions and limitations
|
|
18
|
-
# under the License.
|
|
19
|
-
|
|
20
|
-
module Selenium
|
|
21
|
-
module WebDriver
|
|
22
|
-
module PhantomJS
|
|
23
|
-
#
|
|
24
|
-
# @api private
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
class Service < WebDriver::Service
|
|
28
|
-
DEFAULT_PORT = 8910
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
def start_process
|
|
33
|
-
server_command = [@executable_path, "--webdriver=#{@port}", *@extra_args]
|
|
34
|
-
@process = ChildProcess.build(*server_command.compact)
|
|
35
|
-
|
|
36
|
-
if $DEBUG
|
|
37
|
-
@process.io.inherit!
|
|
38
|
-
elsif Platform.jruby?
|
|
39
|
-
# apparently we need to read the output for phantomjs to work on jruby
|
|
40
|
-
@process.io.stdout = @process.io.stderr = File.new(Platform.null_device, 'w')
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
@process.start
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def stop_process
|
|
47
|
-
super
|
|
48
|
-
return unless Platform.jruby? && !$DEBUG
|
|
49
|
-
begin
|
|
50
|
-
@process.io.close
|
|
51
|
-
rescue
|
|
52
|
-
nil
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def stop_server
|
|
57
|
-
connect_to_server { |http| http.get('/shutdown') }
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def cannot_connect_error_text
|
|
61
|
-
"unable to connect to phantomjs @ #{uri} after #{START_TIMEOUT} seconds"
|
|
62
|
-
end
|
|
63
|
-
end # Service
|
|
64
|
-
end # PhantomJS
|
|
65
|
-
end # WebDriver
|
|
66
|
-
end # Selenium
|
|
@@ -1,682 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
#
|
|
3
|
-
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
|
-
# or more contributor license agreements. See the NOTICE file
|
|
5
|
-
# distributed with this work for additional information
|
|
6
|
-
# regarding copyright ownership. The SFC licenses this file
|
|
7
|
-
# to you under the Apache License, Version 2.0 (the
|
|
8
|
-
# "License"); you may not use this file except in compliance
|
|
9
|
-
# with the License. You may obtain a copy of the License at
|
|
10
|
-
#
|
|
11
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
-
#
|
|
13
|
-
# Unless required by applicable law or agreed to in writing,
|
|
14
|
-
# software distributed under the License is distributed on an
|
|
15
|
-
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
16
|
-
# KIND, either express or implied. See the License for the
|
|
17
|
-
# specific language governing permissions and limitations
|
|
18
|
-
# under the License.
|
|
19
|
-
|
|
20
|
-
require 'json'
|
|
21
|
-
|
|
22
|
-
module Selenium
|
|
23
|
-
module WebDriver
|
|
24
|
-
module Remote
|
|
25
|
-
#
|
|
26
|
-
# Low level bridge to the remote server, through which the rest of the API works.
|
|
27
|
-
#
|
|
28
|
-
# @api private
|
|
29
|
-
#
|
|
30
|
-
|
|
31
|
-
class W3CBridge
|
|
32
|
-
include BridgeHelper
|
|
33
|
-
include Atoms
|
|
34
|
-
|
|
35
|
-
# TODO: constant shouldn't be modified in class
|
|
36
|
-
COMMANDS = {}
|
|
37
|
-
|
|
38
|
-
#
|
|
39
|
-
# Defines a wrapper method for a command, which ultimately calls #execute.
|
|
40
|
-
#
|
|
41
|
-
# @param name [Symbol]
|
|
42
|
-
# name of the resulting method
|
|
43
|
-
# @param verb [Symbol]
|
|
44
|
-
# the appropriate http verb, such as :get, :post, or :delete
|
|
45
|
-
# @param url [String]
|
|
46
|
-
# a URL template, which can include some arguments, much like the definitions on the server.
|
|
47
|
-
# the :session_id parameter is implicitly handled, but the remainder will become required method arguments.
|
|
48
|
-
#
|
|
49
|
-
|
|
50
|
-
def self.command(name, verb, url)
|
|
51
|
-
COMMANDS[name] = [verb, url.freeze]
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
attr_accessor :context, :http, :file_detector
|
|
55
|
-
attr_reader :capabilities
|
|
56
|
-
|
|
57
|
-
#
|
|
58
|
-
# Initializes the bridge with the given server URL.
|
|
59
|
-
#
|
|
60
|
-
# @param url [String] url for the remote server
|
|
61
|
-
# @param http_client [Object] an HTTP client instance that implements the same protocol as Http::Default
|
|
62
|
-
# @param desired_capabilities [Capabilities] an instance of Remote::Capabilities describing the capabilities you want
|
|
63
|
-
#
|
|
64
|
-
|
|
65
|
-
def initialize(opts = {})
|
|
66
|
-
edge_check(opts)
|
|
67
|
-
|
|
68
|
-
opts = opts.dup
|
|
69
|
-
|
|
70
|
-
port = opts.delete(:port) || 4444
|
|
71
|
-
http_client = opts.delete(:http_client) { Http::Default.new }
|
|
72
|
-
desired_capabilities = opts.delete(:desired_capabilities) { W3CCapabilities.firefox }
|
|
73
|
-
url = opts.delete(:url) { "http://#{Platform.localhost}:#{port}/wd/hub" }
|
|
74
|
-
|
|
75
|
-
desired_capabilities = W3CCapabilities.send(desired_capabilities) if desired_capabilities.is_a? Symbol
|
|
76
|
-
|
|
77
|
-
desired_capabilities[:marionette] = opts.delete(:marionette) unless opts[:marionette].nil?
|
|
78
|
-
|
|
79
|
-
unless opts.empty?
|
|
80
|
-
raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
uri = url.is_a?(URI) ? url : URI.parse(url)
|
|
84
|
-
uri.path += '/' unless uri.path =~ %r{\/$}
|
|
85
|
-
|
|
86
|
-
http_client.server_url = uri
|
|
87
|
-
|
|
88
|
-
@http = http_client
|
|
89
|
-
@capabilities = create_session(desired_capabilities)
|
|
90
|
-
@file_detector = nil
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def browser
|
|
94
|
-
@browser ||= (
|
|
95
|
-
name = @capabilities.browser_name
|
|
96
|
-
name ? name.tr(' ', '_').to_sym : 'unknown'
|
|
97
|
-
)
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def edge_check(opts)
|
|
101
|
-
caps = opts[:desired_capabilities]
|
|
102
|
-
return unless caps && caps[:browser_name] && caps[:browser_name] == 'MicrosoftEdge'
|
|
103
|
-
require_relative '../edge/legacy_support'
|
|
104
|
-
extend Edge::LegacySupport
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def driver_extensions
|
|
108
|
-
[
|
|
109
|
-
DriverExtensions::HasInputDevices,
|
|
110
|
-
DriverExtensions::UploadsFiles,
|
|
111
|
-
DriverExtensions::TakesScreenshot,
|
|
112
|
-
DriverExtensions::HasSessionId,
|
|
113
|
-
DriverExtensions::Rotatable,
|
|
114
|
-
DriverExtensions::HasTouchScreen,
|
|
115
|
-
DriverExtensions::HasRemoteStatus,
|
|
116
|
-
DriverExtensions::HasWebStorage
|
|
117
|
-
]
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
#
|
|
121
|
-
# Returns the current session ID.
|
|
122
|
-
#
|
|
123
|
-
|
|
124
|
-
def session_id
|
|
125
|
-
@session_id || raise(Error::WebDriverError, 'no current session exists')
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def create_session(desired_capabilities)
|
|
129
|
-
# TODO - Remove this when Mozilla fixes bug
|
|
130
|
-
desired_capabilities[:browser_name] = 'firefox' if desired_capabilities[:browser_name] == 'Firefox'
|
|
131
|
-
|
|
132
|
-
resp = raw_execute :newSession, {}, {desiredCapabilities: desired_capabilities}
|
|
133
|
-
@session_id = resp['sessionId']
|
|
134
|
-
return W3CCapabilities.json_create resp['value'] if @session_id
|
|
135
|
-
|
|
136
|
-
raise Error::WebDriverError, 'no sessionId in returned payload'
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
def status
|
|
140
|
-
jwp = Selenium::WebDriver::Remote::Bridge::COMMANDS[:status]
|
|
141
|
-
self.class.command(:status, jwp.first, jwp.last)
|
|
142
|
-
execute :status
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def get(url)
|
|
146
|
-
execute :get, {}, {url: url}
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def implicit_wait_timeout=(milliseconds)
|
|
150
|
-
timeout('implicit', milliseconds)
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def script_timeout=(milliseconds)
|
|
154
|
-
timeout('script', milliseconds)
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def timeout(type, milliseconds)
|
|
158
|
-
execute :setTimeout, {}, {type: type, ms: milliseconds}
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
#
|
|
162
|
-
# alerts
|
|
163
|
-
#
|
|
164
|
-
|
|
165
|
-
def accept_alert
|
|
166
|
-
execute :acceptAlert
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
def dismiss_alert
|
|
170
|
-
execute :dismissAlert
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def alert=(keys)
|
|
174
|
-
execute :sendAlertText, {}, {handler: 'prompt', text: keys}
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def alert_text
|
|
178
|
-
execute :getAlertText
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
#
|
|
182
|
-
# navigation
|
|
183
|
-
#
|
|
184
|
-
|
|
185
|
-
def go_back
|
|
186
|
-
execute :back
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
def go_forward
|
|
190
|
-
execute :forward
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
def url
|
|
194
|
-
execute :getCurrentUrl
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
def title
|
|
198
|
-
execute :getTitle
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
def page_source
|
|
202
|
-
execute_script('var source = document.documentElement.outerHTML;' \
|
|
203
|
-
'if (!source) { source = new XMLSerializer().serializeToString(document); }' \
|
|
204
|
-
'return source;')
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
def switch_to_window(name)
|
|
208
|
-
execute :switchToWindow, {}, {handle: name}
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
def switch_to_frame(id)
|
|
212
|
-
id = find_element_by('id', id) if id.is_a? String
|
|
213
|
-
execute :switchToFrame, {}, {id: id}
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
def switch_to_parent_frame
|
|
217
|
-
execute :switchToParentFrame
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
def switch_to_default_content
|
|
221
|
-
switch_to_frame nil
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
QUIT_ERRORS = [IOError].freeze
|
|
225
|
-
|
|
226
|
-
def quit
|
|
227
|
-
execute :deleteSession
|
|
228
|
-
http.close
|
|
229
|
-
rescue *QUIT_ERRORS
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
def close
|
|
233
|
-
execute :closeWindow
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
def refresh
|
|
237
|
-
execute :refresh
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
#
|
|
241
|
-
# window handling
|
|
242
|
-
#
|
|
243
|
-
|
|
244
|
-
def window_handles
|
|
245
|
-
execute :getWindowHandles
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
def window_handle
|
|
249
|
-
execute :getWindowHandle
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
def resize_window(width, height, handle = :current)
|
|
253
|
-
unless handle == :current
|
|
254
|
-
raise Error::WebDriverError, 'Switch to desired window before changing its size'
|
|
255
|
-
end
|
|
256
|
-
execute :setWindowSize, {}, {width: width,
|
|
257
|
-
height: height}
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
def maximize_window(handle = :current)
|
|
261
|
-
unless handle == :current
|
|
262
|
-
raise Error::UnsupportedOperationError, 'Switch to desired window before changing its size'
|
|
263
|
-
end
|
|
264
|
-
execute :maximizeWindow
|
|
265
|
-
end
|
|
266
|
-
|
|
267
|
-
def full_screen_window
|
|
268
|
-
execute :fullscreenWindow
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
def window_size(handle = :current)
|
|
272
|
-
unless handle == :current
|
|
273
|
-
raise Error::UnsupportedOperationError, 'Switch to desired window before getting its size'
|
|
274
|
-
end
|
|
275
|
-
data = execute :getWindowSize
|
|
276
|
-
|
|
277
|
-
Dimension.new data['width'], data['height']
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
def reposition_window(_x, _y, _handle = nil)
|
|
281
|
-
raise Error::UnsupportedOperationError, 'The W3C standard does not currently support setting the Window Position'
|
|
282
|
-
end
|
|
283
|
-
|
|
284
|
-
def window_position(_handle = nil)
|
|
285
|
-
raise Error::UnsupportedOperationError, 'The W3C standard does not currently support getting the Window Position'
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
def screenshot
|
|
289
|
-
execute :takeScreenshot
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
#
|
|
293
|
-
# HTML 5
|
|
294
|
-
#
|
|
295
|
-
|
|
296
|
-
def local_storage_item(key, value = nil)
|
|
297
|
-
if value
|
|
298
|
-
execute_script("localStorage.setItem('#{key}', '#{value}')")
|
|
299
|
-
else
|
|
300
|
-
execute_script("return localStorage.getItem('#{key}')")
|
|
301
|
-
end
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
def remove_local_storage_item(key)
|
|
305
|
-
execute_script("localStorage.removeItem('#{key}')")
|
|
306
|
-
end
|
|
307
|
-
|
|
308
|
-
def local_storage_keys
|
|
309
|
-
execute_script('return Object.keys(localStorage)')
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
def clear_local_storage
|
|
313
|
-
execute_script('localStorage.clear()')
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
def local_storage_size
|
|
317
|
-
execute_script('return localStorage.length')
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
def session_storage_item(key, value = nil)
|
|
321
|
-
if value
|
|
322
|
-
execute_script("sessionStorage.setItem('#{key}', '#{value}')")
|
|
323
|
-
else
|
|
324
|
-
execute_script("return sessionStorage.getItem('#{key}')")
|
|
325
|
-
end
|
|
326
|
-
end
|
|
327
|
-
|
|
328
|
-
def remove_session_storage_item(key)
|
|
329
|
-
execute_script("sessionStorage.removeItem('#{key}')")
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
def session_storage_keys
|
|
333
|
-
execute_script('return Object.keys(sessionStorage)')
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
def clear_session_storage
|
|
337
|
-
execute_script('sessionStorage.clear()')
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
def session_storage_size
|
|
341
|
-
execute_script('return sessionStorage.length')
|
|
342
|
-
end
|
|
343
|
-
|
|
344
|
-
def location
|
|
345
|
-
raise Error::UnsupportedOperationError, 'The W3C standard does not currently support getting location'
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
def set_location(_lat, _lon, _alt)
|
|
349
|
-
raise Error::UnsupportedOperationError, 'The W3C standard does not currently support setting location'
|
|
350
|
-
end
|
|
351
|
-
|
|
352
|
-
def network_connection
|
|
353
|
-
raise Error::UnsupportedOperationError, 'The W3C standard does not currently support getting network connection'
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
def network_connection=(_type)
|
|
357
|
-
raise Error::UnsupportedOperationError, 'The W3C standard does not currently support setting network connection'
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
#
|
|
361
|
-
# javascript execution
|
|
362
|
-
#
|
|
363
|
-
|
|
364
|
-
def execute_script(script, *args)
|
|
365
|
-
result = execute :executeScript, {}, {script: script, args: args}
|
|
366
|
-
unwrap_script_result result
|
|
367
|
-
end
|
|
368
|
-
|
|
369
|
-
def execute_async_script(script, *args)
|
|
370
|
-
result = execute :executeAsyncScript, {}, {script: script, args: args}
|
|
371
|
-
unwrap_script_result result
|
|
372
|
-
end
|
|
373
|
-
|
|
374
|
-
#
|
|
375
|
-
# cookies
|
|
376
|
-
#
|
|
377
|
-
|
|
378
|
-
def add_cookie(cookie)
|
|
379
|
-
execute :addCookie, {}, {cookie: cookie}
|
|
380
|
-
end
|
|
381
|
-
|
|
382
|
-
def delete_cookie(name)
|
|
383
|
-
execute :deleteCookie, name: name
|
|
384
|
-
end
|
|
385
|
-
|
|
386
|
-
# TODO: - write specs
|
|
387
|
-
def cookie(name)
|
|
388
|
-
execute :getCookie, name: name
|
|
389
|
-
end
|
|
390
|
-
|
|
391
|
-
def cookies
|
|
392
|
-
execute :getAllCookies
|
|
393
|
-
end
|
|
394
|
-
|
|
395
|
-
def delete_all_cookies
|
|
396
|
-
cookies.each { |cookie| delete_cookie(cookie['name']) }
|
|
397
|
-
end
|
|
398
|
-
|
|
399
|
-
#
|
|
400
|
-
# actions
|
|
401
|
-
#
|
|
402
|
-
|
|
403
|
-
def click_element(element)
|
|
404
|
-
execute :elementClick, id: element.values.first
|
|
405
|
-
end
|
|
406
|
-
|
|
407
|
-
def click
|
|
408
|
-
execute :click, {}, {button: 0}
|
|
409
|
-
end
|
|
410
|
-
|
|
411
|
-
def double_click
|
|
412
|
-
execute :doubleClick
|
|
413
|
-
end
|
|
414
|
-
|
|
415
|
-
def context_click
|
|
416
|
-
execute :click, {}, {button: 2}
|
|
417
|
-
end
|
|
418
|
-
|
|
419
|
-
def mouse_down
|
|
420
|
-
execute :mouseDown
|
|
421
|
-
end
|
|
422
|
-
|
|
423
|
-
def mouse_up
|
|
424
|
-
execute :mouseUp
|
|
425
|
-
end
|
|
426
|
-
|
|
427
|
-
def mouse_move_to(element, x = nil, y = nil)
|
|
428
|
-
params = {element: element}
|
|
429
|
-
|
|
430
|
-
if x && y
|
|
431
|
-
params[:xoffset] = x
|
|
432
|
-
params[:yoffset] = y
|
|
433
|
-
end
|
|
434
|
-
|
|
435
|
-
execute :mouseMoveTo, {}, params
|
|
436
|
-
end
|
|
437
|
-
|
|
438
|
-
def send_keys_to_active_element(keys)
|
|
439
|
-
send_keys_to_element(active_element, keys)
|
|
440
|
-
end
|
|
441
|
-
|
|
442
|
-
# TODO: - Implement file verification
|
|
443
|
-
def send_keys_to_element(element, keys)
|
|
444
|
-
execute :elementSendKeys, {id: element.values.first}, {value: keys.join('').split(//)}
|
|
445
|
-
end
|
|
446
|
-
|
|
447
|
-
def clear_element(element)
|
|
448
|
-
execute :elementClear, id: element.values.first
|
|
449
|
-
end
|
|
450
|
-
|
|
451
|
-
def submit_element(element)
|
|
452
|
-
form = find_element_by('xpath', "./ancestor-or-self::form", element)
|
|
453
|
-
execute_script("var e = arguments[0].ownerDocument.createEvent('Event');" \
|
|
454
|
-
"e.initEvent('submit', true, true);" \
|
|
455
|
-
'if (arguments[0].dispatchEvent(e)) { arguments[0].submit() }', form.as_json)
|
|
456
|
-
end
|
|
457
|
-
|
|
458
|
-
def drag_element(element, right_by, down_by)
|
|
459
|
-
execute :dragElement, {id: element.values.first}, {x: right_by, y: down_by}
|
|
460
|
-
end
|
|
461
|
-
|
|
462
|
-
def touch_single_tap(element)
|
|
463
|
-
execute :touchSingleTap, {}, {element: element}
|
|
464
|
-
end
|
|
465
|
-
|
|
466
|
-
def touch_double_tap(element)
|
|
467
|
-
execute :touchDoubleTap, {}, {element: element}
|
|
468
|
-
end
|
|
469
|
-
|
|
470
|
-
def touch_long_press(element)
|
|
471
|
-
execute :touchLongPress, {}, {element: element}
|
|
472
|
-
end
|
|
473
|
-
|
|
474
|
-
def touch_down(x, y)
|
|
475
|
-
execute :touchDown, {}, {x: x, y: y}
|
|
476
|
-
end
|
|
477
|
-
|
|
478
|
-
def touch_up(x, y)
|
|
479
|
-
execute :touchUp, {}, {x: x, y: y}
|
|
480
|
-
end
|
|
481
|
-
|
|
482
|
-
def touch_move(x, y)
|
|
483
|
-
execute :touchMove, {}, {x: x, y: y}
|
|
484
|
-
end
|
|
485
|
-
|
|
486
|
-
def touch_scroll(element, x, y)
|
|
487
|
-
if element
|
|
488
|
-
execute :touchScroll, {}, {element: element,
|
|
489
|
-
xoffset: x,
|
|
490
|
-
yoffset: y}
|
|
491
|
-
else
|
|
492
|
-
execute :touchScroll, {}, {xoffset: x, yoffset: y}
|
|
493
|
-
end
|
|
494
|
-
end
|
|
495
|
-
|
|
496
|
-
def touch_flick(xspeed, yspeed)
|
|
497
|
-
execute :touchFlick, {}, {xspeed: xspeed, yspeed: yspeed}
|
|
498
|
-
end
|
|
499
|
-
|
|
500
|
-
def touch_element_flick(element, right_by, down_by, speed)
|
|
501
|
-
execute :touchFlick, {}, {element: element,
|
|
502
|
-
xoffset: right_by,
|
|
503
|
-
yoffset: down_by,
|
|
504
|
-
speed: speed}
|
|
505
|
-
end
|
|
506
|
-
|
|
507
|
-
def screen_orientation=(orientation)
|
|
508
|
-
execute :setScreenOrientation, {}, {orientation: orientation}
|
|
509
|
-
end
|
|
510
|
-
|
|
511
|
-
def screen_orientation
|
|
512
|
-
execute :getScreenOrientation
|
|
513
|
-
end
|
|
514
|
-
|
|
515
|
-
#
|
|
516
|
-
# element properties
|
|
517
|
-
#
|
|
518
|
-
|
|
519
|
-
def element_tag_name(element)
|
|
520
|
-
execute :getElementTagName, id: element.values.first
|
|
521
|
-
end
|
|
522
|
-
|
|
523
|
-
def element_attribute(element, name)
|
|
524
|
-
execute_atom :getAttribute, element, name
|
|
525
|
-
end
|
|
526
|
-
|
|
527
|
-
def element_property(element, name)
|
|
528
|
-
execute :getElementProperty, id: element.values.first, name: name
|
|
529
|
-
end
|
|
530
|
-
|
|
531
|
-
def element_value(element)
|
|
532
|
-
element_property element, 'value'
|
|
533
|
-
end
|
|
534
|
-
|
|
535
|
-
def element_text(element)
|
|
536
|
-
execute :getElementText, id: element.values.first
|
|
537
|
-
end
|
|
538
|
-
|
|
539
|
-
def element_location(element)
|
|
540
|
-
data = execute :getElementRect, id: element.values.first
|
|
541
|
-
|
|
542
|
-
Point.new data['x'], data['y']
|
|
543
|
-
end
|
|
544
|
-
|
|
545
|
-
def element_location_once_scrolled_into_view(element)
|
|
546
|
-
send_keys_to_element(element, [''])
|
|
547
|
-
element_location(element)
|
|
548
|
-
end
|
|
549
|
-
|
|
550
|
-
def element_size(element)
|
|
551
|
-
data = execute :getElementRect, id: element.values.first
|
|
552
|
-
|
|
553
|
-
Dimension.new data['width'], data['height']
|
|
554
|
-
end
|
|
555
|
-
|
|
556
|
-
def element_enabled?(element)
|
|
557
|
-
execute :isElementEnabled, id: element.values.first
|
|
558
|
-
end
|
|
559
|
-
|
|
560
|
-
def element_selected?(element)
|
|
561
|
-
execute :isElementSelected, id: element.values.first
|
|
562
|
-
end
|
|
563
|
-
|
|
564
|
-
def element_displayed?(element)
|
|
565
|
-
jwp = Selenium::WebDriver::Remote::Bridge::COMMANDS[:isElementDisplayed]
|
|
566
|
-
self.class.command(:isElementDisplayed, jwp.first, jwp.last)
|
|
567
|
-
execute :isElementDisplayed, id: element.values.first
|
|
568
|
-
end
|
|
569
|
-
|
|
570
|
-
def element_value_of_css_property(element, prop)
|
|
571
|
-
execute :getElementCssValue, id: element.values.first, property_name: prop
|
|
572
|
-
end
|
|
573
|
-
|
|
574
|
-
#
|
|
575
|
-
# finding elements
|
|
576
|
-
#
|
|
577
|
-
|
|
578
|
-
def active_element
|
|
579
|
-
Element.new self, execute(:getActiveElement)
|
|
580
|
-
end
|
|
581
|
-
|
|
582
|
-
alias_method :switch_to_active_element, :active_element
|
|
583
|
-
|
|
584
|
-
def find_element_by(how, what, parent = nil)
|
|
585
|
-
how, what = convert_locators(how, what)
|
|
586
|
-
|
|
587
|
-
id = if parent
|
|
588
|
-
execute :findChildElement, {id: parent.values.first}, {using: how, value: what}
|
|
589
|
-
else
|
|
590
|
-
execute :findElement, {}, {using: how, value: what}
|
|
591
|
-
end
|
|
592
|
-
Element.new self, id
|
|
593
|
-
end
|
|
594
|
-
|
|
595
|
-
def find_elements_by(how, what, parent = nil)
|
|
596
|
-
how, what = convert_locators(how, what)
|
|
597
|
-
|
|
598
|
-
ids = if parent
|
|
599
|
-
execute :findChildElements, {id: parent.values.first}, {using: how, value: what}
|
|
600
|
-
else
|
|
601
|
-
execute :findElements, {}, {using: how, value: what}
|
|
602
|
-
end
|
|
603
|
-
|
|
604
|
-
ids.map { |id| Element.new self, id }
|
|
605
|
-
end
|
|
606
|
-
|
|
607
|
-
private
|
|
608
|
-
|
|
609
|
-
def convert_locators(how, what)
|
|
610
|
-
case how
|
|
611
|
-
when 'class name'
|
|
612
|
-
how = 'css selector'
|
|
613
|
-
what = ".#{escape_css(what)}"
|
|
614
|
-
when 'id'
|
|
615
|
-
how = 'css selector'
|
|
616
|
-
what = "##{escape_css(what)}"
|
|
617
|
-
when 'name'
|
|
618
|
-
how = 'css selector'
|
|
619
|
-
what = "*[name='#{escape_css(what)}']"
|
|
620
|
-
when 'tag name'
|
|
621
|
-
how = 'css selector'
|
|
622
|
-
end
|
|
623
|
-
[how, what]
|
|
624
|
-
end
|
|
625
|
-
|
|
626
|
-
#
|
|
627
|
-
# executes a command on the remote server.
|
|
628
|
-
#
|
|
629
|
-
#
|
|
630
|
-
# Returns the 'value' of the returned payload
|
|
631
|
-
#
|
|
632
|
-
|
|
633
|
-
def execute(*args)
|
|
634
|
-
result = raw_execute(*args)
|
|
635
|
-
result.payload.key?('value') ? result['value'] : result
|
|
636
|
-
end
|
|
637
|
-
|
|
638
|
-
#
|
|
639
|
-
# executes a command on the remote server.
|
|
640
|
-
#
|
|
641
|
-
# @return [WebDriver::Remote::Response]
|
|
642
|
-
#
|
|
643
|
-
|
|
644
|
-
def raw_execute(command, opts = {}, command_hash = nil)
|
|
645
|
-
verb, path = COMMANDS[command] || raise(ArgumentError, "unknown command: #{command.inspect}")
|
|
646
|
-
path = path.dup
|
|
647
|
-
|
|
648
|
-
path[':session_id'] = @session_id if path.include?(':session_id')
|
|
649
|
-
|
|
650
|
-
begin
|
|
651
|
-
opts.each do |key, value|
|
|
652
|
-
path[key.inspect] = escaper.escape(value.to_s)
|
|
653
|
-
end
|
|
654
|
-
rescue IndexError
|
|
655
|
-
raise ArgumentError, "#{opts.inspect} invalid for #{command.inspect}"
|
|
656
|
-
end
|
|
657
|
-
|
|
658
|
-
puts "-> #{verb.to_s.upcase} #{path}" if $DEBUG
|
|
659
|
-
http.call verb, path, command_hash
|
|
660
|
-
end
|
|
661
|
-
|
|
662
|
-
def escaper
|
|
663
|
-
@escaper ||= defined?(URI::Parser) ? URI::Parser.new : URI
|
|
664
|
-
end
|
|
665
|
-
|
|
666
|
-
ESCAPE_CSS_REGEXP = /(['"\\#.:;,!?+<>=~*^$|%&@`{}\-\[\]\(\)])/
|
|
667
|
-
UNICODE_CODE_POINT = 30
|
|
668
|
-
|
|
669
|
-
# Escapes invalid characters in CSS selector.
|
|
670
|
-
# @see https://mathiasbynens.be/notes/css-escapes
|
|
671
|
-
def escape_css(string)
|
|
672
|
-
string = string.gsub(ESCAPE_CSS_REGEXP) { |match| "\\#{match}" }
|
|
673
|
-
if !string.empty? && string[0] =~ /[[:digit:]]/
|
|
674
|
-
string = "\\#{UNICODE_CODE_POINT + Integer(string[0])} #{string[1..-1]}"
|
|
675
|
-
end
|
|
676
|
-
|
|
677
|
-
string
|
|
678
|
-
end
|
|
679
|
-
end # W3CBridge
|
|
680
|
-
end # Remote
|
|
681
|
-
end # WebDriver
|
|
682
|
-
end # Selenium
|