Skip to content

Commit 5841a39

Browse files
authored
add mobile gesture for XCUITest (#537)
* add tap for example * add all gestures for xcuitest * fix rubocop * arrange mthods a bit * add easy tests and example * fix rubocop * add select_picker_wheel * remove line * add comments and documentations a bit * add test for select picker wheel * add some links
1 parent 7a7cf44 commit 5841a39

File tree

4 files changed

+214
-0
lines changed

4 files changed

+214
-0
lines changed

docs/ios_xcuitest.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,12 @@ finds_exact(value) # Return any elements include `value` as its name attributes.
7979
xpaths("//some xpaths")
8080
```
8181

82+
## Gesture
83+
- `mobile:` commands are provided by WDA.
84+
- Documentations
85+
- https://github.com/appium/appium/blob/master/docs/en/writing-running-appium/ios-xctest-mobile-gestures.md
86+
- Specs by test code
87+
- https://github.com/appium/appium-xcuitest-driver/blob/master/test/functional/basic/gesture-e2e-specs.js
88+
8289
## Other actions
8390
Basically, other actions such as `type` are compatible with `automationName = Appium`.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# rake ios[ios/xcuitest_gestures]
2+
describe 'ios/xcuitest_gestures' do
3+
def before_first
4+
screen.must_equal catalog
5+
end
6+
7+
def after_last
8+
screen.must_equal catalog
9+
end
10+
11+
t 'before_first' do
12+
before_first
13+
end
14+
15+
t 'tap' do
16+
element = text('controls')
17+
tap(x: 0, y: 0, element: element)
18+
end
19+
20+
t 'double_tap' do
21+
element = button('Tinted')
22+
double_tap(element: element)
23+
end
24+
25+
t 'scroll' do
26+
scroll direction: 'down'
27+
text('Style Default').displayed?.must_be true
28+
end
29+
30+
t 'swipe' do
31+
swipe direction: 'down'
32+
swipe direction: 'down'
33+
34+
proc { text('Style Default') }.must_raise ::Selenium::WebDriver::Error::NoSuchElementError
35+
end
36+
37+
t 'pinch' do
38+
pinch(scale: 0.5, velocity: -1)
39+
end
40+
41+
t 'back to top' do
42+
back_click
43+
end
44+
45+
t 'select_picker_wheel' do
46+
element = text('pickers')
47+
tap(x: 0, y: 0, element: element)
48+
49+
e = find_element :name, 'John Appleseed'
50+
select_picker_wheel(element: e, order: 'next')
51+
52+
e.displayed?.must_be false
53+
find_element(:name, 'Serena Auroux').displayed?.must_be true
54+
55+
back_click
56+
end
57+
58+
t 'after_last' do
59+
after_last
60+
end
61+
end

lib/appium_lib/driver.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
require_relative 'ios/element/textfield'
2525
require_relative 'ios/element/text'
2626
require_relative 'ios/mobile_methods'
27+
require_relative 'ios/xcuitest_gestures'
2728

2829
# android
2930
require_relative 'android/helper'
@@ -411,6 +412,7 @@ def initialize(opts = {})
411412
else
412413
# load iOS specific methods
413414
extend Appium::Ios
415+
extend Appium::Ios::XcuitestGesture if automation_name_is_xcuitest? # Override touch actions
414416
end
415417

416418
# apply os specific patches
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
module Appium
2+
module Ios
3+
module XcuitestGesture
4+
# @param [string] direction Either 'up', 'down', 'left' or 'right'.
5+
# @option opts [Element] :element Element to swipe on
6+
#
7+
# ```ruby
8+
# swipe direction: "down"
9+
# ```
10+
def swipe(direction:, element: nil)
11+
return unless %w(up down left right).include?(direction)
12+
13+
args = { direction: direction }
14+
args[:element] = element.ref if element
15+
16+
execute_script 'mobile: swipe', args
17+
end
18+
19+
# @param [string] direction Either 'up', 'down', 'left' or 'right'.
20+
# @option opts [String] :name the accessibility id of the child element, to which scrolling is performed.
21+
# @option opts [Element] :element Element id to long tap on.
22+
# @option opts [bool] :to_visible Boolean parameter. If set to true then asks to scroll to the first visible
23+
# element in the parent container. Has no effect if element is not set
24+
# @option opts [String] :predicate_string the NSPredicate locator of the child element,
25+
# to which the scrolling should be performed. Has no effect if element is not a container
26+
#
27+
# ```ruby
28+
# scroll direction: "down"
29+
# ```
30+
def scroll(direction:, name: nil, element: nil, to_visible: nil, predicate_string: nil)
31+
return 'Set "up", "down", "left" or "right" for :direction' unless %w(up down left right).include?(direction)
32+
33+
args = { direction: direction }
34+
args[:element] = element.ref if element
35+
args[:name] = name if name
36+
args[:toVisible] = to_visible if to_visible
37+
args[:predicateString] = predicate_string if predicate_string
38+
39+
execute_script 'mobile: scroll', args
40+
end
41+
42+
# @param scale [scale] X tap coordinate of type float. Mandatory parameter
43+
# @param velocity [float] Y tap coordinate of type float. Mandatory parameter
44+
# @option opts [Element] :element Element id to long tap on.
45+
#
46+
# ```ruby
47+
# pinch scale: 0.5, velocity: -1
48+
# ```
49+
def pinch(scale:, velocity: 1.0, element: nil)
50+
return unless automation_name_is_xcuitest?
51+
52+
args = { scale: scale, velocity: velocity }
53+
args[:element] = element.ref if element
54+
55+
execute_script 'mobile: pinch', args
56+
end
57+
58+
# @param x [float] X Screen x tap coordinate of type float. Mandatory parameter only if element is not set
59+
# @param y [float] Y Screen y tap coordinate of type float. Mandatory parameter only if element is not set
60+
# @option opts [Element] :element Element to long tap on.
61+
#
62+
# ```ruby
63+
# double_tap x: 100, y: 100
64+
# double_tap element: find_element(:accessibility_id, "some item")
65+
# ```
66+
def double_tap(x: nil, y: nil, element: nil)
67+
return 'require XCUITest(WDA)' unless automation_name_is_xcuitest?
68+
return 'Set x, y or element' if (x.nil? || y.nil?) && element.nil?
69+
70+
args = element.nil? ? { x: x, y: y } : { element: element.ref }
71+
execute_script 'mobile: doubleTap', args
72+
end
73+
74+
# @param [Element] :element Element to long tap on.
75+
#
76+
# ```ruby
77+
# two_finger_tap element: find_element(:accessibility_id, "some item")
78+
# ```
79+
def two_finger_tap(element:)
80+
return 'require XCUITest(WDA)' unless automation_name_is_xcuitest?
81+
82+
args = { element: element.ref }
83+
execute_script 'mobile: twoFingerTap', args
84+
end
85+
86+
# @param x [float] X tap coordinate of type float. Mandatory parameter
87+
# @param y [float] Y tap coordinate of type float. Mandatory parameter
88+
# @option opts [Element] :element Element id to long tap on. x and y tap coordinates will be calculated
89+
# relatively to the current element position on the screen if this argument is provided.
90+
# Otherwise they should be calculated relatively to screen borders.
91+
#
92+
# ```ruby
93+
# tap x: 100, y: 100
94+
# tap x: 100, y: 100, element: find_element(:accessibility_id, "some item")
95+
# ```
96+
def tap(x:, y:, element: nil)
97+
return 'require XCUITest(WDA)' unless automation_name_is_xcuitest?
98+
99+
args = { x: x, y: y }
100+
args[:element] = element.ref
101+
execute_script 'mobile: tap', args
102+
end
103+
104+
# rubocop:disable Metrics/ParameterLists
105+
# @param duration [float] Float number of seconds in range [0.5, 60]. How long the tap gesture at starting
106+
# drag point should be before to start dragging. Mandatory parameter
107+
# @param from_x [float] The x coordinate of starting drag point (type float). Mandatory parameter
108+
# @param from_y [float] The y coordinate of starting drag point (type float). Mandatory parameter
109+
# @param to_x [float] The x coordinate of ending drag point (type float). Mandatory parameter
110+
# @param to_y [float] The y coordinate of ending drag point (type float). Mandatory parameter
111+
# @option opts [Element] :element Element id to perform drag on. All the coordinates will be calculated
112+
# relatively this this element position on the screen. Absolute screen coordinates are expected
113+
# if this argument is not set
114+
#
115+
# ```ruby
116+
# drag_from_to_for_duration from_x: 100, from_y: 100, to_x: 150, to_y: 150
117+
# ```
118+
def drag_from_to_for_duration(from_x:, from_y:, to_x:, to_y:, duration: 1.0, element: nil)
119+
return 'require XCUITest(WDA)' unless automation_name_is_xcuitest?
120+
121+
args = { from_x: from_x, from_y: from_y, to_x: to_x, to_y: to_y, duration: duration }
122+
args[:element] = element if element
123+
execute_script 'mobile: dragFromToForDuration', args
124+
end
125+
# rubocop:enable Metrics/ParameterLists
126+
127+
# https://github.com/facebook/WebDriverAgent/pull/523
128+
# https://github.com/appium/appium-xcuitest-driver/pull/420
129+
# @param order [String] The order to move picker to. "next" or "previous".
130+
# @param element [Element] Element id to perform select picker wheel on.
131+
#
132+
# ```ruby
133+
# select_picker_wheel order: "next", element: find_element(:accessibility_id, "some picker")
134+
# ```
135+
def select_picker_wheel(element:, order:)
136+
return 'require XCUITest(WDA)' unless automation_name_is_xcuitest?
137+
return 'Set "next" or "previous" for :order' unless %w(next previous).include?(order)
138+
139+
args = { element: element.ref, order: order }
140+
execute_script 'mobile: selectPickerWheelValue', args
141+
end
142+
end # module XcuitestGesture
143+
end # module Ios
144+
end # module Appium

0 commit comments

Comments
 (0)