Class: TestaAppiumDriver::Driver

Inherits:
Object
  • Object
show all
Includes:
ClassSelectors, Helpers, TypeSelectors
Defined in:
lib/testa_appium_driver/driver.rb,
lib/testa_appium_driver/ios/driver.rb,
lib/testa_appium_driver/android/driver.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from ClassSelectors

#add_selector, #button, #buttons, #card_view, #card_views, #check_box, #check_boxes, #edit_text, #edit_texts, #element, #elements, #frame_layout, #frame_layouts, #horizontal_scroll_view, #horizontal_scroll_views, #image_button, #image_buttons, #image_view, #image_views, #linear_layout, #linear_layouts, #list_view, #list_views, #progress_bar, #progress_bars, #radio_button, #radio_buttons, #radio_group, #radio_groups, #recycler_view, #recycler_views, #relative_layout, #relative_layouts, #scroll_view, #scroll_views, #search_view, #search_views, #spinner, #spinners, #switch, #switches, #text_view, #text_views, #toast, #toasts, #toolbar, #toolbars, #view, #view_group, #view_groups, #view_pager, #view_pagers, #views, #web_view, #web_views

Methods included from TypeSelectors

#add_selector, #button, #buttons, #cell, #cells, #collection_view, #collection_views, #element, #elements, #image, #images, #navigation_bar, #navigation_bars, #other, #others, #scroll_view, #scroll_views, #secure_text_field, #secure_text_fields, #static_text, #static_texts, #table, #tables, #text_field, #text_fields, #window, #windows

Methods included from Helpers

#extract_selectors_from_params, #hash_to_class_chain, #hash_to_uiautomator, #hash_to_xpath, #is_scrollable_selector?, #resolve_id

Constructor Details

#initialize(opts = {}) ⇒ Driver

custom options

  • default_find_strategy: default strategy to be used for finding elements. Available strategies :uiautomator or :xpath

  • default_scroll_strategy: default strategy to be used for scrolling. Available strategies: :uiautomator(android only), :w3c



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/testa_appium_driver/driver.rb', line 29

def initialize(opts = {})
  @testa_opts = opts[:testa_appium_driver] || {}

  core = Appium::Core.for(opts)
  @driver = core.start_driver
  @automation_name = @driver.capabilities["automationName"].downcase.to_sym
  @device = @driver.capabilities.platform_name.downcase.to_sym

  extend_for(@device, @automation_name)

  handle_testa_opts

  invalidate_cache

  #disable_wait_for_idle
  #disable_implicit_wait

  ::Appium::Core::Element.set_driver(self, @driver.capabilities["udid"])
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

method missing is used to forward methods to the actual appium driver after the method is executed, find element cache is invalidated



145
146
147
148
149
# File 'lib/testa_appium_driver/driver.rb', line 145

def method_missing(method, *args, &block)
  r = @driver.send(method, *args, &block)
  invalidate_cache
  r
end

Instance Attribute Details

#automation_nameString (readonly)

Returns driver automation name (uiautomator2 or xcuitest).

Returns:

  • (String)

    driver automation name (uiautomator2 or xcuitest)



24
25
26
# File 'lib/testa_appium_driver/driver.rb', line 24

def automation_name
  @automation_name
end

#deviceString (readonly)

Returns iOS or Android.

Returns:

  • (String)

    iOS or Android



21
22
23
# File 'lib/testa_appium_driver/driver.rb', line 21

def device
  @device
end

#driver::Appium::Core::Base::Driver

Returns the ruby_lib_core appium driver.

Returns:

  • (::Appium::Core::Base::Driver)

    the ruby_lib_core appium driver



18
19
20
# File 'lib/testa_appium_driver/driver.rb', line 18

def driver
  @driver
end

Instance Method Details

#android?Boolean

Returns:

  • (Boolean)


308
309
310
# File 'lib/testa_appium_driver/driver.rb', line 308

def android?
  device == :android
end

#backObject



201
202
203
# File 'lib/testa_appium_driver/driver.rb', line 201

def back
  @driver.back
end

#click(x, y, double: false) ⇒ Object



250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/testa_appium_driver/driver.rb', line 250

def click(x, y, double: false)
  ws = driver.window_size
  window_width = ws.width.to_i
  window_height = ws.height.to_i
  if x.kind_of?(Integer)
    if x < 0
      x = window_width + x
    end
  elsif x.kind_of?(Float) && x <= 1.0 && x >= 0
    x = window_width*x
  else
    raise "x value #{x} not supported. Use integer as pixel or float (0..1) as percentage of screen"
  end

  if y.kind_of?(Integer)
    if y < 0
      y = window_height + y
    end
  elsif y.kind_of?(Float) && y <= 1.0 && y >= 0
    y = window_height*y
  else
    raise "y value #{x} not supported. Use integer as pixel or float (0..1) as percentage of screen"
  end


  action_builder = @driver.action
  f1 = action_builder.add_pointer_input(:touch, "finger1")
  f1.create_pointer_move(duration: 0, x: x, y: y, origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
  f1.create_pointer_down(:left)
  f1.create_pointer_up(:left)
  if double
    f1.create_pause(0.1)
    f1.create_pointer_down(:left)
    f1.create_pointer_up(:left)
  end
  @driver.perform_actions [f1]
end

#current_packageObject

@@return [String] current package under test



192
193
194
# File 'lib/testa_appium_driver/driver.rb', line 192

def current_package
  @driver.current_package
end

#disable_implicit_waitObject

disables implicit wait



152
153
154
155
156
157
158
# File 'lib/testa_appium_driver/driver.rb', line 152

def disable_implicit_wait
  @implicit_wait_ms = @driver.get_timeouts["implicit"].to_i
  @implicit_wait_ms = @implicit_wait_ms/1000 if @implicit_wait_ms > 100000
  @implicit_wait_uiautomator_ms = @driver.get_settings["waitForSelectorTimeout"]
  @driver.manage.timeouts.implicit_wait = 0
  @driver.update_settings({waitForSelectorTimeout: 0})
end

#disable_wait_for_idleObject

disables wait for idle, only executed for android devices



161
162
163
164
165
166
# File 'lib/testa_appium_driver/driver.rb', line 161

def disable_wait_for_idle
  if @device == :android
    @wait_for_idle_timeout = @driver.settings.get["waitForIdleTimeout"]
    @driver.update_settings({waitForIdleTimeout: 0})
  end
end

#double_click(x, y) ⇒ Object



288
289
290
# File 'lib/testa_appium_driver/driver.rb', line 288

def double_click(x,y)
  click(x,y, double: true)
end

#dpad_down_keyObject



226
227
228
# File 'lib/testa_appium_driver/driver.rb', line 226

def dpad_down_key
  @driver.press_keycode(20)
end

#dpad_left_keyObject



234
235
236
# File 'lib/testa_appium_driver/driver.rb', line 234

def dpad_left_key
  @driver.press_keycode(23)
end

#dpad_right_keyObject



230
231
232
# File 'lib/testa_appium_driver/driver.rb', line 230

def dpad_right_key
  @driver.press_keycode(22)
end

#dpad_up_keyObject



222
223
224
# File 'lib/testa_appium_driver/driver.rb', line 222

def dpad_up_key
  @driver.press_keycode(19)
end

#enter_keyObject



238
239
240
# File 'lib/testa_appium_driver/driver.rb', line 238

def enter_key
  @driver.press_keycode(66)
end

#execute(from_element, single, strategies_and_selectors, skip_cache: false, ignore_implicit_wait: false) ⇒ Selenium::WebDriver::Element, Array

Executes the find_element with the resolved locator strategy and selector. Find_element might be skipped if cache is hit. Cache stores last executed find_element with given selector, strategy and from_element. If given values are the same within last 5 seconds element is retrieved from cache.

Parameters:

  • from_element (TestaAppiumDriver::Locator, TestaAppiumDriver::Driver)

    element from which start the search

  • single (Boolean)

    fetch single or multiple results

  • strategies_and_selectors (Array<Hash>)

    array of usable strategies and selectors

  • skip_cache (Boolean) (defaults to: false)

    to skip checking and storing cache

Returns:

  • (Selenium::WebDriver::Element, Array)

    element is returned if single is true, array otherwise



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/testa_appium_driver/driver.rb', line 72

def execute(from_element, single, strategies_and_selectors, skip_cache: false, ignore_implicit_wait: false)

  # if user wants to wait for element to exist, he can use wait_until_present
  start_time = Time.now.to_f
  ss_index = 0




  # resolve from_element unique id, so that we can cache it properly
  from_element_id = from_element.instance_of?(TestaAppiumDriver::Locator) ? from_element.strategies_and_selectors : nil

  begin
    begin
      ss = strategies_and_selectors[ss_index % strategies_and_selectors.count]
    rescue ZeroDivisionError
      puts "aa"
    end
    ss_index +=1

    puts "Executing #{from_element_id ? "from #{from_element.strategy}: #{from_element.strategies_and_selectors} => " : ""}#{ss.keys[0]}: #{ss.values[0]}"

    if @cache[:selector] != ss.values[0] || # cache miss, selector is different
      @cache[:time] + 5 <= Time.now || # cache miss, older than 5 seconds
      @cache[:strategy] != ss.keys[0] || # cache miss, different find strategy
      @cache[:from_element_id] != from_element_id || # cache miss, search is started from different element
      skip_cache # cache is skipped

      if ss.keys[0] == FIND_STRATEGY_IMAGE
        set_find_by_image_settings(ss.values[0].dup)
        if single
          execute_result = from_element.find_element_by_image(ss.values[0][:image])
        else
          execute_result = from_element.find_elements_by_image(ss.values[0][:image])
        end
        restore_set_by_image_settings
      else
        if single
          execute_result = from_element.find_element(ss)
        else
          execute_result = from_element.find_elements(ss)
        end
      end



      unless skip_cache
        @cache[:selector] = ss.values[0]
        @cache[:strategy] = ss.keys[0]
        @cache[:time] = Time.now
        @cache[:from_element_id] = from_element_id
        @cache[:element] = execute_result
      end
    else
      # this is a cache hit, use the element from cache
      execute_result = @cache[:element]
      puts "Using cache from #{@cache[:time].strftime("%H:%M:%S.%L")}, strategy: #{@cache[:strategy]}"
    end
  rescue => e
    #if (start_time + @implicit_wait_ms/1000 < Time.now.to_f && !ignore_implicit_wait) || ss_index < strategies_and_selectors.count
    if ss_index < strategies_and_selectors.count
      sleep EXISTS_WAIT if ss_index >= strategies_and_selectors.count
      retry
    else
      raise e
    end
  end

  execute_result
end

#first_and_last_child(from_element = @driver) ⇒ Object



301
302
303
304
305
306
# File 'lib/testa_appium_driver/driver.rb', line 301

def first_and_last_child(from_element = @driver)
  elements = from_element.find_elements(xpath: "./*")
  return nil if elements.count == 0

  [elements[0], elements[-1]]
end

#first_and_last_leaf(from_element = @driver) ⇒ Array<Selenium::WebDriver::Element] array of 2 elements, the first element without children and the last element without children in the current page

Returns Array<Selenium::WebDriver::Element] array of 2 elements, the first element without children and the last element without children in the current page.

Returns:

  • (Array<Selenium::WebDriver::Element] array of 2 elements, the first element without children and the last element without children in the current page)

    Array<Selenium::WebDriver::Element] array of 2 elements, the first element without children and the last element without children in the current page



295
296
297
298
299
# File 'lib/testa_appium_driver/driver.rb', line 295

def first_and_last_leaf(from_element = @driver)
  elements = from_element.find_elements(xpath: ".//*[not(*)]")
  return nil if elements.count == 0
  [elements[0], elements[-1]]
end

#hide_keyboardObject



210
211
212
# File 'lib/testa_appium_driver/driver.rb', line 210

def hide_keyboard
  @driver.hide_keyboard
end

#home_keyObject



214
215
216
# File 'lib/testa_appium_driver/driver.rb', line 214

def home_key
  @driver.press_keycode(3)
end

#invalidate_cacheObject

invalidates current find_element cache



51
52
53
54
55
56
57
58
59
# File 'lib/testa_appium_driver/driver.rb', line 51

def invalidate_cache
  @cache = {
    strategy: nil,
    selector: nil,
    element: nil,
    from_element: nil,
    time: Time.at(0)
  }
end

#ios?Boolean

Returns:

  • (Boolean)


312
313
314
# File 'lib/testa_appium_driver/driver.rb', line 312

def ios?
  device == :ios
end

#is_keyboard_shown?Boolean

Returns:

  • (Boolean)


206
207
208
# File 'lib/testa_appium_driver/driver.rb', line 206

def is_keyboard_shown?
  @driver.is_keyboard_shown
end

#long_press_keycode(code) ⇒ Object



246
247
248
# File 'lib/testa_appium_driver/driver.rb', line 246

def long_press_keycode(code)
  @driver.long_press_keycode(code)
end

#press_keycode(code) ⇒ Object



242
243
244
# File 'lib/testa_appium_driver/driver.rb', line 242

def press_keycode(code)
  @driver.press_keycode(code)
end

#restore_set_by_image_settingsObject



186
187
188
# File 'lib/testa_appium_driver/driver.rb', line 186

def restore_set_by_image_settings
  @driver.update_settings(@default_find_image_settings) if @default_find_image_settings
end

#scrollableTestaAppiumDriver::Locator

Returns first scrollable element.

Parameters:

  • params (Hash)

Returns:



13
14
15
# File 'lib/testa_appium_driver/ios/driver.rb', line 13

def scrollable(params = {})
  scroll_view(params)
end

#scrollablesTestaAppiumDriver::Locator

Returns first scrollable element.

Parameters:

  • params (Hash)

Returns:



19
20
21
# File 'lib/testa_appium_driver/ios/driver.rb', line 19

def scrollables(params = {})
  scroll_views(params)
end

#set_find_by_image_settings(settings) ⇒ Object



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/testa_appium_driver/driver.rb', line 169

def set_find_by_image_settings(settings)
  settings.delete(:image)
  @default_find_image_settings = {}
  old_settings = @driver.get_settings
  @default_find_image_settings[:imageMatchThreshold] = old_settings["imageMatchThreshold"]
  @default_find_image_settings[:fixImageFindScreenshotDims] = old_settings["fixImageFindScreenshotDims"]
  @default_find_image_settings[:fixImageTemplateSize] = old_settings["fixImageTemplateSize"]
  @default_find_image_settings[:fixImageTemplateScale] = old_settings["fixImageTemplateScale"]
  @default_find_image_settings[:defaultImageTemplateScale] = old_settings["defaultImageTemplateScale"]
  @default_find_image_settings[:checkForImageElementStaleness] = old_settings["checkForImageElementStaleness"]
  @default_find_image_settings[:autoUpdateImageElementPosition] = old_settings["autoUpdateImageElementPosition"]
  @default_find_image_settings[:imageElementTapStrategy] = old_settings["imageElementTapStrategy"]
  @default_find_image_settings[:getMatchedImageResult] = old_settings["getMatchedImageResult"]

  @driver.update_settings(settings)
end

#shell(command, args: nil, timeout: nil, includeStderr: true) ⇒ Object

executes shell command

Parameters:

  • command (String)

    Shell command name to execute for example echo or rm

  • args (Array<String>) (defaults to: nil)

    Array of command arguments, example: [‘-f’, ‘/sdcard/my_file.txt’]

  • timeout (Integer) (defaults to: nil)

    Command timeout in milliseconds. If the command blocks for longer than this timeout then an exception is going to be thrown. The default timeout is 20000 ms

  • includeStderr (Boolean) (defaults to: true)

    Whether to include stderr stream into the returned result.



15
16
17
18
19
20
21
22
23
# File 'lib/testa_appium_driver/android/driver.rb', line 15

def shell(command, args: nil, timeout: nil, includeStderr: true)
  params = {
    command: command,
    includeStderr: includeStderr,
  }
  params[:args] = args unless args.nil?
  params[:timeout] = timeout unless timeout.nil?
  @driver.execute_script("mobile: shell", params)
end

#tab_keyObject



218
219
220
# File 'lib/testa_appium_driver/driver.rb', line 218

def tab_key
  @driver.press_keycode(61)
end

#window_sizeObject



197
198
199
# File 'lib/testa_appium_driver/driver.rb', line 197

def window_size
  @driver.window_size
end