testcentricity_mobile 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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