Skip to content

Commit ab4e010

Browse files
committed
[rb] each action interaction class validates its own source
This allows users to extend ActionBuilder by creating their own device subclasses
1 parent 77a0c71 commit ab4e010

13 files changed

Lines changed: 134 additions & 29 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def create_pause(duration = 0)
4545
end
4646

4747
def no_actions? # Determine if only pauses are present
48-
actions = @actions.reject { |action| action.type == Interaction::PAUSE }
48+
actions = @actions.reject { |action| action.type == :pause }
4949
actions.empty?
5050
end
5151
end # InputDevice

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

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,14 @@ module Selenium
2121
module WebDriver
2222
module Interactions
2323
class Interaction
24-
PAUSE = :pause
25-
26-
attr_reader :source
24+
attr_reader :type
2725

2826
def initialize(source)
29-
unless Interactions::SOURCE_TYPES.include? source.type
30-
raise TypeError,
31-
"#{source.type} is not a valid input type"
32-
end
27+
assert_source(source)
28+
end
3329

34-
@source = source
30+
def assert_source(_source)
31+
raise NotImplementedError, 'subclass responsibility'
3532
end
3633
end
3734
end # Interactions

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ class Pause < Interaction
2424
def initialize(source, duration = nil)
2525
super(source)
2626
@duration = duration
27+
@type = :pause
2728
end
2829

29-
def type
30-
PAUSE
30+
def assert_source(source)
31+
raise TypeError, "#{source.type} is not a valid input type" unless source.is_a? InputDevice
3132
end
3233

3334
def encode

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,13 @@ module Selenium
2121
module WebDriver
2222
module Interactions
2323
class PointerCancel < Interaction
24-
def type
25-
:pointerCancel
24+
def initialize(source)
25+
super(source)
26+
@type = :pointerCancel
27+
end
28+
29+
def assert_source(source)
30+
raise TypeError, "#{source.type} is not a valid input type" unless source.is_a? PointerInput
2631
end
2732

2833
def encode

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,11 @@ def initialize(source, duration, x, y, element: nil, origin: nil)
3131
@x_offset = x
3232
@y_offset = y
3333
@origin = element || origin || :viewport
34+
@type = :pointerMove
3435
end
3536

36-
def type
37-
:pointerMove
37+
def assert_source(source)
38+
raise TypeError, "#{source.type} is not a valid input type" unless source.is_a? PointerInput
3839
end
3940

4041
def encode

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ def initialize(source, direction, button)
2828
super(source)
2929
@direction = assert_direction(direction)
3030
@button = assert_button(button)
31+
@type = @direction
3132
end
3233

33-
def type
34-
@direction
34+
def assert_source(source)
35+
raise TypeError, "#{source.type} is not a valid input type" unless source.is_a? PointerInput
3536
end
3637

3738
def assert_button(button)

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ def initialize(source, type, key)
2929
@key = Keys.encode_key(key)
3030
end
3131

32+
def assert_source(source)
33+
raise TypeError, "#{source.type} is not a valid input type" unless source.is_a? KeyInput
34+
end
35+
3236
def assert_type(type)
3337
raise TypeError, "#{type.inspect} is not a valid key subtype" unless KeyInput::SUBTYPES.key? type
3438

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

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,73 @@
2222
module Selenium
2323
module WebDriver
2424
module Interactions
25+
class NewDevice < InputDevice
26+
def type
27+
:special
28+
end
29+
30+
def encode
31+
{type: type, name: name, actions: @actions.map(&:encode)}
32+
end
33+
34+
def create_special(val)
35+
add_action(NewInteraction.new(self, val))
36+
end
37+
end
38+
39+
class NewInteraction < Interaction
40+
def initialize(source, special)
41+
super(source)
42+
@special = special
43+
@type = :newType
44+
end
45+
46+
def assert_source(source)
47+
raise TypeError, "#{source.type} is not a valid input type" unless source.is_a? NewDevice
48+
end
49+
50+
def encode
51+
{type: type, special: @special}
52+
end
53+
end
54+
55+
class SubActionBuilder < ActionBuilder
56+
def initialize(bridge, mouse, keyboard, async = nil)
57+
super
58+
@devices << NewDevice.new('new')
59+
end
60+
61+
def special_action(special, device: nil)
62+
special_input(device).create_special(special)
63+
self
64+
end
65+
66+
def special_inputs
67+
@devices.select { |device| device.is_a? NewDevice }
68+
end
69+
70+
private
71+
72+
def special_input(device = nil)
73+
device ? get_device(device) : special_inputs.first
74+
end
75+
end
76+
2577
describe Interaction do
26-
let(:source) { NoneInput.new }
27-
let(:interaction) { Interaction.new(source) }
78+
it 'can create subclass' do
79+
bridge = instance_double(Remote::Bridge)
80+
allow(bridge).to receive(:send_actions)
81+
sub_action_builder = SubActionBuilder.new(bridge, Interactions.pointer(:mouse), Interactions.key('key'))
82+
sub_action_builder.special_action('special').perform
2883

29-
it 'returns valid source when provided' do
30-
expect(interaction.source).to eq source
84+
expect(bridge).to have_received(:send_actions).with([{type: :special,
85+
name: 'new',
86+
actions: [{type: :newType,
87+
special: 'special'}]}])
3188
end
3289

33-
it 'raises a TypeError if the provided source is invalid' do
34-
allow(source).to receive(:type).and_return(:no_type)
35-
expect { interaction }.to raise_error(TypeError)
90+
it 'raises a NotImplementedError if not a subclass' do
91+
expect { Interaction.new(NoneInput.new) }.to raise_error(NotImplementedError)
3692
end
3793
end
3894
end # Interactions

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ module Interactions
2727
let(:pause) { Pause.new(source) }
2828
let(:duration) { 5 }
2929

30+
describe '#initialize' do
31+
it 'accepts key input' do
32+
key = Interactions.key('key')
33+
expect { Pause.new(key) }.not_to raise_error
34+
end
35+
36+
it 'accepts pointer input' do
37+
mouse = Interactions.pointer(:mouse)
38+
expect { Pause.new(mouse) }.not_to raise_error
39+
end
40+
end
41+
3042
describe '#type' do
3143
it 'returns :pause' do
3244
expect(pause.type).to eq(:pause)

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ module Interactions
2525
describe PointerCancel do
2626
let(:pointer_cancel) { PointerCancel.new(Interactions.pointer(:mouse)) }
2727

28+
describe '#initialize' do
29+
it 'raises a TypeError if source is not a PointerInput' do
30+
key = Interactions.key('key')
31+
expect { PointerCancel.new(key) }.to raise_error(TypeError)
32+
end
33+
end
34+
2835
describe '#type' do
2936
it 'equals :pointerCancel' do
3037
expect(pointer_cancel.type).to eq(:pointerCancel)

0 commit comments

Comments
 (0)