selenium-webdriver 3.142.7 → 4.16.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.
- checksums.yaml +5 -5
- data/CHANGES +685 -5
- data/Gemfile +6 -1
- data/LICENSE +1 -1
- data/NOTICE +2 -0
- data/README.md +4 -5
- data/bin/linux/selenium-manager +0 -0
- data/bin/macos/selenium-manager +0 -0
- data/bin/windows/selenium-manager.exe +0 -0
- data/lib/selenium/server.rb +93 -90
- data/lib/selenium/webdriver/atoms/findElements.js +122 -0
- data/lib/selenium/webdriver/atoms/getAttribute.js +100 -7
- data/lib/selenium/webdriver/atoms/isDisplayed.js +77 -78
- data/lib/selenium/webdriver/atoms/mutationListener.js +55 -0
- data/lib/selenium/webdriver/atoms.rb +5 -3
- data/lib/selenium/webdriver/bidi/browsing_context.rb +88 -0
- data/lib/selenium/webdriver/bidi/browsing_context_info.rb +35 -0
- data/lib/selenium/webdriver/bidi/log/base_log_entry.rb +35 -0
- data/lib/selenium/webdriver/bidi/log/console_log_entry.rb +35 -0
- data/lib/selenium/webdriver/bidi/log/filter_by.rb +40 -0
- data/lib/selenium/webdriver/bidi/log/generic_log_entry.rb +33 -0
- data/lib/selenium/webdriver/bidi/log/javascript_log_entry.rb +33 -0
- data/lib/selenium/webdriver/bidi/log_inspector.rb +143 -0
- data/lib/selenium/webdriver/bidi/navigate_result.rb +33 -0
- data/lib/selenium/webdriver/bidi/session.rb +51 -0
- data/lib/selenium/webdriver/{common/keyboard.rb → bidi.rb} +21 -35
- data/lib/selenium/webdriver/chrome/driver.rb +9 -86
- data/lib/selenium/webdriver/chrome/features.rb +48 -0
- data/lib/selenium/webdriver/chrome/options.rb +9 -158
- data/lib/selenium/webdriver/chrome/profile.rb +3 -80
- data/lib/selenium/webdriver/chrome/service.rb +7 -29
- data/lib/selenium/webdriver/chrome.rb +5 -20
- data/lib/selenium/webdriver/chromium/driver.rb +60 -0
- data/lib/selenium/webdriver/{chrome/bridge.rb → chromium/features.rb} +48 -17
- data/lib/selenium/webdriver/chromium/options.rb +243 -0
- data/lib/selenium/webdriver/chromium/profile.rb +113 -0
- data/lib/selenium/webdriver/chromium.rb +29 -0
- data/lib/selenium/webdriver/common/action_builder.rb +126 -238
- data/lib/selenium/webdriver/common/child_process.rb +124 -0
- data/lib/selenium/webdriver/common/driver.rb +82 -43
- data/lib/selenium/webdriver/common/driver_extensions/downloads_files.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/{has_location.rb → full_page_screenshot.rb} +13 -11
- data/lib/selenium/webdriver/common/driver_extensions/has_addons.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_apple_permissions.rb +49 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +87 -0
- data/lib/selenium/webdriver/common/driver_extensions/{has_touch_screen.rb → has_bidi.rb} +9 -9
- data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +86 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_cdp.rb +36 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +42 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_debugger.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +41 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_file_downloads.rb +65 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +36 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +143 -0
- data/lib/selenium/webdriver/common/driver_extensions/{has_remote_status.rb → has_logs.rb} +4 -4
- data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +16 -1
- data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +69 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +11 -13
- data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +75 -0
- data/lib/selenium/webdriver/common/driver_extensions/{rotatable.rb → prints_page.rb} +18 -20
- data/lib/selenium/webdriver/common/driver_finder.rb +45 -0
- data/lib/selenium/webdriver/common/element.rb +89 -29
- data/lib/selenium/webdriver/common/error.rb +53 -194
- data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +2 -2
- data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
- data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -22
- data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
- data/lib/selenium/webdriver/common/interactions/key_actions.rb +10 -6
- data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
- data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
- data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
- data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +71 -82
- data/lib/selenium/webdriver/common/interactions/pointer_cancel.rb +45 -0
- data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
- data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
- data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
- data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
- data/lib/selenium/webdriver/common/interactions/scroll.rb +59 -0
- data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
- data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
- data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +113 -0
- data/lib/selenium/webdriver/common/{w3c_manager.rb → interactions/wheel_input.rb} +14 -17
- data/lib/selenium/webdriver/common/keys.rb +1 -0
- data/lib/selenium/webdriver/common/local_driver.rb +46 -0
- data/lib/selenium/webdriver/common/log_entry.rb +2 -2
- data/lib/selenium/webdriver/common/logger.rb +119 -19
- data/lib/selenium/webdriver/common/manager.rb +11 -38
- data/lib/selenium/webdriver/common/options.rb +154 -23
- data/lib/selenium/webdriver/common/platform.rb +15 -52
- data/lib/selenium/webdriver/common/port_prober.rb +4 -6
- data/lib/selenium/webdriver/common/profile_helper.rb +11 -9
- data/lib/selenium/webdriver/common/proxy.rb +8 -5
- data/lib/selenium/webdriver/common/search_context.rb +7 -9
- data/lib/selenium/webdriver/common/selenium_manager.rb +140 -0
- data/lib/selenium/webdriver/common/service.rb +26 -143
- data/lib/selenium/webdriver/common/service_manager.rb +144 -0
- data/lib/selenium/webdriver/common/shadow_root.rb +86 -0
- data/lib/selenium/webdriver/common/socket_lock.rb +4 -4
- data/lib/selenium/webdriver/common/socket_poller.rb +4 -4
- data/lib/selenium/webdriver/common/takes_screenshot.rb +65 -0
- data/lib/selenium/webdriver/common/target_locator.rb +31 -4
- data/lib/selenium/webdriver/common/timeouts.rb +31 -4
- data/lib/selenium/webdriver/common/virtual_authenticator/credential.rb +85 -0
- data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator.rb +72 -0
- data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options.rb +62 -0
- data/lib/selenium/webdriver/common/wait.rb +1 -1
- data/lib/selenium/webdriver/common/websocket_connection.rb +164 -0
- data/lib/selenium/webdriver/common/window.rb +6 -10
- data/lib/selenium/webdriver/common/zipper.rb +4 -10
- data/lib/selenium/webdriver/common.rb +43 -20
- data/lib/selenium/webdriver/devtools/console_event.rb +36 -0
- data/lib/selenium/webdriver/devtools/exception_event.rb +34 -0
- data/lib/selenium/webdriver/devtools/mutation_event.rb +35 -0
- data/lib/selenium/webdriver/devtools/network_interceptor.rb +173 -0
- data/lib/selenium/webdriver/devtools/pinned_script.rb +57 -0
- data/lib/selenium/webdriver/devtools/request.rb +65 -0
- data/lib/selenium/webdriver/devtools/response.rb +64 -0
- data/lib/selenium/webdriver/devtools.rb +96 -0
- data/lib/selenium/webdriver/edge/driver.rb +11 -27
- data/lib/selenium/webdriver/edge/features.rb +48 -0
- data/lib/selenium/webdriver/edge/options.rb +18 -43
- data/lib/selenium/webdriver/edge/profile.rb +33 -0
- data/lib/selenium/webdriver/edge/service.rb +8 -23
- data/lib/selenium/webdriver/edge.rb +11 -16
- data/lib/selenium/webdriver/firefox/driver.rb +38 -19
- data/lib/selenium/webdriver/firefox/extension.rb +8 -0
- data/lib/selenium/webdriver/firefox/features.rb +70 -0
- data/lib/selenium/webdriver/firefox/options.rb +73 -61
- data/lib/selenium/webdriver/firefox/profile.rb +20 -72
- data/lib/selenium/webdriver/firefox/service.rb +3 -25
- data/lib/selenium/webdriver/firefox/util.rb +1 -1
- data/lib/selenium/webdriver/firefox.rb +17 -28
- data/lib/selenium/webdriver/ie/driver.rb +5 -45
- data/lib/selenium/webdriver/ie/features.rb +34 -0
- data/lib/selenium/webdriver/ie/options.rb +14 -44
- data/lib/selenium/webdriver/ie/service.rb +3 -27
- data/lib/selenium/webdriver/ie.rb +4 -16
- data/lib/selenium/webdriver/remote/bridge/commands.rb +164 -0
- data/lib/selenium/webdriver/remote/bridge.rb +574 -88
- data/lib/selenium/webdriver/remote/capabilities.rb +144 -158
- data/lib/selenium/webdriver/remote/driver.rb +45 -14
- data/lib/selenium/webdriver/remote/features.rb +75 -0
- data/lib/selenium/webdriver/remote/http/common.rb +3 -8
- data/lib/selenium/webdriver/remote/http/curb.rb +1 -3
- data/lib/selenium/webdriver/remote/http/default.rb +24 -33
- data/lib/selenium/webdriver/remote/response.rb +17 -49
- data/lib/selenium/webdriver/remote/server_error.rb +1 -1
- data/lib/selenium/webdriver/remote.rb +15 -12
- data/lib/selenium/webdriver/safari/driver.rb +7 -29
- data/lib/selenium/webdriver/safari/{bridge.rb → features.rb} +7 -5
- data/lib/selenium/webdriver/safari/options.rb +12 -27
- data/lib/selenium/webdriver/safari/service.rb +13 -11
- data/lib/selenium/webdriver/safari.rb +14 -20
- data/lib/selenium/webdriver/support/block_event_listener.rb +1 -1
- data/lib/selenium/webdriver/support/color.rb +24 -24
- data/lib/selenium/webdriver/support/event_firing_bridge.rb +5 -5
- data/lib/selenium/webdriver/support/guards/guard.rb +90 -0
- data/lib/selenium/webdriver/{firefox/marionette/bridge.rb → support/guards/guard_condition.rb} +21 -20
- data/lib/selenium/webdriver/support/guards.rb +95 -0
- data/lib/selenium/webdriver/support/relative_locator.rb +50 -0
- data/lib/selenium/webdriver/support/select.rb +6 -4
- data/lib/selenium/webdriver/support.rb +1 -0
- data/lib/selenium/webdriver/version.rb +1 -1
- data/lib/selenium/webdriver.rb +19 -17
- data/selenium-webdriver.gemspec +36 -18
- metadata +161 -91
- data/lib/selenium/webdriver/common/bridge_helper.rb +0 -82
- data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +0 -58
- data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +0 -64
- data/lib/selenium/webdriver/common/mouse.rb +0 -89
- data/lib/selenium/webdriver/common/touch_action_builder.rb +0 -78
- data/lib/selenium/webdriver/common/touch_screen.rb +0 -123
- data/lib/selenium/webdriver/common/w3c_action_builder.rb +0 -212
- data/lib/selenium/webdriver/edge/bridge.rb +0 -76
- data/lib/selenium/webdriver/firefox/binary.rb +0 -187
- 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 -111
- data/lib/selenium/webdriver/firefox/legacy/driver.rb +0 -83
- data/lib/selenium/webdriver/firefox/marionette/driver.rb +0 -90
- 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/remote/http/persistent.rb +0 -60
- data/lib/selenium/webdriver/remote/oss/bridge.rb +0 -594
- data/lib/selenium/webdriver/remote/oss/commands.rb +0 -223
- data/lib/selenium/webdriver/remote/w3c/bridge.rb +0 -605
- data/lib/selenium/webdriver/remote/w3c/capabilities.rb +0 -310
- data/lib/selenium/webdriver/remote/w3c/commands.rb +0 -157
|
@@ -19,346 +19,234 @@
|
|
|
19
19
|
|
|
20
20
|
module Selenium
|
|
21
21
|
module WebDriver
|
|
22
|
-
#
|
|
23
|
-
# The ActionBuilder provides the user a way to set up and perform
|
|
24
|
-
# complex user interactions.
|
|
25
|
-
#
|
|
26
|
-
# This class should not be instantiated directly, but is created by Driver#action
|
|
27
|
-
#
|
|
28
|
-
# @example
|
|
29
|
-
#
|
|
30
|
-
# driver.action.key_down(:shift).
|
|
31
|
-
# click(element).
|
|
32
|
-
# click(second_element).
|
|
33
|
-
# key_up(:shift).
|
|
34
|
-
# drag_and_drop(element, third_element).
|
|
35
|
-
# perform
|
|
36
|
-
#
|
|
37
|
-
|
|
38
22
|
class ActionBuilder
|
|
23
|
+
include KeyActions # Actions specific to key inputs
|
|
24
|
+
include PointerActions # Actions specific to pointer inputs
|
|
25
|
+
include WheelActions # Actions specific to wheel inputs
|
|
26
|
+
|
|
27
|
+
attr_reader :devices
|
|
28
|
+
|
|
39
29
|
#
|
|
40
|
-
#
|
|
30
|
+
# Initialize a W3C Action Builder. Differs from previous by requiring a bridge and allowing asynchronous actions.
|
|
31
|
+
# The W3C implementation allows asynchronous actions per device. e.g. A key can be pressed at the same time that
|
|
32
|
+
# the mouse is moving. Keep in mind that pauses must be added for other devices in order to line up the actions
|
|
33
|
+
# correctly when using asynchronous.
|
|
34
|
+
#
|
|
35
|
+
# @param [Selenium::WebDriver::Remote::Bridge] bridge the bridge for the current driver instance.
|
|
36
|
+
# @param [Boolean] deprecated_async Whether to perform the actions asynchronously per device.
|
|
37
|
+
# Defaults to false for backwards compatibility.
|
|
38
|
+
# @param [Array<Selenium::WebDriver::Interactions::InputDevices>] devices list of valid sources of input.
|
|
39
|
+
# @param [Boolean] async Whether to perform the actions asynchronously per device.
|
|
40
|
+
# @return [ActionBuilder] A self reference.
|
|
41
41
|
#
|
|
42
42
|
|
|
43
|
-
def initialize(
|
|
44
|
-
@
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
def initialize(bridge, devices: [], async: false, duration: 250)
|
|
44
|
+
@bridge = bridge
|
|
45
|
+
@duration = duration
|
|
46
|
+
@async = async
|
|
47
|
+
@devices = []
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
Array(devices).each { |device| add_input(device) }
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
#
|
|
53
|
-
#
|
|
54
|
-
# the modifier key - subsequent interactions may assume it's kept pressed.
|
|
55
|
-
# Note that the modifier key is never released implicitly - either
|
|
56
|
-
# #key_up(key) or #send_keys(:null) must be called to release the modifier.
|
|
53
|
+
# Adds a PointerInput device of the given kind
|
|
57
54
|
#
|
|
58
|
-
#
|
|
59
|
-
# driver.action.click(element).send_keys(key)
|
|
60
|
-
# # or
|
|
61
|
-
# driver.action.click.send_keys(key)
|
|
55
|
+
# @example Add a touch pointer input device
|
|
62
56
|
#
|
|
63
|
-
#
|
|
57
|
+
# builder = device.action
|
|
58
|
+
# builder.add_pointer_input('touch', :touch)
|
|
64
59
|
#
|
|
65
|
-
#
|
|
60
|
+
# @param [String] name name for the device
|
|
61
|
+
# @param [Symbol] kind kind of pointer device to create
|
|
62
|
+
# @return [Interactions::PointerInput] The pointer input added
|
|
66
63
|
#
|
|
67
|
-
# @example Press a key on an element
|
|
68
64
|
#
|
|
69
|
-
# el = driver.find_element(id: "some_id")
|
|
70
|
-
# driver.action.key_down(el, :shift).perform
|
|
71
|
-
#
|
|
72
|
-
# @overload key_down(key)
|
|
73
|
-
# @param [:shift, :alt, :control, :command, :meta] key The modifier key to press
|
|
74
|
-
# @overload key_down(element, key)
|
|
75
|
-
# @param [Element] element An optional element to move to first
|
|
76
|
-
# @param [:shift, :alt, :control, :command, :meta] key The modifier key to press
|
|
77
|
-
# @raise [ArgumentError] if the given key is not a modifier
|
|
78
|
-
# @return [ActionBuilder] A self reference
|
|
79
|
-
|
|
80
|
-
def key_down(*args)
|
|
81
|
-
@actions << [:mouse, :click, [args.shift]] if args.first.is_a? Element
|
|
82
65
|
|
|
83
|
-
|
|
84
|
-
|
|
66
|
+
def add_pointer_input(kind, name)
|
|
67
|
+
add_input(Interactions.pointer(kind, name: name))
|
|
85
68
|
end
|
|
86
69
|
|
|
87
70
|
#
|
|
88
|
-
#
|
|
89
|
-
# Releasing a non-depressed modifier key will yield undefined behaviour.
|
|
71
|
+
# Adds a KeyInput device
|
|
90
72
|
#
|
|
91
|
-
# @example
|
|
73
|
+
# @example Add a key input device
|
|
92
74
|
#
|
|
93
|
-
#
|
|
75
|
+
# builder = device.action
|
|
76
|
+
# builder.add_key_input('keyboard2')
|
|
94
77
|
#
|
|
95
|
-
# @
|
|
96
|
-
#
|
|
97
|
-
# el = driver.find_element(id: "some_id")
|
|
98
|
-
# driver.action.key_up(el, :alt).perform
|
|
78
|
+
# @param [String] name name for the device
|
|
79
|
+
# @return [Interactions::KeyInput] The key input added
|
|
99
80
|
#
|
|
100
|
-
# @overload key_up(key)
|
|
101
|
-
# @param [:shift, :alt, :control, :command, :meta] key The modifier key to release
|
|
102
|
-
# @overload key_up(element, key)
|
|
103
|
-
# @param [Element] element An optional element to move to first
|
|
104
|
-
# @param [:shift, :alt, :control, :command, :meta] key The modifier key to release
|
|
105
|
-
# @raise [ArgumentError] if the given key is not a modifier
|
|
106
|
-
# @return [ActionBuilder] A self reference
|
|
107
|
-
#
|
|
108
|
-
|
|
109
|
-
def key_up(*args)
|
|
110
|
-
@actions << [:mouse, :click, [args.shift]] if args.first.is_a? Element
|
|
111
81
|
|
|
112
|
-
|
|
113
|
-
|
|
82
|
+
def add_key_input(name)
|
|
83
|
+
add_input(Interactions.key(name))
|
|
114
84
|
end
|
|
115
85
|
|
|
116
86
|
#
|
|
117
|
-
#
|
|
118
|
-
# Element#send_keys(keys) on the active element in two ways:
|
|
87
|
+
# Adds a WheelInput device
|
|
119
88
|
#
|
|
120
|
-
#
|
|
121
|
-
# * There is no attempt to re-focus the element - so send_keys(:tab) for switching elements should work.
|
|
89
|
+
# @example Add a wheel input device
|
|
122
90
|
#
|
|
123
|
-
#
|
|
91
|
+
# builder = device.action
|
|
92
|
+
# builder.add_wheel_input('wheel2')
|
|
124
93
|
#
|
|
125
|
-
#
|
|
126
|
-
#
|
|
94
|
+
# @param [String] name name for the device
|
|
95
|
+
# @return [Interactions::WheelInput] The wheel input added
|
|
127
96
|
#
|
|
128
|
-
|
|
97
|
+
|
|
98
|
+
def add_wheel_input(name)
|
|
99
|
+
add_input(Interactions.wheel(name))
|
|
100
|
+
end
|
|
101
|
+
|
|
129
102
|
#
|
|
130
|
-
#
|
|
103
|
+
# Retrieves the input device for the given name or type
|
|
131
104
|
#
|
|
132
|
-
# @
|
|
133
|
-
#
|
|
134
|
-
# @
|
|
135
|
-
# @param [Element] element An optional element to move to first
|
|
136
|
-
# @param [Array, Symbol, String] keys The key(s) to press and release
|
|
137
|
-
# @return [ActionBuilder] A self reference
|
|
105
|
+
# @param [String] name name of the input device
|
|
106
|
+
# @param [String] type name of the input device
|
|
107
|
+
# @return [Selenium::WebDriver::Interactions::InputDevice] input device with given name or type
|
|
138
108
|
#
|
|
139
109
|
|
|
140
|
-
def
|
|
141
|
-
@
|
|
110
|
+
def device(name: nil, type: nil)
|
|
111
|
+
input = @devices.find { |device| (device.name == name.to_s || name.nil?) && (device.type == type || type.nil?) }
|
|
142
112
|
|
|
143
|
-
|
|
144
|
-
|
|
113
|
+
raise(ArgumentError, "Can not find device: #{name}") if name && input.nil?
|
|
114
|
+
|
|
115
|
+
input
|
|
145
116
|
end
|
|
146
117
|
|
|
147
118
|
#
|
|
148
|
-
#
|
|
149
|
-
# equivalent to:
|
|
119
|
+
# Retrieves the current PointerInput devices
|
|
150
120
|
#
|
|
151
|
-
#
|
|
152
|
-
#
|
|
153
|
-
# @example Clicking and holding on some element
|
|
154
|
-
#
|
|
155
|
-
# el = driver.find_element(id: "some_id")
|
|
156
|
-
# driver.action.click_and_hold(el).perform
|
|
157
|
-
#
|
|
158
|
-
# @param [Element] element the element to move to and click.
|
|
159
|
-
# @return [ActionBuilder] A self reference.
|
|
121
|
+
# @return [Array] array of current PointerInput devices
|
|
160
122
|
#
|
|
161
123
|
|
|
162
|
-
def
|
|
163
|
-
@
|
|
164
|
-
self
|
|
124
|
+
def pointer_inputs
|
|
125
|
+
@devices.select { |device| device.type == Interactions::POINTER }
|
|
165
126
|
end
|
|
166
127
|
|
|
167
128
|
#
|
|
168
|
-
#
|
|
129
|
+
# Retrieves the current KeyInput device
|
|
169
130
|
#
|
|
170
|
-
# @
|
|
171
|
-
#
|
|
172
|
-
# el = driver.find_element(id: "some_id")
|
|
173
|
-
# driver.action.click_and_hold(el).release.perform
|
|
174
|
-
#
|
|
175
|
-
# @return [ActionBuilder] A self reference.
|
|
131
|
+
# @return [Selenium::WebDriver::Interactions::InputDevice] current KeyInput device
|
|
176
132
|
#
|
|
177
133
|
|
|
178
|
-
def
|
|
179
|
-
@
|
|
180
|
-
self
|
|
134
|
+
def key_inputs
|
|
135
|
+
@devices.select { |device| device.type == Interactions::KEY }
|
|
181
136
|
end
|
|
182
137
|
|
|
183
138
|
#
|
|
184
|
-
#
|
|
139
|
+
# Retrieves the current WheelInput device
|
|
185
140
|
#
|
|
186
|
-
#
|
|
187
|
-
#
|
|
188
|
-
# When no element is passed, the current mouse position will be clicked.
|
|
189
|
-
#
|
|
190
|
-
# @example Clicking on an element
|
|
191
|
-
#
|
|
192
|
-
# el = driver.find_element(id: "some_id")
|
|
193
|
-
# driver.action.click(el).perform
|
|
194
|
-
#
|
|
195
|
-
# @example Clicking at the current mouse position
|
|
196
|
-
#
|
|
197
|
-
# driver.action.click.perform
|
|
198
|
-
#
|
|
199
|
-
# @param [Selenium::WebDriver::Element] element An optional element to click.
|
|
200
|
-
# @return [ActionBuilder] A self reference.
|
|
141
|
+
# @return [Selenium::WebDriver::Interactions::InputDevice] current WheelInput devices
|
|
201
142
|
#
|
|
202
143
|
|
|
203
|
-
def
|
|
204
|
-
@
|
|
205
|
-
self
|
|
144
|
+
def wheel_inputs
|
|
145
|
+
@devices.select { |device| device.type == Interactions::WHEEL }
|
|
206
146
|
end
|
|
207
147
|
|
|
208
148
|
#
|
|
209
|
-
#
|
|
210
|
-
#
|
|
211
|
-
# driver.action.move_to(element).double_click
|
|
149
|
+
# Creates a pause for the given device of the given duration. If no duration is given, the pause will only wait
|
|
150
|
+
# for all actions to complete in that tick.
|
|
212
151
|
#
|
|
213
|
-
# @example
|
|
152
|
+
# @example Send keys to an element
|
|
214
153
|
#
|
|
215
|
-
#
|
|
216
|
-
#
|
|
154
|
+
# action_builder = driver.action
|
|
155
|
+
# keyboard = action_builder.key_input
|
|
156
|
+
# el = driver.find_element(id: "some_id")
|
|
157
|
+
# driver.action.click(el).pause(keyboard).pause(keyboard).pause(keyboard).send_keys('keys').perform
|
|
217
158
|
#
|
|
218
|
-
# @param [
|
|
159
|
+
# @param [InputDevice] device Input device to pause
|
|
160
|
+
# @param [Float] duration Duration to pause
|
|
219
161
|
# @return [ActionBuilder] A self reference.
|
|
220
162
|
#
|
|
221
163
|
|
|
222
|
-
def
|
|
223
|
-
|
|
164
|
+
def pause(device: nil, duration: 0)
|
|
165
|
+
device ||= pointer_input
|
|
166
|
+
device.create_pause(duration)
|
|
224
167
|
self
|
|
225
168
|
end
|
|
226
169
|
|
|
227
170
|
#
|
|
228
|
-
#
|
|
229
|
-
# view and its location is calculated using getBoundingClientRect. Then the
|
|
230
|
-
# mouse is moved to optional offset coordinates from the element.
|
|
231
|
-
#
|
|
232
|
-
# Note that when using offsets, both coordinates need to be passed.
|
|
233
|
-
#
|
|
234
|
-
# @example Scroll element into view and move the mouse to it
|
|
235
|
-
#
|
|
236
|
-
# el = driver.find_element(id: "some_id")
|
|
237
|
-
# driver.action.move_to(el).perform
|
|
171
|
+
# Creates multiple pauses for the given device of the given duration.
|
|
238
172
|
#
|
|
239
|
-
# @example
|
|
173
|
+
# @example Send keys to an element
|
|
240
174
|
#
|
|
175
|
+
# action_builder = driver.action
|
|
176
|
+
# keyboard = action_builder.key_input
|
|
241
177
|
# el = driver.find_element(id: "some_id")
|
|
242
|
-
# driver.action.
|
|
178
|
+
# driver.action.click(el).pauses(keyboard, 3).send_keys('keys').perform
|
|
243
179
|
#
|
|
244
|
-
# @param [
|
|
245
|
-
# @param [Integer]
|
|
246
|
-
#
|
|
247
|
-
# @param [Integer] down_by Optional offset from the top-left corner. A negative value means
|
|
248
|
-
# coordinates above the element.
|
|
180
|
+
# @param [InputDevice] device Input device to pause
|
|
181
|
+
# @param [Integer] number of pauses to add for the device
|
|
182
|
+
# @param [Float] duration Duration to pause
|
|
249
183
|
# @return [ActionBuilder] A self reference.
|
|
250
184
|
#
|
|
251
185
|
|
|
252
|
-
def
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
[:mouse, :move_to, [element]]
|
|
257
|
-
end
|
|
186
|
+
def pauses(device: nil, number: nil, duration: 0)
|
|
187
|
+
number ||= 2
|
|
188
|
+
device ||= pointer_input
|
|
189
|
+
duration ||= 0
|
|
258
190
|
|
|
191
|
+
number.times { device.create_pause(duration) }
|
|
259
192
|
self
|
|
260
193
|
end
|
|
261
194
|
|
|
262
195
|
#
|
|
263
|
-
#
|
|
264
|
-
# If the coordinates provided are outside the viewport (the mouse will
|
|
265
|
-
# end up outside the browser window) then the viewport is scrolled to
|
|
266
|
-
# match.
|
|
267
|
-
#
|
|
268
|
-
# @example Move the mouse to a certain offset from its current position
|
|
269
|
-
#
|
|
270
|
-
# driver.action.move_by(100, 100).perform
|
|
271
|
-
#
|
|
272
|
-
# @param [Integer] right_by horizontal offset. A negative value means moving the
|
|
273
|
-
# mouse left.
|
|
274
|
-
# @param [Integer] down_by vertical offset. A negative value means moving the mouse
|
|
275
|
-
# up.
|
|
276
|
-
# @return [ActionBuilder] A self reference.
|
|
277
|
-
# @raise [MoveTargetOutOfBoundsError] if the provided offset is outside
|
|
278
|
-
# the document's boundaries.
|
|
196
|
+
# Executes the actions added to the builder.
|
|
279
197
|
#
|
|
280
198
|
|
|
281
|
-
def
|
|
282
|
-
@
|
|
283
|
-
|
|
199
|
+
def perform
|
|
200
|
+
@bridge.send_actions @devices.filter_map(&:encode)
|
|
201
|
+
clear_all_actions
|
|
202
|
+
nil
|
|
284
203
|
end
|
|
285
204
|
|
|
286
205
|
#
|
|
287
|
-
#
|
|
288
|
-
# a move_to to the location of the element.
|
|
289
|
-
#
|
|
290
|
-
# @example Context-click at middle of given element
|
|
291
|
-
#
|
|
292
|
-
# el = driver.find_element(id: "some_id")
|
|
293
|
-
# driver.action.context_click(el).perform
|
|
294
|
-
#
|
|
295
|
-
# @param [Selenium::WebDriver::Element] element An element to context click.
|
|
296
|
-
# @return [ActionBuilder] A self reference.
|
|
206
|
+
# Clears all actions from the builder.
|
|
297
207
|
#
|
|
298
208
|
|
|
299
|
-
def
|
|
300
|
-
@
|
|
301
|
-
self
|
|
209
|
+
def clear_all_actions
|
|
210
|
+
@devices.each(&:clear_actions)
|
|
302
211
|
end
|
|
303
212
|
|
|
304
213
|
#
|
|
305
|
-
#
|
|
306
|
-
# source element, moves to the location of the target element, then
|
|
307
|
-
# releases the mouse.
|
|
308
|
-
#
|
|
309
|
-
# @example Drag and drop one element onto another
|
|
310
|
-
#
|
|
311
|
-
# el1 = driver.find_element(id: "some_id1")
|
|
312
|
-
# el2 = driver.find_element(id: "some_id2")
|
|
313
|
-
# driver.action.drag_and_drop(el1, el2).perform
|
|
314
|
-
#
|
|
315
|
-
# @param [Selenium::WebDriver::Element] source element to emulate button down at.
|
|
316
|
-
# @param [Selenium::WebDriver::Element] target element to move to and release the
|
|
317
|
-
# mouse at.
|
|
318
|
-
# @return [ActionBuilder] A self reference.
|
|
214
|
+
# Releases all action states from the browser.
|
|
319
215
|
#
|
|
320
216
|
|
|
321
|
-
def
|
|
322
|
-
|
|
323
|
-
move_to target
|
|
324
|
-
release
|
|
325
|
-
|
|
326
|
-
self
|
|
217
|
+
def release_actions
|
|
218
|
+
@bridge.release_actions
|
|
327
219
|
end
|
|
328
220
|
|
|
221
|
+
private
|
|
222
|
+
|
|
329
223
|
#
|
|
330
|
-
#
|
|
331
|
-
# the source element, moves by a given offset, then releases the mouse.
|
|
332
|
-
#
|
|
333
|
-
# @example Drag and drop an element by offset
|
|
334
|
-
#
|
|
335
|
-
# el = driver.find_element(id: "some_id1")
|
|
336
|
-
# driver.action.drag_and_drop_by(el, 100, 100).perform
|
|
224
|
+
# Adds pauses for all devices but the given devices
|
|
337
225
|
#
|
|
338
|
-
# @param [
|
|
339
|
-
# @param [Integer] right_by horizontal move offset.
|
|
340
|
-
# @param [Integer] down_by vertical move offset.
|
|
341
|
-
# @return [ActionBuilder] A self reference.
|
|
226
|
+
# @param [Array[InputDevice]] action_devices Array of Input Devices performing an action in this tick.
|
|
342
227
|
#
|
|
343
228
|
|
|
344
|
-
def
|
|
345
|
-
|
|
346
|
-
move_by right_by, down_by
|
|
347
|
-
release
|
|
229
|
+
def tick(*action_devices)
|
|
230
|
+
return if @async
|
|
348
231
|
|
|
349
|
-
|
|
232
|
+
@devices.each { |device| device.create_pause unless action_devices.include? device }
|
|
350
233
|
end
|
|
351
234
|
|
|
352
235
|
#
|
|
353
|
-
#
|
|
236
|
+
# Adds an InputDevice
|
|
354
237
|
#
|
|
355
238
|
|
|
356
|
-
def
|
|
357
|
-
|
|
358
|
-
@devices.fetch(receiver).__send__(method, *args)
|
|
359
|
-
end
|
|
239
|
+
def add_input(device)
|
|
240
|
+
device = Interactions.send(device) if device.is_a?(Symbol) && Interactions.respond_to?(device)
|
|
360
241
|
|
|
361
|
-
|
|
242
|
+
raise TypeError, "#{device.inspect} is not a valid InputDevice" unless device.is_a?(Interactions::InputDevice)
|
|
243
|
+
|
|
244
|
+
unless @async
|
|
245
|
+
max_device = @devices.max { |a, b| a.actions.length <=> b.actions.length }
|
|
246
|
+
pauses(device: device, number: max_device.actions.length) if max_device
|
|
247
|
+
end
|
|
248
|
+
@devices << device
|
|
249
|
+
device
|
|
362
250
|
end
|
|
363
251
|
end # ActionBuilder
|
|
364
252
|
end # WebDriver
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
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
|
+
#
|
|
23
|
+
# @api private
|
|
24
|
+
#
|
|
25
|
+
|
|
26
|
+
class ChildProcess
|
|
27
|
+
TimeoutError = Class.new(StandardError)
|
|
28
|
+
|
|
29
|
+
SIGTERM = 'TERM'
|
|
30
|
+
SIGKILL = 'KILL'
|
|
31
|
+
|
|
32
|
+
POLL_INTERVAL = 0.1
|
|
33
|
+
|
|
34
|
+
attr_accessor :detach
|
|
35
|
+
attr_writer :io
|
|
36
|
+
|
|
37
|
+
def self.build(*command)
|
|
38
|
+
new(*command)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def initialize(*command)
|
|
42
|
+
@command = command
|
|
43
|
+
@detach = false
|
|
44
|
+
@pid = nil
|
|
45
|
+
@status = nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def io
|
|
49
|
+
@io ||= Platform.null_device
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def start
|
|
53
|
+
options = {%i[out err] => io}
|
|
54
|
+
options[:pgroup] = true unless Platform.windows? # NOTE: this is a bug only in Windows 7
|
|
55
|
+
|
|
56
|
+
WebDriver.logger.debug("Starting process: #{@command} with #{options}", id: :process)
|
|
57
|
+
@pid = Process.spawn(*@command, options)
|
|
58
|
+
WebDriver.logger.debug(" -> pid: #{@pid}", id: :process)
|
|
59
|
+
|
|
60
|
+
Process.detach(@pid) if detach
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def stop(timeout = 3)
|
|
64
|
+
return unless @pid
|
|
65
|
+
return if exited?
|
|
66
|
+
|
|
67
|
+
WebDriver.logger.debug("Sending TERM to process: #{@pid}", id: :process)
|
|
68
|
+
terminate(@pid)
|
|
69
|
+
poll_for_exit(timeout)
|
|
70
|
+
|
|
71
|
+
WebDriver.logger.debug(" -> stopped #{@pid}", id: :process)
|
|
72
|
+
rescue TimeoutError, Errno::EINVAL
|
|
73
|
+
WebDriver.logger.debug(" -> sending KILL to process: #{@pid}", id: :process)
|
|
74
|
+
kill(@pid)
|
|
75
|
+
wait
|
|
76
|
+
WebDriver.logger.debug(" -> killed #{@pid}", id: :process)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def alive?
|
|
80
|
+
@pid && !exited?
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def exited?
|
|
84
|
+
return false unless @pid
|
|
85
|
+
|
|
86
|
+
WebDriver.logger.debug("Checking if #{@pid} is exited:", id: :process)
|
|
87
|
+
_, @status = Process.waitpid2(@pid, Process::WNOHANG | Process::WUNTRACED) if @status.nil?
|
|
88
|
+
return false if @status.nil?
|
|
89
|
+
|
|
90
|
+
exit_code = @status.exitstatus || @status.termsig
|
|
91
|
+
WebDriver.logger.debug(" -> exit code is #{exit_code.inspect}", id: :process)
|
|
92
|
+
|
|
93
|
+
!!exit_code
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def poll_for_exit(timeout)
|
|
97
|
+
WebDriver.logger.debug("Polling #{timeout} seconds for exit of #{@pid}", id: :process)
|
|
98
|
+
|
|
99
|
+
end_time = Time.now + timeout
|
|
100
|
+
sleep POLL_INTERVAL until exited? || Time.now > end_time
|
|
101
|
+
|
|
102
|
+
raise TimeoutError, " -> #{@pid} still alive after #{timeout} seconds" unless exited?
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def wait
|
|
106
|
+
return if exited?
|
|
107
|
+
|
|
108
|
+
_, @status = Process.waitpid2(@pid)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
def terminate(pid)
|
|
114
|
+
Process.kill(SIGTERM, pid)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def kill(pid)
|
|
118
|
+
Process.kill(SIGKILL, pid)
|
|
119
|
+
rescue Errno::ECHILD, Errno::ESRCH
|
|
120
|
+
# already dead
|
|
121
|
+
end
|
|
122
|
+
end # ChildProcess
|
|
123
|
+
end # WebDriver
|
|
124
|
+
end # Selenium
|