Skip to content

Commit 884c3cc

Browse files
committed
[rb] use convenience methods for scrolling
1 parent 5519bab commit 884c3cc

5 files changed

Lines changed: 132 additions & 44 deletions

File tree

rb/lib/selenium/webdriver/common.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
require 'selenium/webdriver/common/interactions/pointer_input'
5555
require 'selenium/webdriver/common/interactions/scroll'
5656
require 'selenium/webdriver/common/interactions/wheel_input'
57+
require 'selenium/webdriver/common/interactions/scroll_origin'
5758
require 'selenium/webdriver/common/interactions/wheel_actions'
5859
require 'selenium/webdriver/common/action_builder'
5960
require 'selenium/webdriver/common/html5/shared_web_storage'
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

rb/lib/selenium/webdriver/common/interactions/wheel_actions.rb

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,40 +25,70 @@ def default_scroll_duration
2525
end
2626

2727
#
28-
# Scrolls by the provided amount from a designated origination point.
29-
#
30-
# The scroll origin is either the center of an element or the upper left of the viewport plus offsets.
31-
# If the origin is an element, and the element is not in the viewport, the bottom of the element will first
32-
# be scrolled to the bottom of the viewport.
28+
# If the element is outside the viewport, scrolls the bottom of the element to the bottom of the viewport.
3329
#
3430
# @example Scroll to element
3531
# el = driver.find_element(id: "some_id")
36-
# driver.action.scroll(origin: element).perform
32+
# driver.action.scroll_to(element).perform
3733
#
38-
# @example Scroll from element by a specified amount
34+
# @param [Object] Which element to scroll into the viewport.
35+
# @return [Selenium::WebDriver::WheelActions] A self reference.
36+
def scroll_to(element, device: nil)
37+
scroll(origin: element, device: device)
38+
end
39+
40+
#
41+
# Scrolls by provided amounts with the origin in the top left corner of the viewport.
42+
#
43+
# @example Scroll viewport by a specified amount
3944
# el = driver.find_element(id: "some_id")
40-
# driver.action.scroll(delta_x: 100, delta_y: 200, origin: element).perform
45+
# driver.action.scroll_by(100, 200).perform
4146
#
42-
# @example Scroll from element by a specified amount with an offset
47+
# @param [Integer] delta_x Distance along X axis to scroll using the wheel. A negative value scrolls left.
48+
# @param [Integer] delta_y Distance along Y axis to scroll using the wheel. A negative value scrolls up.
49+
# @return [Selenium::WebDriver::WheelActions] A self reference.
50+
def scroll_by(delta_x, delta_y, device: nil)
51+
scroll(delta_x: delta_x, delta_y: delta_y, device: device)
52+
end
53+
54+
#
55+
# Scrolls by provided amount based on a provided origin.
56+
#
57+
# The scroll origin is either the center of an element or the upper left of the viewport plus any offsets.
58+
# If the origin is an element, and the element is not in the viewport, the bottom of the element will first
59+
# be scrolled to the bottom of the viewport.
60+
#
61+
# @example Scroll from element by a specified amount
4362
# el = driver.find_element(id: "some_id")
44-
# driver.action.scroll(x: 10, y: 10, delta_x: 100, delta_y: 200, origin: element).perform
63+
# origin = WheelActions::ScrollOrigin.element(el)
64+
# driver.action.scroll_from(origin, 0, 200).perform
4565
#
46-
# @example Scroll viewport by a specified amount
66+
# @example Scroll from element by a specified amount with an offset
4767
# el = driver.find_element(id: "some_id")
48-
# driver.action.scroll(delta_x: 100, delta_y: 200).perform
68+
# origin = WheelActions::ScrollOrigin.element(el, 10, 10)
69+
# driver.action.scroll_from(origin, 100, 200).perform
4970
#
5071
# @example Scroll viewport by a specified amount with an offset
51-
# el = driver.find_element(id: "some_id")
52-
# driver.action.scroll(x: 10, y: 10, delta_x: 100, delta_y: 200).perform
72+
# origin = WheelActions::ScrollOrigin.viewport(10, 10)
73+
# driver.action.scroll_from(origin, 0, 200).perform
5374
#
54-
# @option opts [Integer] x The horizontal offset from the origin from which to start the scroll.
55-
# @option opts [Integer] y The vertical offset from the origin from which to start the scroll.
56-
# @option opts [Integer] delta_x Distance along X axis to scroll using the wheel. A negative value scrolls left.
57-
# @option opts [Integer] delta_y Distance along Y axis to scroll using the wheel. A negative value scrolls up.
58-
# @option opts [String, Element] origin The origin of the scroll, either the viewport or the center of an element.
75+
# @param [ScrollOrigin] scroll_origin Where scroll originates (viewport or element center) plus provided offsets.
76+
# @param [Integer] delta_x Distance along X axis to scroll using the wheel. A negative value scrolls left.
77+
# @param [Integer] delta_y Distance along Y axis to scroll using the wheel. A negative value scrolls up.
5978
# @return [Selenium::WebDriver::WheelActions] A self reference.
6079
# @raise [Error::MoveTargetOutOfBoundsError] If the origin with offset is outside the viewport.
61-
#
80+
def scroll_from(scroll_origin, delta_x, delta_y, device: nil)
81+
raise TypeError, "#{scroll_origin.inspect} isn't a valid ScrollOrigin" unless scroll_origin.is_a?(ScrollOrigin)
82+
83+
scroll(x: scroll_origin.x_offset,
84+
y: scroll_origin.y_offset,
85+
delta_x: delta_x,
86+
delta_y: delta_y,
87+
origin: scroll_origin.origin,
88+
device: device)
89+
end
90+
91+
private
6292

6393
def scroll(**opts)
6494
opts[:duration] = default_scroll_duration
@@ -68,8 +98,6 @@ def scroll(**opts)
6898
self
6999
end
70100

71-
private
72-
73101
def wheel_input(name = nil)
74102
device(name: name, type: Interactions::WHEEL) || add_wheel_input('wheel')
75103
end

rb/spec/integration/selenium/webdriver/action_builder_spec.rb

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -270,23 +270,38 @@ def in_viewport?(element)
270270
driver.execute_script(in_viewport, element)
271271
end
272272

273-
describe '#scroll', only: {browser: %i[chrome edge]} do
273+
describe '#scroll_to', only: {browser: %i[chrome edge]} do
274274
it 'scrolls to element' do
275275
driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html')
276276
iframe = driver.find_element(tag_name: 'iframe')
277277

278278
expect(in_viewport?(iframe)).to eq false
279279

280-
driver.action.scroll(origin: iframe).perform
280+
driver.action.scroll_to(iframe).perform
281281

282282
expect(in_viewport?(iframe)).to eq true
283283
end
284+
end
285+
286+
describe '#scroll_by', only: {browser: %i[chrome edge]} do
287+
it 'scrolls by given amount' do
288+
driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html')
289+
footer = driver.find_element(tag_name: 'footer')
290+
delta_y = footer.rect.y
291+
292+
driver.action.scroll_by(0, delta_y).perform
293+
294+
expect(in_viewport?(footer)).to eq true
295+
end
296+
end
284297

298+
describe '#scroll_from', only: {browser: %i[chrome edge]} do
285299
it 'scrolls from element by given amount' do
286300
driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html')
287301
iframe = driver.find_element(tag_name: 'iframe')
302+
scroll_origin = WheelActions::ScrollOrigin.element(iframe)
288303

289-
driver.action.scroll(delta_y: 200, origin: iframe).perform
304+
driver.action.scroll_from(scroll_origin, 0, 200).perform
290305

291306
driver.switch_to.frame(iframe)
292307
checkbox = driver.find_element(name: 'scroll_checkbox')
@@ -296,8 +311,9 @@ def in_viewport?(element)
296311
it 'scrolls from element by given amount with offset' do
297312
driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html')
298313
footer = driver.find_element(tag_name: 'footer')
314+
scroll_origin = WheelActions::ScrollOrigin.element(footer, 0, -50)
299315

300-
driver.action.scroll(y: -50, delta_y: 200, origin: footer).perform
316+
driver.action.scroll_from(scroll_origin, 0, 200).perform
301317

302318
iframe = driver.find_element(tag_name: 'iframe')
303319
driver.switch_to.frame(iframe)
@@ -308,26 +324,18 @@ def in_viewport?(element)
308324
it 'raises MoveTargetOutOfBoundsError when origin offset from element is out of viewport' do
309325
driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html')
310326
footer = driver.find_element(tag_name: 'footer')
327+
scroll_origin = WheelActions::ScrollOrigin.element(footer, 0, 50)
311328

312329
expect {
313-
driver.action.scroll(y: 50, delta_y: 200, origin: footer).perform
330+
driver.action.scroll_from(scroll_origin, 0, 200).perform
314331
}.to raise_error(Error::MoveTargetOutOfBoundsError)
315332
end
316333

317-
it 'scrolls by given amount' do
318-
driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html')
319-
footer = driver.find_element(tag_name: 'footer')
320-
delta_y = footer.rect.y
321-
322-
driver.action.scroll(delta_y: delta_y).perform
323-
324-
expect(in_viewport?(footer)).to eq true
325-
end
326-
327334
it 'scrolls by given amount with offset' do
328335
driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame.html')
336+
scroll_origin = WheelActions::ScrollOrigin.viewport(10, 10)
329337

330-
driver.action.scroll(x: 10, y: 10, delta_y: 200).perform
338+
driver.action.scroll_from(scroll_origin, 0, 200).perform
331339

332340
iframe = driver.find_element(tag_name: 'iframe')
333341
driver.switch_to.frame(iframe)
@@ -337,9 +345,10 @@ def in_viewport?(element)
337345

338346
it 'raises MoveTargetOutOfBoundsError when origin offset is out of viewport' do
339347
driver.navigate.to url_for('scrolling_tests/frame_with_nested_scrolling_frame.html')
348+
scroll_origin = WheelActions::ScrollOrigin.viewport(-10, -10)
340349

341350
expect {
342-
driver.action.scroll(x: -10, y: -10, delta_y: 200).perform
351+
driver.action.scroll_from(scroll_origin, 0, 200).perform
343352
}.to raise_error(Error::MoveTargetOutOfBoundsError)
344353
end
345354
end

rb/spec/unit/selenium/webdriver/common/interactions/wheel_actions_spec.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,16 @@ module WebDriver
5151
it 'gets wheel input' do
5252
allow(builder).to receive(:wheel_input).and_call_original
5353

54-
builder.scroll x: 5, y: 5, device: wheel.name
54+
builder.scroll_by 5, 5, device: wheel.name
5555

5656
expect(builder).to have_received(:wheel_input).with(wheel.name)
5757
end
5858

5959
it 'calls create_scroll with origin element offset' do
6060
allow(wheel).to receive(:create_scroll).and_call_original
6161

62-
builder.scroll x: 10, y: 10, delta_x: 5, delta_y: 5, origin: element, device: wheel.name
62+
scroll_origin = WheelActions::ScrollOrigin.element(element, 10, 10)
63+
builder.scroll_from scroll_origin, 5, 5, device: wheel.name
6364

6465
expect(wheel).to have_received(:create_scroll).with(duration: duration,
6566
origin: element,
@@ -72,7 +73,8 @@ module WebDriver
7273
it 'calls create_scroll with origin viewport offset' do
7374
allow(wheel).to receive(:create_scroll).and_call_original
7475

75-
builder.scroll x: -10, y: -10, delta_x: 5, delta_y: 5, device: wheel.name, origin: :viewport
76+
scroll_origin = WheelActions::ScrollOrigin.viewport(-10, -10)
77+
builder.scroll_from scroll_origin, 5, 5, device: wheel.name
7678

7779
expect(wheel).to have_received(:create_scroll).with(duration: duration,
7880
origin: :viewport,
@@ -85,13 +87,13 @@ module WebDriver
8587
it 'passes the wheel to the #tick method' do
8688
allow(builder).to receive(:tick)
8789

88-
builder.scroll
90+
builder.scroll_to(element)
8991

9092
expect(builder).to have_received(:tick).with(wheel)
9193
end
9294

9395
it 'returns itself' do
94-
expect(builder.scroll).to eq(builder)
96+
expect(builder.scroll_to(element)).to eq(builder)
9597
end
9698
end
9799
end

0 commit comments

Comments
 (0)