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,607 @@
1
+ require 'test/unit'
2
+
3
+ module TestCentricity
4
+ class ScreenSection < BaseScreenSectionObject
5
+ include Test::Unit::Assertions
6
+
7
+ attr_reader :context, :name
8
+ attr_accessor :locator
9
+ attr_accessor :parent
10
+ attr_accessor :parent_list
11
+ attr_accessor :list_index
12
+
13
+ def initialize(name, parent, locator, context)
14
+ @name = name
15
+ @parent = parent
16
+ @locator = locator
17
+ @context = context
18
+ @parent_list = nil
19
+ @list_index = nil
20
+ end
21
+
22
+ def get_locator
23
+ if @locator.zero? && defined?(section_locator)
24
+ my_locator = section_locator
25
+ else
26
+ my_locator = @locator
27
+ end
28
+ locators = []
29
+ if @context == :section && !@parent.nil?
30
+ locators.push(@parent.get_locator)
31
+ end
32
+
33
+ if @parent_list.nil?
34
+ locators.push(my_locator)
35
+ else
36
+ locators.push(@parent_list.get_locator)
37
+ if @list_index.nil?
38
+ locators.push(my_locator)
39
+ else
40
+ item_objects = @parent_list.item_refs
41
+ if item_objects.nil?
42
+ list_key = my_locator.keys[0]
43
+ list_value = "#{my_locator.values[0]}[#{@list_index}]"
44
+ else
45
+ list_key = :object
46
+ list_value = item_objects[@list_index - 1]
47
+ end
48
+ locators.push( { list_key => list_value } )
49
+ end
50
+ end
51
+ locators
52
+ end
53
+
54
+ def set_list_index(list, index = 1)
55
+ @parent_list = list unless list.nil?
56
+ @list_index = index
57
+ end
58
+
59
+ def get_item_count
60
+ raise 'No parent list defined' if @parent_list.nil?
61
+ @parent_list.get_item_count
62
+ end
63
+
64
+ def get_list_items
65
+ items = []
66
+ (1..get_item_count).each do |item|
67
+ set_list_index(nil, item)
68
+ begin
69
+ items.push(get_value)
70
+ rescue
71
+ scroll_into_view(@parent_list.scroll_mode)
72
+ items.push(get_value)
73
+ end
74
+ end
75
+ items
76
+ end
77
+
78
+ def get_object_type
79
+ :section
80
+ end
81
+
82
+ def get_name
83
+ @name
84
+ end
85
+
86
+ def set_parent(parent)
87
+ @parent = parent
88
+ end
89
+
90
+ # Declare and instantiate a single generic UI Element for this screen section object.
91
+ #
92
+ # @param element_name [Symbol] name of UI object (as a symbol)
93
+ # @param locator [Hash] { locator_strategy: locator_identifier }
94
+ # The locator_strategy (a Symbol) specifies the selector strategy that Appium will use to find the UI element. Valid
95
+ # selectors are accessibility_id:, id:, name:, class:, xpath:, predicate: (iOS only), class_chain: (iOS only), and
96
+ # css: (WebViews in hybrid apps only).
97
+ # * The locator_identifier (a String) is the value or attribute that uniquely and unambiguously identifies the UI element.
98
+ #
99
+ # @example
100
+ # element :video_player, { accessibility_id: 'YouTube Video Player' }
101
+ #
102
+ def self.element(element_name, locator)
103
+ define_section_element(element_name, TestCentricity::AppElements::AppUIElement, locator)
104
+ end
105
+
106
+ # Declare and instantiate a collection of generic UI Elements for this screen section object.
107
+ #
108
+ # @param element_hash [Hash] names of UI objects (as a Symbol) and locator Hash
109
+ # @example
110
+ # elements drop_down_field: { accessibility_id: 'drop_trigger' },
111
+ # settings_item: { accessibility_id: 'settings' },
112
+ # log_out_item: { accessibility_id: 'logout' }
113
+ #
114
+ def self.elements(element_hash)
115
+ element_hash.each_pair { |element_name, locator| element(element_name, locator) }
116
+ end
117
+
118
+ # Declare and instantiate a single button UI Element for this screen section object.
119
+ #
120
+ # @param element_name [Symbol] name of button object (as a symbol)
121
+ # @param locator [Hash] { locator_strategy: locator_identifier }
122
+ # @example
123
+ # button :video_play, { accessibility_id: 'video icon play' }
124
+ #
125
+ def self.button(element_name, locator)
126
+ define_section_element(element_name, TestCentricity::AppElements::AppButton, locator)
127
+ end
128
+
129
+ # Declare and instantiate a collection of buttons for this screen section object.
130
+ #
131
+ # @param element_hash [Hash] names of buttons (as symbol) and locator Hash
132
+ # @example
133
+ # buttons video_back: { accessibility_id: 'video icon backward' },
134
+ # video_play: { accessibility_id: 'video icon play' },
135
+ # video_pause: { accessibility_id: 'video icon stop' },
136
+ # video_forward: { accessibility_id: 'video icon forward' }
137
+ #
138
+ def self.buttons(element_hash)
139
+ element_hash.each_pair { |element_name, locator| button(element_name, locator) }
140
+ end
141
+
142
+ # Declare and instantiate a single textfield UI Element for this screen section object.
143
+ #
144
+ # @param element_name [Symbol] name of textfield object (as a symbol)
145
+ # @param locator [Hash] { locator_strategy: locator_identifier }
146
+ # @example
147
+ # textfield :payee_name_field, { xpath: '//android.widget.EditText[@content-desc="Full Name* input field"]' }
148
+ # textfield :payee_name_field, { xpath: '//XCUIElementTypeTextField[@name="Full Name* input field"]' }
149
+ #
150
+ def self.textfield(element_name, locator)
151
+ define_section_element(element_name, TestCentricity::AppElements::AppTextField, locator)
152
+ end
153
+
154
+ # Declare and instantiate a collection of textfields for this screen section object.
155
+ #
156
+ # @param element_hash [Hash] names of textfields (as symbol) and locator Hash
157
+ # @example
158
+ # textfields username_field: { accessibility_id: 'Username input field' },
159
+ # password_field: { accessibility_id: 'Password input field' }
160
+ #
161
+ def self.textfields(element_hash)
162
+ element_hash.each_pair { |element_name, locator| textfield(element_name, locator) }
163
+ end
164
+
165
+ # Declare and instantiate a single switch UI Element for this screen section object.
166
+ #
167
+ # @param element_name [Symbol] name of switch object (as a symbol)
168
+ # @param locator [Hash] { locator_strategy: locator_identifier }
169
+ # @example
170
+ # switch :debug_mode_switch, { accessibility_id: 'debug mode' }
171
+ #
172
+ def self.switch(element_name, locator)
173
+ define_section_element(element_name, TestCentricity::AppElements::AppSwitch, locator)
174
+ end
175
+
176
+ # Declare and instantiate a collection of switches for this screen section object.
177
+ #
178
+ # @param element_hash [Hash] names of switches (as symbol) and locator Hash
179
+ # @example
180
+ # switches debug_mode_switch: { accessibility_id: 'debug mode' },
181
+ # metrics_switch: { accessibility_id: 'metrics' }
182
+ #
183
+ def self.switches(element_hash)
184
+ element_hash.each_pair { |element_name, locator| switch(element_name, locator) }
185
+ end
186
+
187
+ # Declare and instantiate a single checkbox UI Element for this screen section object.
188
+ #
189
+ # @param element_name [Symbol] name of checkbox object (as a symbol)
190
+ # @param locator [Hash] { locator_strategy: locator_identifier }
191
+ # @example
192
+ # checkbox :bill_address_check, { xpath: '//XCUIElementTypeOther[contains(@name, "billing checkbox")]'}
193
+ #
194
+ def self.checkbox(element_name, locator)
195
+ define_section_element(element_name, TestCentricity::AppElements::AppCheckBox, locator)
196
+ end
197
+
198
+ # Declare and instantiate a collection of checkboxes for this screen section object.
199
+ #
200
+ # @param element_hash [Hash] names of checkboxes (as symbol) and locator Hash
201
+ # @example
202
+ # checkboxes bill_address_check: { xpath: '//XCUIElementTypeOther[contains(@name, "billing checkbox")]'},
203
+ # is_gift_check: { accessibility_id: 'is a gift' }
204
+ #
205
+ def self.checkboxes(element_hash)
206
+ element_hash.each_pair { |element_name, locator| checkbox(element_name, locator) }
207
+ end
208
+
209
+ # Declare and instantiate a single label UI Element for this screen section object.
210
+ #
211
+ # @param element_name [Symbol] name of label object (as a symbol)
212
+ # @param locator [Hash] { locator_strategy: locator_identifier }
213
+ # @example
214
+ # label :header_label, { accessibility_id: 'container header' }
215
+ #
216
+ def self.label(element_name, locator)
217
+ define_section_element(element_name, TestCentricity::AppElements::AppLabel, locator)
218
+ end
219
+
220
+ # Declare and instantiate a collection of labels for this screen section object.
221
+ #
222
+ # @param element_hash [Hash] names of labels (as symbol) and locator Hash
223
+ # @example
224
+ # labels total_qty_value: { accessibility_id: 'total number' },
225
+ # total_price_value: { accessibility_id: 'total price' }
226
+ #
227
+ def self.labels(element_hash)
228
+ element_hash.each_pair { |element_name, locator| label(element_name, locator) }
229
+ end
230
+
231
+ # Declare and instantiate a single list UI Element for this screen section object.
232
+ #
233
+ # @param element_name [Symbol] name of list object (as a symbol)
234
+ # @param locator [Hash] { locator_strategy: locator_identifier }
235
+ # @example
236
+ # list :carousel_list, { accessibility_id: 'Carousel' }
237
+ #
238
+ def self.list(element_name, locator)
239
+ define_section_element(element_name, TestCentricity::AppElements::AppList, locator)
240
+ end
241
+
242
+ # Declare and instantiate a collection of lists for this screen section object.
243
+ #
244
+ # @param element_hash [Hash] names of lists (as symbol) and locator Hash
245
+ # @example
246
+ # lists product_grid: { xpath: '//android.widget.ScrollView/android.view.ViewGroup' },
247
+ # cart_list: { xpath: '//android.widget.ScrollView[@content-desc="cart screen"]' }
248
+ #
249
+ def self.lists(element_hash)
250
+ element_hash.each_pair { |element_name, locator| list(element_name, locator) }
251
+ end
252
+
253
+ # Declare and instantiate a single image UI Element for this screen section object.
254
+ #
255
+ # @param element_name [Symbol] name of image object (as a symbol)
256
+ # @param locator [Hash] { locator_strategy: locator_identifier }
257
+ # @example
258
+ # image :product_image, { xpath: '//XCUIElementTypeImage' }
259
+ #
260
+ def self.image(element_name, locator)
261
+ define_section_element(element_name, TestCentricity::AppElements::AppImage, locator)
262
+ end
263
+
264
+ # Declare and instantiate a collection of images for this screen section object.
265
+ #
266
+ # @param element_hash [Hash] names of images (as symbol) and locator Hash
267
+ # @example
268
+ # images empty_cart_image: { accessibility_id: 'empty_cart' },
269
+ # logo_image: { accessibility_id: 'WebdriverIO logo' }
270
+ #
271
+ def self.images(element_hash)
272
+ element_hash.each_pair { |element_name, locator| image(element_name, locator) }
273
+ end
274
+
275
+ # Instantiate a single ScreenSection object within this ScreenSection object.
276
+ #
277
+ # @param section_name [Symbol] name of ScreenSection object (as a symbol)
278
+ # @param class_name [Class] Class name of ScreenSection object
279
+ # @example
280
+ # section :nav_menu, NavMenu
281
+ #
282
+ def self.section(section_name, obj, locator = 0)
283
+ define_section_element(section_name, obj, locator)
284
+ end
285
+
286
+ # Declare and instantiate a collection of ScreenSection objects for this ScreenSection object.
287
+ #
288
+ # @param element_hash [Hash] names of ScreenSections (as symbol) and class name
289
+ # @example
290
+ # sections product_grid_item: ProductGridItem,
291
+ # sort_by_menu: SortByMenu
292
+ #
293
+ def self.sections(section_hash)
294
+ section_hash.each_pair { |section_name, class_name| section(section_name, class_name) }
295
+ end
296
+
297
+ # Click on a screen Section object
298
+ #
299
+ # @example
300
+ # bar_chart_section.click
301
+ #
302
+ def click
303
+ section = find_section
304
+ section_not_found_exception(section)
305
+ section.click
306
+ end
307
+
308
+ # Tap on a screen Section object
309
+ #
310
+ # @example
311
+ # bar_chart_section.tap
312
+ #
313
+ def tap
314
+ section = find_section
315
+ section_not_found_exception(section)
316
+ driver.action
317
+ .click_and_hold(section)
318
+ .release
319
+ .perform
320
+ end
321
+
322
+ # Double-tap on a screen Section object
323
+ #
324
+ # @example
325
+ # bar_chart_section.double_tap
326
+ #
327
+ def double_tap
328
+ section = find_section
329
+ section_not_found_exception(section)
330
+ driver.action
331
+ .click_and_hold(section)
332
+ .release
333
+ .pause(duration: 0.2)
334
+ .click_and_hold(section)
335
+ .release
336
+ .perform
337
+ end
338
+
339
+ # Long press on a screen Section object
340
+ #
341
+ # @param duration [Float] duration of long press in seconds
342
+ # @example
343
+ # header_image.long_press(1.5)
344
+ #
345
+ def long_press(duration = 1)
346
+ section = find_section
347
+ section_not_found_exception(section)
348
+ driver.action
349
+ .click_and_hold(section)
350
+ .pause(duration: duration)
351
+ .release
352
+ .perform
353
+ end
354
+
355
+ # Scroll the screen Section object until it is visible. If scroll_mode is not specified, then vertical scrolling will
356
+ # be used.
357
+ #
358
+ # @param scroll_mode [Symbol] :vertical (default) or :horizontal
359
+ # @example
360
+ # carousel_item.scroll_into_view(scroll_mode = :horizontal)
361
+ #
362
+ def scroll_into_view(scroll_mode = :vertical)
363
+ return if visible?
364
+ obj = element
365
+ object_not_found_exception(obj)
366
+ driver.action.move_to(obj).perform
367
+ case scroll_mode
368
+ when :vertical
369
+ start_direction = :down
370
+ end_direction = :up
371
+ when :horizontal
372
+ start_direction = :right
373
+ end_direction = :left
374
+ else
375
+ raise "#{scroll_mode} is not a valid selector"
376
+ end
377
+ try_count = 8
378
+ direction = start_direction
379
+ while hidden?
380
+ swipe_gesture(direction, distance = 0.2)
381
+ try_count -= 1
382
+ if try_count.zero?
383
+ if direction == end_direction
384
+ break
385
+ else
386
+ direction = end_direction
387
+ try_count = 8
388
+ end
389
+ end
390
+ end
391
+ end
392
+
393
+ # Does screen Section object exists?
394
+ #
395
+ # @return [Boolean]
396
+ # @example
397
+ # navigation_toolbar.exists?
398
+ #
399
+ def exists?
400
+ section = find_section
401
+ section != nil
402
+ end
403
+
404
+ # Is screen Section object enabled?
405
+ #
406
+ # @return [Boolean]
407
+ # @example
408
+ # bar_chart_section.enabled?
409
+ #
410
+ def enabled?
411
+ section = find_section
412
+ section_not_found_exception(section)
413
+ section.enabled?
414
+ end
415
+
416
+ # Is screen Section object disabled (not enabled)?
417
+ #
418
+ # @return [Boolean]
419
+ # @example
420
+ # bar_chart_section.disabled?
421
+ #
422
+ def disabled?
423
+ section = find_section
424
+ section_not_found_exception(section)
425
+ section.enabled?
426
+ end
427
+
428
+ # Is screen Section object visible?
429
+ #
430
+ # @return [Boolean]
431
+ # @example
432
+ # navigation_toolbar.visible?
433
+ #
434
+ def visible?
435
+ section = find_section
436
+ return false if section.nil?
437
+
438
+ section.displayed?
439
+ end
440
+
441
+ # Is screen Section object hidden (not visible)?
442
+ #
443
+ # @return [Boolean]
444
+ # @example
445
+ # navigation_toolbar.hidden?
446
+ #
447
+ def hidden?
448
+ !visible?
449
+ end
450
+
451
+ # Wait until the screen Section object exists, or until the specified wait time has expired. If the wait time is nil,
452
+ # then the wait time will be Environ.default_max_wait_time.
453
+ #
454
+ # @param seconds [Integer or Float] wait time in seconds
455
+ # @example
456
+ # navigation_toolbar.wait_until_exists(1.5)
457
+ #
458
+ def wait_until_exists(seconds = nil, post_exception = true)
459
+ timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
460
+ wait = Selenium::WebDriver::Wait.new(timeout: timeout)
461
+ wait.until { exists? }
462
+ rescue
463
+ if post_exception
464
+ raise "Could not find Section object '#{get_name}' (#{get_locator}) after #{timeout} seconds" unless exists?
465
+ else
466
+ exists?
467
+ end
468
+ end
469
+
470
+ # Wait until the screen Section object no longer exists, or until the specified wait time has expired. If the wait
471
+ # time is nil, then the wait time will be Environ.default_max_wait_time.
472
+ #
473
+ # @param seconds [Integer or Float] wait time in seconds
474
+ # @example
475
+ # navigation_toolbar.wait_until_gone(5)
476
+ #
477
+ def wait_until_gone(seconds = nil, post_exception = true)
478
+ timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
479
+ wait = Selenium::WebDriver::Wait.new(timeout: timeout)
480
+ wait.until { !exists? }
481
+ rescue
482
+ if post_exception
483
+ raise "Section object '#{get_name}' (#{get_locator}) remained visible after #{timeout} seconds" if exists?
484
+ else
485
+ exists?
486
+ end
487
+ end
488
+
489
+ # Wait until the screen Section object is visible, or until the specified wait time has expired. If the wait time is nil,
490
+ # then the wait time will be Environ.default_max_wait_time.
491
+ #
492
+ # @param seconds [Integer or Float] wait time in seconds
493
+ # @example
494
+ # navigation_toolbar.wait_until_visible(1.5)
495
+ #
496
+ def wait_until_visible(seconds = nil, post_exception = true)
497
+ timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
498
+ wait = Selenium::WebDriver::Wait.new(timeout: timeout)
499
+ wait.until { visible? }
500
+ rescue
501
+ if post_exception
502
+ raise "Could not find Section object '#{get_name}' (#{get_locator}) after #{timeout} seconds" unless visible?
503
+ else
504
+ visible?
505
+ end
506
+ end
507
+
508
+ # Wait until the screen Section object is hidden, or until the specified wait time has expired. If the wait time is nil,
509
+ # then the wait time will be Environ.default_max_wait_time.
510
+ #
511
+ # @param seconds [Integer or Float] wait time in seconds
512
+ # @example
513
+ # navigation_toolbar.wait_until_hidden(2)
514
+ #
515
+ def wait_until_hidden(seconds = nil, post_exception = true)
516
+ timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
517
+ wait = Selenium::WebDriver::Wait.new(timeout: timeout)
518
+ wait.until { hidden? }
519
+ rescue
520
+ if post_exception
521
+ raise "Section object '#{get_name}' (#{get_locator}) remained visible after #{timeout} seconds" if visible?
522
+ else
523
+ visible?
524
+ end
525
+ end
526
+
527
+ # Return width of screen Section object.
528
+ #
529
+ # @return [Integer]
530
+ # @example
531
+ # button_width = my_button.width
532
+ #
533
+ def width
534
+ section = find_section
535
+ section_not_found_exception(section)
536
+ section.size.width
537
+ end
538
+
539
+ # Return height of screen Section object.
540
+ #
541
+ # @return [Integer]
542
+ # @example
543
+ # button_height = my_button.height
544
+ #
545
+ def height
546
+ section = find_section
547
+ section_not_found_exception(section)
548
+ section.size.height
549
+ end
550
+
551
+ # Return x coordinate of screen Section object's location.
552
+ #
553
+ # @return [Integer]
554
+ # @example
555
+ # button_x = my_button.x_loc
556
+ #
557
+ def x_loc
558
+ section = find_section
559
+ section_not_found_exception(section)
560
+ section.location.x
561
+ end
562
+
563
+ # Return y coordinate of screen Section object's location.
564
+ #
565
+ # @return [Integer]
566
+ # @example
567
+ # button_x = my_button.x_loc
568
+ #
569
+ def y_loc
570
+ section = find_section
571
+ section_not_found_exception(section)
572
+ section.location.y
573
+ end
574
+
575
+ private
576
+
577
+ def find_section
578
+ obj = nil
579
+ locators = get_locator
580
+ locators.each do |loc|
581
+ if obj.nil?
582
+ obj = find_element(loc.keys[0], loc.values[0])
583
+ puts "Found object #{loc}" if ENV['DEBUG']
584
+ else
585
+ obj = obj.find_element(loc.keys[0], loc.values[0])
586
+ puts "Found object #{loc}" if ENV['DEBUG']
587
+ end
588
+ end
589
+ obj
590
+ rescue
591
+ nil
592
+ end
593
+
594
+ def section_not_found_exception(obj)
595
+ raise ObjectNotFoundError.new("Section object '#{get_name}' (#{get_locator}) not found") unless obj
596
+ end
597
+
598
+ def self.define_section_element(element_name, obj, locator)
599
+ define_method(element_name) do
600
+ ivar_name = "@#{element_name}"
601
+ ivar = instance_variable_get(ivar_name)
602
+ return ivar if ivar
603
+ instance_variable_set(ivar_name, obj.new(element_name, self, locator, :section))
604
+ end
605
+ end
606
+ end
607
+ end