Class: TestCentricity::ScreenObject

Inherits:
BaseScreenSectionObject show all
Includes:
Test::Unit::Assertions
Defined in:
lib/testcentricity_mobile/app_core/screen_object.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from BaseScreenSectionObject

#populate_data_fields, #swipe_gesture, trait, #verify_ui_states

Constructor Details

#initializeScreenObject



9
10
11
12
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 9

def initialize
  raise "Screen object #{self.class.name} does not have a screen_name trait defined" unless defined?(screen_name)
  @locator = screen_locator if defined?(screen_locator)
end

Instance Attribute Details

#locatorObject (readonly)

Returns the value of attribute locator.



7
8
9
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 7

def locator
  @locator
end

Class Method Details

.alert(element_name, locator) ⇒ Object

Declare and instantiate a single alert UI Element for this screen object.

Examples:

alert :generic_alert_modal, { id: 'android:id/parentPanel' }
alert :generic_alert_modal, { class: 'XCUIElementTypeAlert' }


247
248
249
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 247

def self.alert(element_name, locator)
  define_screen_element(element_name, TestCentricity::AppElements::AppAlert, locator)
end

.alerts(element_hash) ⇒ Object

Declare and instantiate a collection of alerts for this screen object.

Examples:

alerts grant_modal: { id: 'com.android.permissioncontroller:id/grant_dialog' },
       alert_modal: { id: 'android:id/parentPanel' }


258
259
260
261
262
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 258

def self.alerts(element_hash)
  element_hash.each do |element_name, locator|
    alert(element_name, locator)
  end
end

.button(element_name, locator) ⇒ Object

Declare and instantiate a single button UI Element for this screen object.

Examples:

button :video_play,  { accessibility_id: 'video icon play' }


51
52
53
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 51

def self.button(element_name, locator)
  define_screen_element(element_name, TestCentricity::AppElements::AppButton, locator)
end

.buttons(element_hash) ⇒ Object

Declare and instantiate a collection of buttons for this screen object.

Examples:

buttons video_back:    { accessibility_id: 'video icon backward' },
        video_play:    { accessibility_id: 'video icon play' },
        video_pause:   { accessibility_id: 'video icon stop' },
        video_forward: { accessibility_id: 'video icon forward' }


64
65
66
67
68
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 64

def self.buttons(element_hash)
  element_hash.each do |element_name, locator|
    button(element_name, locator)
  end
end

.checkbox(element_name, locator) ⇒ Object

Declare and instantiate a single checkbox UI Element for this screen object.

Examples:

checkbox :bill_address_check, { xpath: '//XCUIElementTypeOther[contains(@name, "billing checkbox")]'}


126
127
128
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 126

def self.checkbox(element_name, locator)
  define_screen_element(element_name, TestCentricity::AppElements::AppCheckBox, locator)
end

.checkboxes(element_hash) ⇒ Object

Declare and instantiate a collection of checkboxes for this screen object.

Examples:

checkboxes bill_address_check: { xpath: '//XCUIElementTypeOther[contains(@name, "billing checkbox")]'},
           is_gift_check: { accessibility_id: 'is a gift' }


137
138
139
140
141
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 137

def self.checkboxes(element_hash)
  element_hash.each do |element_name, locator|
    checkbox(element_name, locator)
  end
end

.define_screen_element(element_name, obj, locator) ⇒ Object



416
417
418
419
420
421
422
423
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 416

def self.define_screen_element(element_name, obj, locator)
  define_method(element_name) do
    ivar_name = "@#{element_name}"
    ivar = instance_variable_get(ivar_name)
    return ivar if ivar
    instance_variable_set(ivar_name, obj.new(element_name, self, locator, :screen))
  end
end

.element(element_name, locator) ⇒ Object

Declare and instantiate a single generic UI Element for this screen object.

  • The locator_identifier (a String) is the value or attribute that uniquely and unambiguously identifies the UI element.

Examples:

element :video_player, { accessibility_id: 'YouTube Video Player' }


26
27
28
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 26

def self.element(element_name, locator)
  define_screen_element(element_name, TestCentricity::AppElements::AppUIElement, locator)
end

.elements(element_hash) ⇒ Object

Declare and instantiate a collection of generic UI Elements for this screen object.

Examples:

elements drop_down_field: { accessibility_id: 'drop_trigger' },
         settings_item: { accessibility_id: 'settings' },
         video_player: { accessibility_id: 'YouTube Video Player' }


38
39
40
41
42
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 38

def self.elements(element_hash)
  element_hash.each do |element_name, locator|
    element(element_name, locator)
  end
end

.image(element_name, locator) ⇒ Object

Declare and instantiate a single image UI Element for this screen object.

Examples:

image :product_image, { xpath: '//XCUIElementTypeImage' }


222
223
224
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 222

def self.image(element_name, locator)
  define_screen_element(element_name, TestCentricity::AppElements::AppImage, locator)
end

.images(element_hash) ⇒ Object

Declare and instantiate a collection of images for this screen object.

Examples:

images empty_cart_image: { accessibility_id: 'empty_cart' },
       logo_image: { accessibility_id: 'WebdriverIO logo' }


233
234
235
236
237
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 233

def self.images(element_hash)
  element_hash.each do |element_name, locator|
    image(element_name, locator)
  end
end

.label(element_name, locator) ⇒ Object

Declare and instantiate a single label UI Element for this screen object.

Examples:

label :header_label, { accessibility_id: 'container header' }


174
175
176
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 174

def self.label(element_name, locator)
  define_screen_element(element_name, TestCentricity::AppElements::AppLabel, locator)
end

.labels(element_hash) ⇒ Object

Declare and instantiate a collection of labels for this screen object.

Examples:

labels total_qty_value:   { accessibility_id: 'total number' },
       total_price_value: { accessibility_id: 'total price' }


185
186
187
188
189
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 185

def self.labels(element_hash)
  element_hash.each do |element_name, locator|
    label(element_name, locator)
  end
end

.list(element_name, locator) ⇒ Object

Declare and instantiate a single list UI Element for this screen object.

Examples:

list :carousel_list, { accessibility_id: 'Carousel' }


198
199
200
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 198

def self.list(element_name, locator)
  define_screen_element(element_name, TestCentricity::AppElements::AppList, locator)
end

.lists(element_hash) ⇒ Object

Declare and instantiate a collection of lists for this screen object.

Examples:

lists product_grid: { xpath: '//android.widget.ScrollView/android.view.ViewGroup' },
      cart_list: { xpath: '//android.widget.ScrollView[@content-desc="cart screen"]' }


209
210
211
212
213
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 209

def self.lists(element_hash)
  element_hash.each do |element_name, locator|
    list(element_name, locator)
  end
end

.radio(element_name, locator) ⇒ Object

Declare and instantiate a single radio button UI Element for this screen object.

Examples:

radio :unicode_radio, { xpath: '//XCUIElementTypeRadioButton[@label="Unicode"]'}


150
151
152
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 150

def self.radio(element_name, locator)
  define_screen_element(element_name, TestCentricity::AppElements::AppRadio, locator)
end

.radios(element_hash) ⇒ Object

Declare and instantiate a collection of radio buttons for this screen object.

Examples:

radios unicode_radio: { xpath: '//XCUIElementTypeRadioButton[@label="Unicode"]'},
       ascii_radio:   { xpath: '//XCUIElementTypeRadioButton[@label="ASCII"] }


161
162
163
164
165
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 161

def self.radios(element_hash)
  element_hash.each do |element_name, locator|
    radio(element_name, locator)
  end
end

.section(section_name, obj, locator = 0) ⇒ Object

Instantiate a single ScreenSection object within this ScreenObject.

Examples:

section :nav_menu, NavMenu


271
272
273
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 271

def self.section(section_name, obj, locator = 0)
  define_screen_element(section_name, obj, locator)
end

.sections(section_hash) ⇒ Object

Declare and instantiate a collection of ScreenSection objects for this screen object.

Examples:

sections nav_bar:  NavBar,
         nav_menu: NavMenu


282
283
284
285
286
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 282

def self.sections(section_hash)
  section_hash.each do |section_name, class_name|
    section(section_name, class_name)
  end
end

.switch(element_name, locator) ⇒ Object

Declare and instantiate a single switch UI Element for this screen object.

Examples:

switch :debug_mode_switch, { accessibility_id: 'debug mode' }


102
103
104
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 102

def self.switch(element_name, locator)
  define_screen_element(element_name, TestCentricity::AppElements::AppSwitch, locator)
end

.switches(element_hash) ⇒ Object

Declare and instantiate a collection of switches for this screen object.

Examples:

switches debug_mode_switch: { accessibility_id: 'debug mode' },
         metrics_switch: { accessibility_id: 'metrics' }


113
114
115
116
117
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 113

def self.switches(element_hash)
  element_hash.each do |element_name, locator|
    switch(element_name, locator)
  end
end

.textfield(element_name, locator) ⇒ Object

Declare and instantiate a single textfield UI Element for this screen object.

Examples:

textfield :payee_name_field, { xpath: '//android.widget.EditText[@content-desc="Full Name* input field"]' }
textfield :payee_name_field, { xpath: '//XCUIElementTypeTextField[@name="Full Name* input field"]' }


78
79
80
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 78

def self.textfield(element_name, locator)
  define_screen_element(element_name, TestCentricity::AppElements::AppTextField, locator)
end

.textfields(element_hash) ⇒ Object

Declare and instantiate a collection of textfields for this screen object.

Examples:

textfields username_field: { accessibility_id: 'Username input field' },
           password_field: { accessibility_id: 'Password input field' }


89
90
91
92
93
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 89

def self.textfields(element_hash)
  element_hash.each do |element_name, locator|
    textfield(element_name, locator)
  end
end

Instance Method Details

#exists?Boolean

Does Screen object exists?

Examples:

home_screen.exists?


294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 294

def exists?
  @locator.is_a?(Array) ? tries ||= 2 : tries ||= 1
  if @locator.is_a?(Array)
    loc = @locator[tries - 1]
    find_element(loc.keys[0], loc.values[0])
  else
    find_element(@locator.keys[0], @locator.values[0])
  end
  true
rescue
  retry if (tries -= 1) > 0
  false
end

#load_screenObject

Load the screen using its defined deep_link trait. When testing on physical iOS devices running iOS/iPadOS versions earlier than version 16.4, deep links can only be opened by sending the deeplink URL to the mobile Safari web browser, and then accepting the confirmation modal that pops up. This method handles invoking deeplinks on Android and iOS/iPadOS simulators and physical devices.

This method verifies that the target screen is loaded and displayed, and sets ScreenManager.current_screen to reference the target screen instance.

Examples:

cart_screen.load_screen


372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 372

def load_screen
  # return if target screen is already loaded
  if exists?
    ScreenManager.current_screen = self
    return
  end

  url = if deep_link.include?("://")
          deep_link
        elsif !Environ.current.deep_link_prefix.blank?
          "#{Environ.current.deep_link_prefix}://#{deep_link}"
        end

  if Environ.is_android?
    Environ.appium_driver.execute_script('mobile:deepLink', { url: url, package: Environ.current.android_app_id })
  elsif Environ.is_ios?
    if Environ.is_device? && Environ.device_os_version.to_f < 16.4
      # launch Safari browser on iOS real device if iOS version is below 16.4
      Environ.appium_driver.execute_script('mobile: launchApp', { bundleId: 'com.apple.mobilesafari' })
      unless Environ.appium_driver.is_keyboard_shown
        begin
          # attempt to find and click URL button on iOS 15 Safari browser
          find_element(:accessibility_id, 'TabBarItemTitle').click
        rescue
          # fall back to URL button on iOS 14 Safari browser
          find_element(:xpath, '//XCUIElementTypeButton[@name="URL"]').click
        end
      end
      # enter deep-link URL
      wait_for_object(:xpath, '//XCUIElementTypeTextField[@name="URL"]', 5).send_keys("#{url}\uE007")
      # wait for and accept the popup modal
      wait_for_object(:xpath, '//XCUIElementTypeButton[@name="Open"]', 10).click
    else
      # iOS version is >= 16.4 so directly load screen via deepLink
      Environ.appium_driver.get(url)
    end
  else
    raise "#{Environ.device_os} is not supported"
  end
  verify_screen_exists
end


346
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 346

def navigate_to; end

#verify_screen_existsObject

Verifies that the target screen is displayed, and sets ScreenManager.current_screen to reference the target screen instance.



353
354
355
356
357
358
359
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 353

def verify_screen_exists
  wait = Selenium::WebDriver::Wait.new(timeout: Environ.default_max_wait_time)
  wait.until { exists? }
  ScreenManager.current_screen = self
rescue
  raise "Could not find screen_locator for screen object '#{self.class.name}' (#{@locator}) after #{Environ.default_max_wait_time} seconds"
end

#verify_screen_uiObject



348
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 348

def verify_screen_ui; end

#wait_until_exists(seconds = nil, post_exception = true) ⇒ Object

Wait until the Screen object exists, or until the specified wait time has expired. If the wait time is nil, then the wait time will be Environ.default_max_wait_time.

Examples:

cart_screen.wait_until_exists(15)


315
316
317
318
319
320
321
322
323
324
325
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 315

def wait_until_exists(seconds = nil, post_exception = true)
  timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
  wait = Selenium::WebDriver::Wait.new(timeout: timeout)
  wait.until { exists? }
rescue
  if post_exception
    raise "Screen object #{self.class.name} not found after #{timeout} seconds" unless exists?
  else
    exists?
  end
end

#wait_until_gone(seconds = nil, post_exception = true) ⇒ Object

Wait until the Screen object is gone, or until the specified wait time has expired. If the wait time is nil, then the wait time will be Environ.default_max_wait_time.

Examples:

.wait_until_gone(15)


334
335
336
337
338
339
340
341
342
343
344
# File 'lib/testcentricity_mobile/app_core/screen_object.rb', line 334

def wait_until_gone(seconds = nil, post_exception = true)
  timeout = seconds.nil? ? Environ.default_max_wait_time : seconds
  wait = Selenium::WebDriver::Wait.new(timeout: timeout)
  wait.until { !exists? }
rescue
  if post_exception
    raise "Screen object #{self.class.name} remained visible after #{timeout} seconds" if exists?
  else
    exists?
  end
end