selenium-webdriver 4.0.3 → 4.2.1

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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +64 -0
  3. data/LICENSE +1 -1
  4. data/NOTICE +1 -1
  5. data/lib/selenium/server.rb +14 -9
  6. data/lib/selenium/webdriver/bidi/session.rb +38 -0
  7. data/lib/selenium/webdriver/bidi.rb +55 -0
  8. data/lib/selenium/webdriver/chrome/features.rb +5 -0
  9. data/lib/selenium/webdriver/chrome/options.rb +20 -20
  10. data/lib/selenium/webdriver/common/action_builder.rb +108 -21
  11. data/lib/selenium/webdriver/common/driver.rb +2 -2
  12. data/lib/selenium/webdriver/common/driver_extensions/has_bidi.rb +38 -0
  13. data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +10 -0
  14. data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +1 -2
  15. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +1 -1
  16. data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +1 -1
  17. data/lib/selenium/webdriver/common/element.rb +1 -1
  18. data/lib/selenium/webdriver/common/error.rb +1 -1
  19. data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
  20. data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -25
  21. data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
  22. data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -1
  23. data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
  24. data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
  25. data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
  26. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +49 -43
  27. data/lib/selenium/webdriver/common/interactions/pointer_cancel.rb +45 -0
  28. data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
  29. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
  30. data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
  31. data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
  32. data/lib/selenium/webdriver/common/interactions/scroll.rb +57 -0
  33. data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
  34. data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
  35. data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +113 -0
  36. data/lib/selenium/webdriver/common/interactions/wheel_input.rb +42 -0
  37. data/lib/selenium/webdriver/common/keys.rb +1 -0
  38. data/lib/selenium/webdriver/common/platform.rb +4 -4
  39. data/lib/selenium/webdriver/common/search_context.rb +0 -6
  40. data/lib/selenium/webdriver/common/service_manager.rb +2 -3
  41. data/lib/selenium/webdriver/common/shadow_root.rb +1 -1
  42. data/lib/selenium/webdriver/common/socket_poller.rb +1 -1
  43. data/lib/selenium/webdriver/common/websocket_connection.rb +149 -0
  44. data/lib/selenium/webdriver/common/zipper.rb +3 -1
  45. data/lib/selenium/webdriver/common.rb +14 -2
  46. data/lib/selenium/webdriver/devtools/request.rb +1 -1
  47. data/lib/selenium/webdriver/devtools/response.rb +1 -1
  48. data/lib/selenium/webdriver/devtools.rb +5 -101
  49. data/lib/selenium/webdriver/edge/features.rb +1 -0
  50. data/lib/selenium/webdriver/firefox/driver.rb +1 -0
  51. data/lib/selenium/webdriver/firefox/features.rb +2 -5
  52. data/lib/selenium/webdriver/firefox/options.rb +4 -2
  53. data/lib/selenium/webdriver/firefox/profile.rb +1 -5
  54. data/lib/selenium/webdriver/firefox/util.rb +46 -0
  55. data/lib/selenium/webdriver/firefox.rb +1 -0
  56. data/lib/selenium/webdriver/ie/options.rb +2 -2
  57. data/lib/selenium/webdriver/remote/bridge.rb +24 -19
  58. data/lib/selenium/webdriver/remote/commands.rb +0 -5
  59. data/lib/selenium/webdriver/remote/http/default.rb +6 -12
  60. data/lib/selenium/webdriver/remote/response.rb +2 -2
  61. data/lib/selenium/webdriver/remote.rb +6 -5
  62. data/lib/selenium/webdriver/support/cdp_client_generator.rb +4 -4
  63. data/lib/selenium/webdriver/support/color.rb +7 -7
  64. data/lib/selenium/webdriver/support/guards/guard_condition.rb +1 -1
  65. data/lib/selenium/webdriver/support/guards.rb +1 -1
  66. data/lib/selenium/webdriver/version.rb +1 -1
  67. data/lib/selenium/webdriver.rb +2 -1
  68. data/selenium-webdriver.gemspec +7 -4
  69. metadata +55 -5
@@ -20,6 +20,12 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module Interactions
23
+ #
24
+ # Creates actions specific to Pointer Input devices
25
+ #
26
+ # @api private
27
+ #
28
+
23
29
  class PointerInput < InputDevice
24
30
  KIND = {mouse: :mouse, pen: :pen, touch: :touch}.freeze
25
31
 
@@ -28,17 +34,12 @@ module Selenium
28
34
  def initialize(kind, name: nil)
29
35
  super(name)
30
36
  @kind = assert_kind(kind)
31
- end
32
-
33
- def type
34
- Interactions::POINTER
37
+ @type = Interactions::POINTER
35
38
  end
36
39
 
37
40
  def encode
38
- return nil if no_actions?
39
-
40
- output = {type: type, id: name, actions: @actions.map(&:encode)}
41
- output[:parameters] = {pointerType: kind}
41
+ output = super
42
+ output[:parameters] = {pointerType: kind} if output
42
43
  output
43
44
  end
44
45
 
@@ -48,92 +49,22 @@ module Selenium
48
49
  KIND[pointer]
49
50
  end
50
51
 
51
- def create_pointer_move(duration: 0, x: 0, y: 0, element: nil, origin: nil)
52
- add_action(PointerMove.new(self, duration, x, y, element: element, origin: origin))
52
+ def create_pointer_move(duration: 0, x: 0, y: 0, origin: nil, **opts)
53
+ add_action(PointerMove.new(self, duration, x, y, origin: origin, **opts))
53
54
  end
54
55
 
55
- def create_pointer_down(button)
56
- add_action(PointerPress.new(self, :down, button))
56
+ def create_pointer_down(button, **opts)
57
+ add_action(PointerPress.new(self, :down, button, **opts))
57
58
  end
58
59
 
59
- def create_pointer_up(button)
60
- add_action(PointerPress.new(self, :up, button))
60
+ def create_pointer_up(button, **opts)
61
+ add_action(PointerPress.new(self, :up, button, **opts))
61
62
  end
62
63
 
63
64
  def create_pointer_cancel
64
65
  add_action(PointerCancel.new(self))
65
66
  end
66
67
  end # PointerInput
67
-
68
- class PointerPress < Interaction
69
- BUTTONS = {left: 0, middle: 1, right: 2}.freeze
70
- DIRECTIONS = {down: :pointerDown, up: :pointerUp}.freeze
71
-
72
- def initialize(source, direction, button)
73
- super(source)
74
- @direction = assert_direction(direction)
75
- @button = assert_button(button)
76
- end
77
-
78
- def type
79
- @direction
80
- end
81
-
82
- def assert_button(button)
83
- if button.is_a? Symbol
84
- raise TypeError, "#{button.inspect} is not a valid button!" unless BUTTONS.key? button
85
-
86
- button = BUTTONS[button]
87
- end
88
- raise ArgumentError, 'Button number cannot be negative!' unless button >= 0
89
-
90
- button
91
- end
92
-
93
- def assert_direction(direction)
94
- raise TypeError, "#{direction.inspect} is not a valid button direction" unless DIRECTIONS.key? direction
95
-
96
- DIRECTIONS[direction]
97
- end
98
-
99
- def encode
100
- {type: type, button: @button}
101
- end
102
- end # PointerPress
103
-
104
- class PointerMove < Interaction
105
- VIEWPORT = :viewport
106
- POINTER = :pointer
107
- ORIGINS = [VIEWPORT, POINTER].freeze
108
-
109
- def initialize(source, duration, x, y, element: nil, origin: nil)
110
- super(source)
111
- @duration = duration * 1000
112
- @x_offset = x
113
- @y_offset = y
114
- @origin = element || origin
115
- end
116
-
117
- def type
118
- :pointerMove
119
- end
120
-
121
- def encode
122
- output = {type: type, duration: @duration.to_i, x: @x_offset, y: @y_offset}
123
- output[:origin] = @origin
124
- output
125
- end
126
- end # Move
127
-
128
- class PointerCancel < Interaction
129
- def type
130
- :pointerCancel
131
- end
132
-
133
- def encode
134
- {type: type}
135
- end
136
- end # Cancel
137
68
  end # Interactions
138
69
  end # WebDriver
139
70
  end # Selenium
@@ -0,0 +1,60 @@
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
+ module Interactions
23
+ #
24
+ # Action related to moving the pointer.
25
+ #
26
+ # @api private
27
+ #
28
+
29
+ class PointerMove < Interaction
30
+ include PointerEventProperties
31
+
32
+ VIEWPORT = :viewport
33
+ POINTER = :pointer
34
+ ORIGINS = [VIEWPORT, POINTER].freeze
35
+
36
+ def initialize(source, duration, x, y, element: nil, origin: nil, **opts)
37
+ super(source)
38
+ @duration = duration * 1000
39
+ @x_offset = x
40
+ @y_offset = y
41
+ @origin = element || origin || :viewport
42
+ @type = :pointerMove
43
+ @opts = opts
44
+ end
45
+
46
+ def assert_source(source)
47
+ raise TypeError, "#{source.type} is not a valid input type" unless source.is_a? PointerInput
48
+ end
49
+
50
+ def encode
51
+ process_opts.merge('type' => type.to_s,
52
+ 'duration' => @duration.to_i,
53
+ 'x' => @x_offset,
54
+ 'y' => @y_offset,
55
+ 'origin' => @origin)
56
+ end
57
+ end # PointerMove
58
+ end # Interactions
59
+ end # WebDriver
60
+ end # Selenium
@@ -0,0 +1,85 @@
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
+ module Interactions
23
+ #
24
+ # Actions related to clicking, tapping or pressing the pointer.
25
+ #
26
+ # @api private
27
+ #
28
+
29
+ class PointerPress < Interaction
30
+ include PointerEventProperties
31
+
32
+ BUTTONS = {left: 0,
33
+ touch: 0,
34
+ pen_contact: 0,
35
+ middle: 1,
36
+ right: 2,
37
+ pen_barrel: 2,
38
+ x1: 3,
39
+ back: 3,
40
+ x2: 4,
41
+ forward: 4}.freeze
42
+ DIRECTIONS = {down: :pointerDown, up: :pointerUp}.freeze
43
+
44
+ def initialize(source, direction, button, **opts)
45
+ super(source)
46
+ @direction = assert_direction(direction)
47
+ @button = assert_button(button)
48
+ @type = @direction
49
+ @opts = opts
50
+ end
51
+
52
+ def encode
53
+ process_opts.merge('type' => type.to_s, 'button' => @button)
54
+ end
55
+
56
+ private
57
+
58
+ def assert_source(source)
59
+ raise TypeError, "#{source.type} is not a valid input type" unless source.is_a? PointerInput
60
+ end
61
+
62
+ def assert_button(button)
63
+ case button
64
+ when Symbol
65
+ raise ArgumentError, "#{button} is not a valid button!" unless BUTTONS.key? button
66
+
67
+ BUTTONS[button]
68
+ when Integer
69
+ raise ArgumentError, 'Button number cannot be negative!' if button.negative?
70
+
71
+ button
72
+ else
73
+ raise TypeError, "button must be a positive integer or one of #{BUTTONS.keys}, not #{button.class}"
74
+ end
75
+ end
76
+
77
+ def assert_direction(direction)
78
+ raise ArgumentError, "#{direction.inspect} is not a valid button direction" unless DIRECTIONS.key? direction
79
+
80
+ DIRECTIONS[direction]
81
+ end
82
+ end # PointerPress
83
+ end # Interactions
84
+ end # WebDriver
85
+ end # Selenium
@@ -0,0 +1,57 @@
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
+ module Interactions
23
+ #
24
+ # Action related to scrolling a wheel.
25
+ #
26
+ # @api private
27
+ #
28
+
29
+ class Scroll < Interaction
30
+ def initialize(source:, x: 0, y: 0, delta_x: 0, delta_y: 0, origin: :viewport, duration: 0.25)
31
+ super(source)
32
+ @type = :scroll
33
+ @duration = duration * 1000
34
+ @origin = origin
35
+ @x_offset = x
36
+ @y_offset = y
37
+ @delta_x = delta_x
38
+ @delta_y = delta_y
39
+ end
40
+
41
+ def assert_source(source)
42
+ raise TypeError, "#{source.type} is not a valid input type" unless source.is_a? WheelInput
43
+ end
44
+
45
+ def encode
46
+ {'type' => type.to_s,
47
+ 'duration' => @duration.to_i,
48
+ 'x' => @x_offset,
49
+ 'y' => @y_offset,
50
+ 'deltaX' => @delta_x,
51
+ 'deltaY' => @delta_y,
52
+ 'origin' => @origin.is_a?(Element) ? @origin : @origin.to_s}
53
+ end
54
+ end # PointerPress
55
+ end # Interactions
56
+ end # WebDriver
57
+ end # Selenium
@@ -0,0 +1,48 @@
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
+ module WheelActions
23
+ class ScrollOrigin
24
+ class << self
25
+ def element(element, x_offset = 0, y_offset = 0)
26
+ new(element, x_offset, y_offset)
27
+ end
28
+
29
+ def viewport(x_offset = 0, y_offset = 0)
30
+ new(:viewport, x_offset, y_offset)
31
+ end
32
+ end
33
+
34
+ attr_reader :origin, :x_offset, :y_offset
35
+
36
+ #
37
+ # Use a static method to access
38
+ # @api private
39
+ #
40
+ def initialize(origin, x_offset, y_offset)
41
+ @origin = origin
42
+ @x_offset = x_offset
43
+ @y_offset = y_offset
44
+ end
45
+ end # ScrollOrigin
46
+ end # WheelActions
47
+ end # WebDriver
48
+ end # Selenium
@@ -0,0 +1,54 @@
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
+ module Interactions
23
+ #
24
+ # Actions related to pressing keys.
25
+ #
26
+ # @api private
27
+ #
28
+
29
+ class TypingInteraction < Interaction
30
+ attr_reader :type
31
+
32
+ def initialize(source, type, key)
33
+ super(source)
34
+ @type = assert_type(type)
35
+ @key = Keys.encode_key(key)
36
+ end
37
+
38
+ def assert_source(source)
39
+ raise TypeError, "#{source.type} is not a valid input type" unless source.is_a? KeyInput
40
+ end
41
+
42
+ def assert_type(type)
43
+ raise TypeError, "#{type.inspect} is not a valid key subtype" unless KeyInput::SUBTYPES.key? type
44
+
45
+ KeyInput::SUBTYPES[type]
46
+ end
47
+
48
+ def encode
49
+ {type: @type, value: @key}
50
+ end
51
+ end # TypingInteraction
52
+ end # Interactions
53
+ end # WebDriver
54
+ end # Selenium
@@ -0,0 +1,113 @@
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
+ module WheelActions
23
+ attr_writer :default_scroll_duration
24
+
25
+ #
26
+ # By default this is set to 250ms in the ActionBuilder constructor
27
+ # It can be overridden with default_scroll_duration=
28
+ #
29
+
30
+ def default_scroll_duration
31
+ @default_scroll_duration ||= @duration / 1000.0 # convert ms to seconds
32
+ end
33
+
34
+ #
35
+ # If the element is outside the viewport, scrolls the bottom of the element to the bottom of the viewport.
36
+ #
37
+ # @example Scroll to element
38
+ # el = driver.find_element(id: "some_id")
39
+ # driver.action.scroll_to(element).perform
40
+ #
41
+ # @param [Object] Which element to scroll into the viewport.
42
+ # @return [Selenium::WebDriver::WheelActions] A self reference.
43
+ def scroll_to(element, device: nil)
44
+ scroll(origin: element, device: device)
45
+ end
46
+
47
+ #
48
+ # Scrolls by provided amounts with the origin in the top left corner of the viewport.
49
+ #
50
+ # @example Scroll viewport by a specified amount
51
+ # el = driver.find_element(id: "some_id")
52
+ # driver.action.scroll_by(100, 200).perform
53
+ #
54
+ # @param [Integer] delta_x Distance along X axis to scroll using the wheel. A negative value scrolls left.
55
+ # @param [Integer] delta_y Distance along Y axis to scroll using the wheel. A negative value scrolls up.
56
+ # @return [Selenium::WebDriver::WheelActions] A self reference.
57
+ def scroll_by(delta_x, delta_y, device: nil)
58
+ scroll(delta_x: delta_x, delta_y: delta_y, device: device)
59
+ end
60
+
61
+ #
62
+ # Scrolls by provided amount based on a provided origin.
63
+ #
64
+ # The scroll origin is either the center of an element or the upper left of the viewport plus any offsets.
65
+ # If the origin is an element, and the element is not in the viewport, the bottom of the element will first
66
+ # be scrolled to the bottom of the viewport.
67
+ #
68
+ # @example Scroll from element by a specified amount
69
+ # el = driver.find_element(id: "some_id")
70
+ # origin = WheelActions::ScrollOrigin.element(el)
71
+ # driver.action.scroll_from(origin, 0, 200).perform
72
+ #
73
+ # @example Scroll from element by a specified amount with an offset
74
+ # el = driver.find_element(id: "some_id")
75
+ # origin = WheelActions::ScrollOrigin.element(el, 10, 10)
76
+ # driver.action.scroll_from(origin, 100, 200).perform
77
+ #
78
+ # @example Scroll viewport by a specified amount with an offset
79
+ # origin = WheelActions::ScrollOrigin.viewport(10, 10)
80
+ # driver.action.scroll_from(origin, 0, 200).perform
81
+ #
82
+ # @param [ScrollOrigin] scroll_origin Where scroll originates (viewport or element center) plus provided offsets.
83
+ # @param [Integer] delta_x Distance along X axis to scroll using the wheel. A negative value scrolls left.
84
+ # @param [Integer] delta_y Distance along Y axis to scroll using the wheel. A negative value scrolls up.
85
+ # @return [Selenium::WebDriver::WheelActions] A self reference.
86
+ # @raise [Error::MoveTargetOutOfBoundsError] If the origin with offset is outside the viewport.
87
+ def scroll_from(scroll_origin, delta_x, delta_y, device: nil)
88
+ raise TypeError, "#{scroll_origin.inspect} isn't a valid ScrollOrigin" unless scroll_origin.is_a?(ScrollOrigin)
89
+
90
+ scroll(x: scroll_origin.x_offset,
91
+ y: scroll_origin.y_offset,
92
+ delta_x: delta_x,
93
+ delta_y: delta_y,
94
+ origin: scroll_origin.origin,
95
+ device: device)
96
+ end
97
+
98
+ private
99
+
100
+ def scroll(**opts)
101
+ opts[:duration] = default_scroll_duration
102
+ wheel = wheel_input(opts.delete(:device))
103
+ wheel.create_scroll(**opts)
104
+ tick(wheel)
105
+ self
106
+ end
107
+
108
+ def wheel_input(name = nil)
109
+ device(name: name, type: Interactions::WHEEL) || add_wheel_input('wheel')
110
+ end
111
+ end # WheelActions
112
+ end # WebDriver
113
+ end # Selenium
@@ -0,0 +1,42 @@
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
+ module Interactions
23
+ #
24
+ # Creates actions specific to Pointer Input devices
25
+ #
26
+ # @api private
27
+ #
28
+
29
+ class WheelInput < InputDevice
30
+ def initialize(name = nil)
31
+ super(name)
32
+ @type = Interactions::WHEEL
33
+ end
34
+
35
+ def create_scroll(**opts)
36
+ opts[:source] = self
37
+ add_action(Scroll.new(**opts))
38
+ end
39
+ end # PointerInput
40
+ end # Interactions
41
+ end # WebDriver
42
+ end # Selenium
@@ -90,6 +90,7 @@ module Selenium
90
90
  meta: "\ue03D",
91
91
  command: "\ue03D", # alias
92
92
  left_meta: "\ue03D", # alias
93
+ zenkaku_hankaku: "\uE040",
93
94
  right_shift: "\ue050",
94
95
  right_control: "\ue051",
95
96
  right_alt: "\ue052",
@@ -104,7 +104,7 @@ module Selenium
104
104
  end
105
105
 
106
106
  def cygwin?
107
- RUBY_PLATFORM =~ /cygwin/
107
+ RUBY_PLATFORM.include?('cygwin')
108
108
  !Regexp.last_match.nil?
109
109
  end
110
110
 
@@ -177,9 +177,9 @@ module Selenium
177
177
 
178
178
  def find_in_program_files(*binary_names)
179
179
  paths = [
180
- ENV['PROGRAMFILES'] || '\\Program Files',
181
- ENV['ProgramFiles(x86)'] || '\\Program Files (x86)',
182
- ENV['ProgramW6432'] || '\\Program Files'
180
+ ENV.fetch('PROGRAMFILES', '\\Program Files'),
181
+ ENV.fetch('ProgramFiles(x86)', '\\Program Files (x86)'),
182
+ ENV.fetch('ProgramW6432', '\\Program Files')
183
183
  ]
184
184
 
185
185
  paths.each do |root|
@@ -61,9 +61,6 @@ module Selenium
61
61
  raise ArgumentError, "cannot find element by #{how.inspect}" unless by
62
62
 
63
63
  bridge.find_element_by by, what, ref
64
- rescue Selenium::WebDriver::Error::TimeoutError
65
- # Implicit Wait times out in Edge
66
- raise Selenium::WebDriver::Error::NoSuchElementError
67
64
  end
68
65
 
69
66
  #
@@ -79,9 +76,6 @@ module Selenium
79
76
  raise ArgumentError, "cannot find elements by #{how.inspect}" unless by
80
77
 
81
78
  bridge.find_elements_by by, what, ref
82
- rescue Selenium::WebDriver::Error::TimeoutError
83
- # Implicit Wait times out in Edge
84
- []
85
79
  end
86
80
 
87
81
  private
@@ -49,7 +49,7 @@ module Selenium
49
49
  def start
50
50
  raise "already started: #{uri.inspect} #{@executable_path.inspect}" if process_running?
51
51
 
52
- Platform.exit_hook(&method(:stop)) # make sure we don't leave the server running
52
+ Platform.exit_hook { stop } # make sure we don't leave the server running
53
53
 
54
54
  socket_lock.locked do
55
55
  find_free_port
@@ -60,6 +60,7 @@ module Selenium
60
60
 
61
61
  def stop
62
62
  return unless @shutdown_supported
63
+ return if process_exited?
63
64
 
64
65
  stop_server
65
66
  @process.poll_for_exit STOP_TIMEOUT
@@ -116,8 +117,6 @@ module Selenium
116
117
  end
117
118
 
118
119
  def stop_server
119
- return if process_exited?
120
-
121
120
  connect_to_server do |http|
122
121
  headers = WebDriver::Remote::Http::Common::DEFAULT_HEADERS.dup
123
122
  http.get('/shutdown', headers)
@@ -45,7 +45,7 @@ module Selenium
45
45
  alias_method :eql?, :==
46
46
 
47
47
  def hash
48
- @id.hash ^ @bridge.hash
48
+ [@id, @bridge].hash
49
49
  end
50
50
 
51
51
  #