testcentricity_mobile 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,413 @@
1
+ require 'test/unit'
2
+
3
+ module TestCentricity
4
+ class ScreenObject < BaseScreenSectionObject
5
+ include Test::Unit::Assertions
6
+
7
+ attr_reader :locator
8
+
9
+ def initialize
10
+ raise "Screen object #{self.class.name} does not have a screen_name trait defined" unless defined?(screen_name)
11
+ @locator = screen_locator if defined?(screen_locator)
12
+ end
13
+
14
+ # Declare and instantiate a single generic UI Element for this screen object.
15
+ #
16
+ # @param element_name [Symbol] name of UI object (as a symbol)
17
+ # @param locator [Hash] { locator_strategy: locator_identifier }
18
+ # The locator_strategy (a Symbol) specifies the selector strategy that Appium will use to find the UI element. Valid
19
+ # selectors are accessibility_id:, id:, name:, class:, xpath:, predicate: (iOS only), class_chain: (iOS only), and
20
+ # css: (WebViews in hybrid apps only).
21
+ # * The locator_identifier (a String) is the value or attribute that uniquely and unambiguously identifies the UI element.
22
+ #
23
+ # @example
24
+ # element :video_player, { accessibility_id: 'YouTube Video Player' }
25
+ #
26
+ def self.element(element_name, locator)
27
+ define_screen_element(element_name, TestCentricity::AppElements::AppUIElement, locator)
28
+ end
29
+
30
+ # Declare and instantiate a collection of generic UI Elements for this screen object.
31
+ #
32
+ # @param element_hash [Hash] names of UI objects (as a Symbol) and locator Hash
33
+ # @example
34
+ # elements drop_down_field: { accessibility_id: 'drop_trigger' },
35
+ # settings_item: { accessibility_id: 'settings' },
36
+ # video_player: { accessibility_id: 'YouTube Video Player' }
37
+ #
38
+ def self.elements(element_hash)
39
+ element_hash.each do |element_name, locator|
40
+ element(element_name, locator)
41
+ end
42
+ end
43
+
44
+ # Declare and instantiate a single button UI Element for this screen object.
45
+ #
46
+ # @param element_name [Symbol] name of button object (as a symbol)
47
+ # @param locator [Hash] { locator_strategy: locator_identifier }
48
+ # @example
49
+ # button :video_play, { accessibility_id: 'video icon play' }
50
+ #
51
+ def self.button(element_name, locator)
52
+ define_screen_element(element_name, TestCentricity::AppElements::AppButton, locator)
53
+ end
54
+
55
+ # Declare and instantiate a collection of buttons for this screen object.
56
+ #
57
+ # @param element_hash [Hash] names of buttons (as symbol) and locator Hash
58
+ # @example
59
+ # buttons video_back: { accessibility_id: 'video icon backward' },
60
+ # video_play: { accessibility_id: 'video icon play' },
61
+ # video_pause: { accessibility_id: 'video icon stop' },
62
+ # video_forward: { accessibility_id: 'video icon forward' }
63
+ #
64
+ def self.buttons(element_hash)
65
+ element_hash.each do |element_name, locator|
66
+ button(element_name, locator)
67
+ end
68
+ end
69
+
70
+ # Declare and instantiate a single textfield UI Element for this screen object.
71
+ #
72
+ # @param element_name [Symbol] name of textfield object (as a symbol)
73
+ # @param locator [Hash] { locator_strategy: locator_identifier }
74
+ # @example
75
+ # textfield :payee_name_field, { xpath: '//android.widget.EditText[@content-desc="Full Name* input field"]' }
76
+ # textfield :payee_name_field, { xpath: '//XCUIElementTypeTextField[@name="Full Name* input field"]' }
77
+ #
78
+ def self.textfield(element_name, locator)
79
+ define_screen_element(element_name, TestCentricity::AppElements::AppTextField, locator)
80
+ end
81
+
82
+ # Declare and instantiate a collection of textfields for this screen object.
83
+ #
84
+ # @param element_hash [Hash] names of textfields (as symbol) and locator Hash
85
+ # @example
86
+ # textfields username_field: { accessibility_id: 'Username input field' },
87
+ # password_field: { accessibility_id: 'Password input field' }
88
+ #
89
+ def self.textfields(element_hash)
90
+ element_hash.each do |element_name, locator|
91
+ textfield(element_name, locator)
92
+ end
93
+ end
94
+
95
+ # Declare and instantiate a single switch UI Element for this screen object.
96
+ #
97
+ # @param element_name [Symbol] name of switch object (as a symbol)
98
+ # @param locator [Hash] { locator_strategy: locator_identifier }
99
+ # @example
100
+ # switch :debug_mode_switch, { accessibility_id: 'debug mode' }
101
+ #
102
+ def self.switch(element_name, locator)
103
+ define_screen_element(element_name, TestCentricity::AppElements::AppSwitch, locator)
104
+ end
105
+
106
+ # Declare and instantiate a collection of switches for this screen object.
107
+ #
108
+ # @param element_hash [Hash] names of switches (as symbol) and locator Hash
109
+ # @example
110
+ # switches debug_mode_switch: { accessibility_id: 'debug mode' },
111
+ # metrics_switch: { accessibility_id: 'metrics' }
112
+ #
113
+ def self.switches(element_hash)
114
+ element_hash.each do |element_name, locator|
115
+ switch(element_name, locator)
116
+ end
117
+ end
118
+
119
+ # Declare and instantiate a single checkbox UI Element for this screen object.
120
+ #
121
+ # @param element_name [Symbol] name of checkbox object (as a symbol)
122
+ # @param locator [Hash] { locator_strategy: locator_identifier }
123
+ # @example
124
+ # checkbox :bill_address_check, { xpath: '//XCUIElementTypeOther[contains(@name, "billing checkbox")]'}
125
+ #
126
+ def self.checkbox(element_name, locator)
127
+ define_screen_element(element_name, TestCentricity::AppElements::AppCheckBox, locator)
128
+ end
129
+
130
+ # Declare and instantiate a collection of checkboxes for this screen object.
131
+ #
132
+ # @param element_hash [Hash] names of checkboxes (as symbol) and locator Hash
133
+ # @example
134
+ # checkboxes bill_address_check: { xpath: '//XCUIElementTypeOther[contains(@name, "billing checkbox")]'},
135
+ # is_gift_check: { accessibility_id: 'is a gift' }
136
+ #
137
+ def self.checkboxes(element_hash)
138
+ element_hash.each do |element_name, locator|
139
+ checkbox(element_name, locator)
140
+ end
141
+ end
142
+
143
+ # Declare and instantiate a single label UI Element for this screen object.
144
+ #
145
+ # @param element_name [Symbol] name of label object (as a symbol)
146
+ # @param locator [Hash] { locator_strategy: locator_identifier }
147
+ # @example
148
+ # label :header_label, { accessibility_id: 'container header' }
149
+ #
150
+ def self.label(element_name, locator)
151
+ define_screen_element(element_name, TestCentricity::AppElements::AppLabel, locator)
152
+ end
153
+
154
+ # Declare and instantiate a collection of labels for this screen object.
155
+ #
156
+ # @param element_hash [Hash] names of labels (as symbol) and locator Hash
157
+ # @example
158
+ # labels total_qty_value: { accessibility_id: 'total number' },
159
+ # total_price_value: { accessibility_id: 'total price' }
160
+ #
161
+ def self.labels(element_hash)
162
+ element_hash.each do |element_name, locator|
163
+ label(element_name, locator)
164
+ end
165
+ end
166
+
167
+ # Declare and instantiate a single list UI Element for this screen object.
168
+ #
169
+ # @param element_name [Symbol] name of list object (as a symbol)
170
+ # @param locator [Hash] { locator_strategy: locator_identifier }
171
+ # @example
172
+ # list :carousel_list, { accessibility_id: 'Carousel' }
173
+ #
174
+ def self.list(element_name, locator)
175
+ define_screen_element(element_name, TestCentricity::AppElements::AppList, locator)
176
+ end
177
+
178
+ # Declare and instantiate a collection of lists for this screen object.
179
+ #
180
+ # @param element_hash [Hash] names of lists (as symbol) and locator Hash
181
+ # @example
182
+ # lists product_grid: { xpath: '//android.widget.ScrollView/android.view.ViewGroup' },
183
+ # cart_list: { xpath: '//android.widget.ScrollView[@content-desc="cart screen"]' }
184
+ #
185
+ def self.lists(element_hash)
186
+ element_hash.each do |element_name, locator|
187
+ list(element_name, locator)
188
+ end
189
+ end
190
+
191
+ # Declare and instantiate a single image UI Element for this screen object.
192
+ #
193
+ # @param element_name [Symbol] name of image object (as a symbol)
194
+ # @param locator [Hash] { locator_strategy: locator_identifier }
195
+ # @example
196
+ # image :product_image, { xpath: '//XCUIElementTypeImage' }
197
+ #
198
+ def self.image(element_name, locator)
199
+ define_screen_element(element_name, TestCentricity::AppElements::AppImage, locator)
200
+ end
201
+
202
+ # Declare and instantiate a collection of images for this screen object.
203
+ #
204
+ # @param element_hash [Hash] names of images (as symbol) and locator Hash
205
+ # @example
206
+ # images empty_cart_image: { accessibility_id: 'empty_cart' },
207
+ # logo_image: { accessibility_id: 'WebdriverIO logo' }
208
+ #
209
+ def self.images(element_hash)
210
+ element_hash.each do |element_name, locator|
211
+ image(element_name, locator)
212
+ end
213
+ end
214
+
215
+ # Declare and instantiate a single alert UI Element for this screen object.
216
+ #
217
+ # @param element_name [Symbol] name of alert object (as a symbol)
218
+ # @param locator [Hash] { locator_strategy: locator_identifier }
219
+ # @example
220
+ # alert :generic_alert_modal, { id: 'android:id/parentPanel' }
221
+ # alert :generic_alert_modal, { class: 'XCUIElementTypeAlert' }
222
+ #
223
+ def self.alert(element_name, locator)
224
+ define_screen_element(element_name, TestCentricity::AppElements::AppAlert, locator)
225
+ end
226
+
227
+ # Declare and instantiate a collection of alerts for this screen object.
228
+ #
229
+ # @param element_hash [Hash] names of alerts (as symbol) and locator Hash
230
+ # @example
231
+ # alerts grant_modal: { id: 'com.android.permissioncontroller:id/grant_dialog' },
232
+ # alert_modal: { id: 'android:id/parentPanel' }
233
+ #
234
+ def self.alerts(element_hash)
235
+ element_hash.each do |element_name, locator|
236
+ alert(element_name, locator)
237
+ end
238
+ end
239
+
240
+ # Instantiate a single ScreenSection object within this ScreenObject.
241
+ #
242
+ # @param section_name [Symbol] name of ScreenSection object (as a symbol)
243
+ # @param class_name [Class] Class name of ScreenSection object
244
+ # @example
245
+ # section :nav_menu, NavMenu
246
+ #
247
+ def self.section(section_name, obj, locator = 0)
248
+ define_screen_element(section_name, obj, locator)
249
+ end
250
+
251
+ # Declare and instantiate a collection of ScreenSection objects for this screen object.
252
+ #
253
+ # @param element_hash [Hash] names of ScreenSections (as symbol) and class name
254
+ # @example
255
+ # sections nav_bar: NavBar,
256
+ # nav_menu: NavMenu
257
+ #
258
+ def self.sections(section_hash)
259
+ section_hash.each do |section_name, class_name|
260
+ section(section_name, class_name)
261
+ end
262
+ end
263
+
264
+ # Does Screen object exists?
265
+ #
266
+ # @return [Boolean]
267
+ # @example
268
+ # home_screen.exists?
269
+ #
270
+ def exists?
271
+ @locator.is_a?(Array) ? tries ||= 2 : tries ||= 1
272
+ if @locator.is_a?(Array)
273
+ loc = @locator[tries - 1]
274
+ find_element(loc.keys[0], loc.values[0])
275
+ else
276
+ find_element(@locator.keys[0], @locator.values[0])
277
+ end
278
+ true
279
+ rescue
280
+ retry if (tries -= 1) > 0
281
+ false
282
+ end
283
+
284
+ # Wait until the Screen object exists, or until the specified wait time has expired. If the wait time is nil, then
285
+ # the wait time will be Environ.default_max_wait_time.
286
+ #
287
+ # @param seconds [Integer or Float] wait time in seconds
288
+ # @example
289
+ # cart_screen.wait_until_exists(15)
290
+ #
291
+ def wait_until_exists(seconds = nil, post_exception = true)
292
+ timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
293
+ wait = Selenium::WebDriver::Wait.new(timeout: timeout)
294
+ wait.until { exists? }
295
+ rescue
296
+ if post_exception
297
+ raise "Screen object #{self.class.name} not found after #{timeout} seconds" unless exists?
298
+ else
299
+ exists?
300
+ end
301
+ end
302
+
303
+ # Wait until the Screen object is gone, or until the specified wait time has expired. If the wait time is nil, then
304
+ # the wait time will be Environ.default_max_wait_time.
305
+ #
306
+ # @param seconds [Integer or Float] wait time in seconds
307
+ # @example
308
+ # login_screen.wait_until_gone(15)
309
+ #
310
+ def wait_until_gone(seconds = nil, post_exception = true)
311
+ timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
312
+ wait = Selenium::WebDriver::Wait.new(timeout: timeout)
313
+ wait.until { !exists? }
314
+ rescue
315
+ if post_exception
316
+ raise "Screen object #{self.class.name} remained visible after #{timeout} seconds" if exists?
317
+ else
318
+ exists?
319
+ end
320
+ end
321
+
322
+ def navigate_to; end
323
+
324
+ def verify_screen_ui; end
325
+
326
+ # Verifies that the target screen is displayed, and sets ScreenManager.current_screen to reference the target screen
327
+ # instance.
328
+ #
329
+ def verify_screen_exists
330
+ wait = Selenium::WebDriver::Wait.new(timeout: Environ.default_max_wait_time)
331
+ wait.until { exists? }
332
+ ScreenManager.current_screen = self
333
+ rescue
334
+ raise "Could not find screen_locator for screen object '#{self.class.name}' (#{@locator}) after #{Environ.default_max_wait_time} seconds"
335
+ end
336
+
337
+ # Load the screen using its defined deep_link trait. When testing on physical iOS devices running iOS/iPadOS versions
338
+ # earlier than version 16.4, deep links can only be opened by sending the deeplink URL to the mobile Safari web browser,
339
+ # and then accepting the confirmation modal that pops up. This method handles invoking deeplinks on Android and iOS/iPadOS
340
+ # simulators and physical devices.
341
+ #
342
+ # This method verifies that the target screen is loaded and displayed, and sets ScreenManager.current_screen to reference
343
+ # the target screen instance.
344
+ #
345
+ # @example
346
+ # cart_screen.load_screen
347
+ #
348
+ def load_screen
349
+ # return if target screen is already loaded
350
+ if exists?
351
+ ScreenManager.current_screen = self
352
+ return
353
+ end
354
+
355
+ url = if deep_link.include?("://")
356
+ deep_link
357
+ elsif !Environ.current.deep_link_prefix.blank?
358
+ link_url = "#{Environ.current.deep_link_prefix}://#{deep_link}"
359
+ # deeplink handler for Android devices
360
+ if Environ.is_android?
361
+ Environ.appium_driver.execute_script('mobile:deepLink', { url: link_url, package: Environ.current.android_app_id })
362
+ verify_screen_exists
363
+ return
364
+ else
365
+ link_url
366
+ end
367
+ end
368
+ # deeplink handler for iOS devices
369
+ if Environ.is_ios? && Environ.is_device? && Environ.device_os_version.to_f < 16.4
370
+ # launch Safari browser on iOS real device if iOS version is below 16.4
371
+ Environ.appium_driver.execute_script('mobile: launchApp', { bundleId: 'com.apple.mobilesafari' })
372
+ unless Environ.appium_driver.is_keyboard_shown
373
+ begin
374
+ # attempt to find and click URL button on iOS 15 Safari browser
375
+ find_element(:accessibility_id, 'TabBarItemTitle').click
376
+ rescue
377
+ # fall back to URL button on iOS 14 Safari browser
378
+ find_element(:xpath, '//XCUIElementTypeButton[@name="URL"]').click
379
+ end
380
+ end
381
+ # enter deep-link URL
382
+ wait_for_object(:xpath, '//XCUIElementTypeTextField[@name="URL"]', 5).send_keys("#{url}\uE007")
383
+ # wait for and accept the popup modal
384
+ wait_for_object(:xpath, '//XCUIElementTypeButton[@name="Open"]', 10).click
385
+ else
386
+ # iOS version is >= 16.4 so directly load screen via Selenium WebDriver
387
+ Environ.appium_driver.get(url)
388
+ end
389
+ verify_screen_exists
390
+ end
391
+
392
+ private
393
+
394
+ def self.define_screen_element(element_name, obj, locator)
395
+ define_method(element_name) do
396
+ ivar_name = "@#{element_name}"
397
+ ivar = instance_variable_get(ivar_name)
398
+ return ivar if ivar
399
+ instance_variable_set(ivar_name, obj.new(element_name, self, locator, :screen))
400
+ end
401
+ end
402
+
403
+ def wait_for_object(find_method, locator, seconds)
404
+ wait = Selenium::WebDriver::Wait.new(timeout: seconds)
405
+ obj = nil
406
+ wait.until do
407
+ obj = find_element(find_method, locator)
408
+ obj.displayed?
409
+ end
410
+ obj
411
+ end
412
+ end
413
+ end
@@ -0,0 +1,183 @@
1
+ require 'test/unit'
2
+
3
+ module TestCentricity
4
+ class BaseScreenSectionObject
5
+ # Define a trait for this screen or section object.
6
+ #
7
+ # Refer to the `Adding Traits to your ScreenObject` and `Adding Traits to a ScreenSection` sections of the ruby docs
8
+ # for this gem.
9
+ #
10
+ # @param trait_name [Symbol] name of trait (as a symbol)
11
+ # @param block [&block] trait value
12
+ # @example
13
+ # trait(:screen_name) { 'Shopping Basket' }
14
+ # trait(:screen_locator) { accessibility_id: 'My Contacts View' }
15
+ # trait(:deep_link) { 'geo-locations' }
16
+ # trait(:section_name) { 'Cart List Item' }
17
+ # trait(:section_locator) { xpath: '(//XCUIElementTypeOther[@name="product row"])' }
18
+ #
19
+ def self.trait(trait_name, &block)
20
+ define_method(trait_name.to_s, &block)
21
+ end
22
+
23
+ # Populate the specified UI elements on this screen or section object with the associated data from a Hash passed as
24
+ # an argument. Data values must be in the form of a String for textfield controls. For checkboxes and switches, data
25
+ # must either be a Boolean or a String that evaluates to a Boolean value (Yes, No, 1, 0, true, false). For screen section
26
+ # objects, data values must be a String, and the screen section object must have a set method defined.
27
+ #
28
+ # The optional wait_time parameter is used to specify the time (in seconds) to wait for each UI element to become
29
+ # visible before entering the associated data value. This option is useful in situations where entering data, or
30
+ # setting the state of a UI element might cause other UI elements to become visible or active. Specifying a wait_time
31
+ # value ensures that the subsequent UI elements will be ready to be interacted with as states are changed. If the wait
32
+ # time is nil, then the wait time will be 5 seconds.
33
+ #
34
+ # To delete all text content in a text field, pass !DELETE as the data to be entered.
35
+ #
36
+ # If any of the specified UI elements are not currently visible, an attempt will be made to scroll the object in view.
37
+ #
38
+ # Refer to the `Populating your ScreenObject or ScreenSection with data` section of the ruby docs for this gem.
39
+ #
40
+ # @param data [Hash] UI element(s) and associated data to be entered
41
+ # @param wait_time [Integer] wait time in seconds
42
+ # @example
43
+ # fields = {
44
+ # payee_name_field => UserData.current.cardholder_name,
45
+ # card_number_field => UserData.current.card_num,
46
+ # expiration_field => UserData.current.expiry,
47
+ # security_code_field => UserData.current.cvv
48
+ # }
49
+ # populate_data_fields(fields)
50
+ def populate_data_fields(data, wait_time = nil)
51
+ timeout = wait_time.nil? ? 2 : wait_time
52
+ data.each do |data_field, data_param|
53
+ unless data_param.blank?
54
+ # make sure the intended UI target element is visible before trying to set its value
55
+ data_field.scroll_into_view unless data_field.wait_until_visible(timeout, post_exception = false)
56
+ if data_param == '!DELETE'
57
+ data_field.clear
58
+ else
59
+ case data_field.get_object_type
60
+ when :checkbox
61
+ data_field.set_checkbox_state(data_param.to_bool)
62
+ when :textfield
63
+ data_field.clear
64
+ data_field.set(data_param)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ # Verify one or more properties of one or more UI elements on a ScreenObject or ScreenSection. This method accepts
72
+ # a hash containing key/hash pairs of UI elements and their properties or attributes to be verified.
73
+ #
74
+ # Refer to the `Verifying AppUIElements on your ScreenObject or ScreenSection` section of the ruby docs for this gem.
75
+ #
76
+ # @param ui_states [Hash] UI element(s) and associated properties to be validated
77
+ # @param auto_scroll [Boolean] automatically scroll UI elements that are expected to be visible into view (default = true)
78
+ #
79
+ def verify_ui_states(ui_states, auto_scroll = true)
80
+ ui_states.each do |ui_object, object_states|
81
+ object_states.each do |property, state|
82
+ actual = case property
83
+ when :visible
84
+ if auto_scroll && state
85
+ ui_object.scroll_into_view if ui_object.hidden?
86
+ end
87
+ ui_object.visible?
88
+ when :class
89
+ ui_object.get_attribute(:class)
90
+ when :exists
91
+ ui_object.exists?
92
+ when :enabled
93
+ ui_object.enabled?
94
+ when :disabled
95
+ ui_object.disabled?
96
+ when :hidden
97
+ ui_object.hidden?
98
+ when :checked
99
+ ui_object.checked?
100
+ when :selected
101
+ ui_object.selected?
102
+ when :value
103
+ ui_object.get_value
104
+ when :caption
105
+ ui_object.get_caption
106
+ when :placeholder
107
+ ui_object.get_placeholder
108
+ when :readonly
109
+ ui_object.read_only?
110
+ when :maxlength
111
+ ui_object.get_max_length
112
+ when :items
113
+ ui_object.get_list_items
114
+ when :itemcount
115
+ ui_object.get_item_count
116
+ when :width
117
+ ui_object.width
118
+ when :height
119
+ ui_object.height
120
+ when :x
121
+ ui_object.x_loc
122
+ when :y
123
+ ui_object.y_loc
124
+ when :count
125
+ ui_object.count
126
+ when :buttons
127
+ ui_object.buttons
128
+
129
+ end
130
+ error_msg = "Expected UI object '#{ui_object.get_name}' (#{ui_object.get_locator}) #{property} property to"
131
+ ExceptionQueue.enqueue_comparison(ui_object, state, actual, error_msg)
132
+ end
133
+ end
134
+ rescue ObjectNotFoundError => e
135
+ ExceptionQueue.enqueue_exception(e.message)
136
+ ensure
137
+ ExceptionQueue.post_exceptions
138
+ end
139
+
140
+ # Perform a swipe gesture in the specified direction. The swipe start point is the center of the screen, and the
141
+ # swipe end point is the distance specified. A distance of 1 specifies a swipe gesture with a distance that is the
142
+ # full screen height (vertical swipe), or full screen width (horizontal swipe). A distance of 0.5 specifies a swipe
143
+ # gesture with a distance that is half the screen width or height.
144
+ #
145
+ # @param direction [Symbol] :up, :down, :left, or :right
146
+ # @param distance [Float] scroll distance relative to the screen height or width
147
+ # @example
148
+ # swipe_gesture(direction = :down, distance = 1)
149
+ #
150
+ def swipe_gesture(direction, distance = 0.5)
151
+ raise 'Scroll distance must be between 0 and 1' if (distance < 0 || distance > 1)
152
+ size = window_size
153
+ mid_pt = [(size.width * 0.5).to_i, (size.height * 0.5).to_i]
154
+ top = (mid_pt[1] - ((size.height * distance) * 0.5)).to_i
155
+ bottom = (mid_pt[1] + ((size.height * distance) * 0.5)).to_i
156
+ left = (mid_pt[0] - ((size.width * distance) * 0.5)).to_i
157
+ right = (mid_pt[0] + ((size.width * distance) * 0.5)).to_i
158
+
159
+ case direction
160
+ when :up
161
+ start_pt = [mid_pt[0], top]
162
+ end_pt = [mid_pt[0], bottom]
163
+ when :down
164
+ start_pt = [mid_pt[0], bottom]
165
+ end_pt = [mid_pt[0], top]
166
+ when :left
167
+ start_pt = [left, mid_pt[1]]
168
+ end_pt = [right, mid_pt[1]]
169
+ when :right
170
+ start_pt = [right, mid_pt[1]]
171
+ end_pt = [left, mid_pt[1]]
172
+ end
173
+
174
+ puts "Swipe start_pt = #{start_pt} / end_pt = #{end_pt}" if ENV['DEBUG']
175
+ driver.action
176
+ .move_to_location(start_pt[0], start_pt[1])
177
+ .pointer_down
178
+ .move_to_location(end_pt[0], end_pt[1], duration: 0.25)
179
+ .release
180
+ .perform
181
+ end
182
+ end
183
+ end