selenium-webdriver 4.0.2 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES +70 -0
- data/LICENSE +1 -1
- data/NOTICE +1 -1
- data/lib/selenium/server.rb +14 -9
- data/lib/selenium/webdriver/bidi/session.rb +38 -0
- data/lib/selenium/webdriver/bidi.rb +55 -0
- data/lib/selenium/webdriver/chrome/features.rb +5 -0
- data/lib/selenium/webdriver/chrome/options.rb +20 -20
- data/lib/selenium/webdriver/common/action_builder.rb +108 -21
- data/lib/selenium/webdriver/common/driver.rb +2 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_bidi.rb +38 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +10 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +1 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +1 -1
- data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +1 -1
- data/lib/selenium/webdriver/common/element.rb +1 -1
- data/lib/selenium/webdriver/common/error.rb +1 -1
- data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
- data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -25
- data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
- data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -1
- 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 +49 -43
- 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 +57 -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/interactions/wheel_input.rb +42 -0
- data/lib/selenium/webdriver/common/keys.rb +1 -0
- data/lib/selenium/webdriver/common/options.rb +1 -3
- data/lib/selenium/webdriver/common/platform.rb +4 -4
- data/lib/selenium/webdriver/common/search_context.rb +0 -6
- data/lib/selenium/webdriver/common/service_manager.rb +2 -3
- data/lib/selenium/webdriver/common/shadow_root.rb +1 -1
- data/lib/selenium/webdriver/common/socket_poller.rb +1 -1
- data/lib/selenium/webdriver/common/websocket_connection.rb +149 -0
- data/lib/selenium/webdriver/common/zipper.rb +3 -1
- data/lib/selenium/webdriver/common.rb +14 -2
- data/lib/selenium/webdriver/devtools/request.rb +1 -1
- data/lib/selenium/webdriver/devtools/response.rb +1 -1
- data/lib/selenium/webdriver/devtools.rb +5 -101
- data/lib/selenium/webdriver/edge/features.rb +1 -0
- data/lib/selenium/webdriver/firefox/driver.rb +1 -0
- data/lib/selenium/webdriver/firefox/features.rb +2 -5
- data/lib/selenium/webdriver/firefox/options.rb +5 -3
- data/lib/selenium/webdriver/firefox/profile.rb +1 -5
- data/lib/selenium/webdriver/firefox/util.rb +46 -0
- data/lib/selenium/webdriver/firefox.rb +1 -0
- data/lib/selenium/webdriver/ie/options.rb +2 -2
- data/lib/selenium/webdriver/remote/bridge.rb +25 -20
- data/lib/selenium/webdriver/remote/commands.rb +0 -5
- data/lib/selenium/webdriver/remote/http/default.rb +6 -12
- data/lib/selenium/webdriver/remote/response.rb +2 -2
- data/lib/selenium/webdriver/remote.rb +6 -5
- data/lib/selenium/webdriver/support/cdp_client_generator.rb +4 -4
- data/lib/selenium/webdriver/support/color.rb +7 -7
- data/lib/selenium/webdriver/support/guards/guard_condition.rb +1 -1
- data/lib/selenium/webdriver/support/guards.rb +1 -1
- data/lib/selenium/webdriver/support/select.rb +1 -1
- data/lib/selenium/webdriver/version.rb +1 -1
- data/lib/selenium/webdriver.rb +2 -1
- data/selenium-webdriver.gemspec +7 -4
- 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
|
-
|
32
|
-
|
33
|
-
def type
|
34
|
-
Interactions::POINTER
|
37
|
+
@type = Interactions::POINTER
|
35
38
|
end
|
36
39
|
|
37
40
|
def encode
|
38
|
-
|
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,
|
52
|
-
add_action(PointerMove.new(self, duration, x, y,
|
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 # 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
|
@@ -104,7 +104,7 @@ module Selenium
|
|
104
104
|
end
|
105
105
|
|
106
106
|
def cygwin?
|
107
|
-
RUBY_PLATFORM
|
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
|
181
|
-
ENV
|
182
|
-
ENV
|
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
|
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)
|