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.
- checksums.yaml +7 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +105 -0
- data/LICENSE.md +27 -0
- data/README.md +1795 -0
- data/lib/testcentricity_mobile/app_core/appium_connect_helper.rb +634 -0
- data/lib/testcentricity_mobile/app_core/screen_object.rb +413 -0
- data/lib/testcentricity_mobile/app_core/screen_objects_helper.rb +183 -0
- data/lib/testcentricity_mobile/app_core/screen_section.rb +607 -0
- data/lib/testcentricity_mobile/app_elements/alert.rb +152 -0
- data/lib/testcentricity_mobile/app_elements/app_element.rb +692 -0
- data/lib/testcentricity_mobile/app_elements/button.rb +10 -0
- data/lib/testcentricity_mobile/app_elements/checkbox.rb +56 -0
- data/lib/testcentricity_mobile/app_elements/image.rb +10 -0
- data/lib/testcentricity_mobile/app_elements/label.rb +10 -0
- data/lib/testcentricity_mobile/app_elements/list.rb +175 -0
- data/lib/testcentricity_mobile/app_elements/switch.rb +66 -0
- data/lib/testcentricity_mobile/app_elements/textfield.rb +51 -0
- data/lib/testcentricity_mobile/appium_server.rb +71 -0
- data/lib/testcentricity_mobile/data_objects/data_objects_helper.rb +100 -0
- data/lib/testcentricity_mobile/data_objects/environment.rb +416 -0
- data/lib/testcentricity_mobile/exception_queue_helper.rb +160 -0
- data/lib/testcentricity_mobile/utility_helpers.rb +48 -0
- data/lib/testcentricity_mobile/version.rb +3 -0
- data/lib/testcentricity_mobile/world_extensions.rb +61 -0
- data/lib/testcentricity_mobile.rb +99 -0
- metadata +317 -0
@@ -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
|