testa_appium_driver 0.1.5 → 0.1.9

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.
@@ -29,7 +29,6 @@ module TestaAppiumDriver
29
29
  def initialize(opts = {})
30
30
  @testa_opts = opts[:testa_appium_driver] || {}
31
31
 
32
-
33
32
  core = Appium::Core.for(opts)
34
33
  extend_for(core.device, core.automation_name)
35
34
  @device = core.device
@@ -41,6 +40,9 @@ module TestaAppiumDriver
41
40
  invalidate_cache
42
41
 
43
42
 
43
+ disable_wait_for_idle
44
+ disable_implicit_wait
45
+
44
46
  Selenium::WebDriver::Element.set_driver(self, opts[:caps][:udid])
45
47
  end
46
48
 
@@ -63,42 +65,55 @@ module TestaAppiumDriver
63
65
  # Cache stores last executed find_element with given selector, strategy and from_element. If given values are the same within
64
66
  # last 5 seconds element is retrieved from cache.
65
67
  # @param [TestaAppiumDriver::Locator, TestaAppiumDriver::Driver] from_element element from which start the search
66
- # @param [String] selector resolved string of a [TestaAppiumDriver::Locator] selector xpath for xpath strategy, java UiSelectors for uiautomator or id for ID strategy
67
68
  # @param [Boolean] single fetch single or multiple results
68
- # @param [Symbol, nil] strategy [TestaAppiumDriver::FIND_STRATEGY_UIAUTOMATOR], [TestaAppiumDriver::FIND_STRATEGY_XPATH] or [TestaAppiumDriver::FIND_STRATEGY_ID]
69
- # @param [Symbol] default_strategy if strategy is not enforced, default can be used
69
+ # @param [Array<Hash>] strategies_and_selectors array of usable strategies and selectors
70
70
  # @param [Boolean] skip_cache to skip checking and storing cache
71
71
  # @return [Selenium::WebDriver::Element, Array] element is returned if single is true, array otherwise
72
- def execute(from_element, selector, single, strategy, default_strategy, skip_cache = false)
72
+ def execute(from_element, single, strategies_and_selectors, skip_cache: false, ignore_implicit_wait: false)
73
73
 
74
74
  # if user wants to wait for element to exist, he can use wait_until_present
75
- disable_wait_for_idle
75
+ start_time = Time.now.to_f
76
+ ss_index = 0
77
+
76
78
 
77
79
 
78
- # if not restricted to a strategy, use the default one
79
- strategy = default_strategy if strategy.nil?
80
80
 
81
81
  # resolve from_element unique id, so that we can cache it properly
82
- from_element_id = from_element.kind_of?(TestaAppiumDriver::Locator) ? from_element.strategy_and_selector[1] : nil
82
+ from_element_id = from_element.instance_of?(TestaAppiumDriver::Locator) ? from_element.strategies_and_selectors : nil
83
83
 
84
- puts "Executing #{from_element_id ? "from #{from_element.strategy}: #{from_element.strategy_and_selector} => " : ""}#{strategy}: #{selector}"
85
84
  begin
86
- if @cache[:selector] != selector || # cache miss, selector is different
85
+ ss = strategies_and_selectors[ss_index % strategies_and_selectors.count]
86
+ ss_index +=1
87
+
88
+ puts "Executing #{from_element_id ? "from #{from_element.strategy}: #{from_element.strategies_and_selectors} => " : ""}#{ss.keys[0]}: #{ss.values[0]}"
89
+
90
+ if @cache[:selector] != ss.values[0] || # cache miss, selector is different
87
91
  @cache[:time] + 5 <= Time.now || # cache miss, older than 5 seconds
88
- @cache[:strategy] != strategy || # cache miss, different find strategy
92
+ @cache[:strategy] != ss.keys[0] || # cache miss, different find strategy
89
93
  @cache[:from_element_id] != from_element_id || # cache miss, search is started from different element
90
94
  skip_cache # cache is skipped
91
95
 
92
- if single
93
- execute_result = from_element.find_element("#{strategy}": selector)
96
+ if ss.keys[0] == FIND_STRATEGY_IMAGE
97
+ set_find_by_image_settings(ss.values[0].dup)
98
+ if single
99
+ execute_result = from_element.find_element_by_image(ss.values[0][:image])
100
+ else
101
+ execute_result = from_element.find_elements_by_image(ss.values[0][:image])
102
+ end
103
+ restore_set_by_image_settings
94
104
  else
95
- execute_result = from_element.find_elements("#{strategy}": selector)
105
+ if single
106
+ execute_result = from_element.find_element(ss)
107
+ else
108
+ execute_result = from_element.find_elements(ss)
109
+ end
96
110
  end
97
111
 
98
112
 
113
+
99
114
  unless skip_cache
100
- @cache[:selector] = selector
101
- @cache[:strategy] = strategy
115
+ @cache[:selector] = ss.values[0]
116
+ @cache[:strategy] = ss.keys[0]
102
117
  @cache[:time] = Time.now
103
118
  @cache[:from_element_id] = from_element_id
104
119
  @cache[:element] = execute_result
@@ -109,9 +124,13 @@ module TestaAppiumDriver
109
124
  puts "Using cache from #{@cache[:time].strftime("%H:%M:%S.%L")}, strategy: #{@cache[:strategy]}"
110
125
  end
111
126
  rescue => e
112
- raise e
113
- ensure
114
- enable_wait_for_idle
127
+ #if (start_time + @implicit_wait_ms/1000 < Time.now.to_f && !ignore_implicit_wait) || ss_index < strategies_and_selectors.count
128
+ if ss_index < strategies_and_selectors.count
129
+ sleep EXISTS_WAIT if ss_index >= strategies_and_selectors.count
130
+ retry
131
+ else
132
+ raise e
133
+ end
115
134
  end
116
135
 
117
136
  execute_result
@@ -128,31 +147,42 @@ module TestaAppiumDriver
128
147
 
129
148
  # disables implicit wait
130
149
  def disable_implicit_wait
131
- @implicit_wait_ms = @driver.get_timeouts["implicit"]
132
- @driver.manage.timeouts.implicit_wait = 0
150
+ @implicit_wait_ms = @driver.get_timeouts["implicit"].to_i
151
+ @implicit_wait_ms = @implicit_wait_ms/1000 if @implicit_wait_ms > 100000
152
+ @implicit_wait_uiautomator_ms = @driver.get_settings["waitForSelectorTimeout"]
153
+ @driver.manage.timeouts.implicit_wait = 0
154
+ @driver.update_settings({waitForSelectorTimeout: 1})
133
155
  end
134
156
 
135
- # enables implicit wait, can be called only after disabling implicit wait
136
- def enable_implicit_wait
137
- raise "Implicit wait is not disabled" if @implicit_wait_ms.nil?
138
- # get_timeouts always returns in milliseconds, but we should set in seconds
139
- @driver.manage.timeouts.implicit_wait = @implicit_wait_ms / 1000
140
- end
141
157
 
142
158
  # disables wait for idle, only executed for android devices
143
159
  def disable_wait_for_idle
144
160
  if @device == :android
145
161
  @wait_for_idle_timeout = @driver.settings.get["waitForIdleTimeout"]
146
- @driver.update_settings({waitForIdleTimeout: 0})
162
+ @driver.update_settings({waitForIdleTimeout: 1})
147
163
  end
148
164
  end
149
165
 
150
- # enables wait for idle, only executed for android devices
151
- def enable_wait_for_idle
152
- if @device == :android
153
- raise "Wait for idle is not disabled" if @wait_for_idle_timeout.nil?
154
- @driver.update_settings({waitForIdleTimeout: @wait_for_idle_timeout})
155
- end
166
+
167
+ def set_find_by_image_settings(settings)
168
+ settings.delete(:image)
169
+ @default_find_image_settings = {}
170
+ old_settings = @driver.get_settings
171
+ @default_find_image_settings[:imageMatchThreshold] = old_settings["imageMatchThreshold"]
172
+ @default_find_image_settings[:fixImageFindScreenshotDims] = old_settings["fixImageFindScreenshotDims"]
173
+ @default_find_image_settings[:fixImageTemplateSize] = old_settings["fixImageTemplateSize"]
174
+ @default_find_image_settings[:fixImageTemplateScale] = old_settings["fixImageTemplateScale"]
175
+ @default_find_image_settings[:defaultImageTemplateScale] = old_settings["defaultImageTemplateScale"]
176
+ @default_find_image_settings[:checkForImageElementStaleness] = old_settings["checkForImageElementStaleness"]
177
+ @default_find_image_settings[:autoUpdateImageElementPosition] = old_settings["autoUpdateImageElementPosition"]
178
+ @default_find_image_settings[:imageElementTapStrategy] = old_settings["imageElementTapStrategy"]
179
+ @default_find_image_settings[:getMatchedImageResult] = old_settings["getMatchedImageResult"]
180
+
181
+ @driver.update_settings(settings)
182
+ end
183
+
184
+ def restore_set_by_image_settings
185
+ @driver.update_settings(@default_find_image_settings) if @default_find_image_settings
156
186
  end
157
187
 
158
188
 
@@ -179,6 +209,30 @@ module TestaAppiumDriver
179
209
  @driver.hide_keyboard
180
210
  end
181
211
 
212
+ def tab_key
213
+ @driver.press_keycode(61)
214
+ end
215
+
216
+ def dpad_up_key
217
+ @driver.press_keycode(19)
218
+ end
219
+
220
+ def dpad_down_key
221
+ @driver.press_keycode(20)
222
+ end
223
+
224
+ def dpad_right_key
225
+ @driver.press_keycode(22)
226
+ end
227
+
228
+ def dpad_left_key
229
+ @driver.press_keycode(23)
230
+ end
231
+
232
+ def enter_key
233
+ @driver.press_keycode(66)
234
+ end
235
+
182
236
  def press_keycode(code)
183
237
  @driver.press_keycode(code)
184
238
  end
@@ -188,13 +242,11 @@ module TestaAppiumDriver
188
242
  end
189
243
 
190
244
 
245
+
246
+
191
247
  # @return [Array<Selenium::WebDriver::Element] array of 2 elements, the first element without children and the last element without children in the current page
192
248
  def first_and_last_leaf(from_element = @driver)
193
- disable_wait_for_idle
194
- disable_implicit_wait
195
249
  elements = from_element.find_elements(xpath: "//*[not(*)]")
196
- enable_implicit_wait
197
- enable_wait_for_idle
198
250
  return nil if elements.count == 0
199
251
  [elements[0], elements[-1]]
200
252
  end
@@ -7,6 +7,19 @@ module TestaAppiumDriver
7
7
  include TypeSelectors
8
8
 
9
9
 
10
+
11
+ # @param params [Hash]
12
+ # @return [TestaAppiumDriver::Locator] first scrollable element
13
+ def scrollable(params = {})
14
+ scroll_view(params)
15
+ end
16
+
17
+ # @param params [Hash]
18
+ # @return [TestaAppiumDriver::Locator] first scrollable element
19
+ def scrollables(params = {})
20
+ scroll_views(params)
21
+ end
22
+
10
23
  private
11
24
  def handle_testa_opts
12
25
  if @testa_opts[:default_find_strategy].nil?
@@ -3,6 +3,7 @@ require_relative 'locator/attributes'
3
3
  module TestaAppiumDriver
4
4
  class Locator
5
5
  include TypeSelectors
6
+ attr_accessor :class_chain_selector
6
7
 
7
8
  def init(params, selectors, single)
8
9
  if is_scrollable_selector?(selectors, single)
@@ -19,16 +20,23 @@ module TestaAppiumDriver
19
20
  params[:scrollable_locator] = self.dup
20
21
  end
21
22
 
23
+ @class_chain_selector = hash_to_class_chain(selectors, single)
24
+
25
+
22
26
  @scrollable_locator = params[:scrollable_locator] if params[:scrollable_locator]
23
27
  end
24
28
 
25
29
 
26
30
  # @return [Array] returns 2 elements. The first is the resolved find element strategy and the second is the resolved selector
27
- def strategy_and_selector
31
+ def strategies_and_selectors
32
+ ss = []
28
33
  if @can_use_id_strategy
29
- return FIND_STRATEGY_NAME, @can_use_id_strategy
34
+ ss.push({"#{FIND_STRATEGY_NAME}": @can_use_id_strategy})
30
35
  end
31
- [FIND_STRATEGY_XPATH, @xpath_selector]
36
+ ss.push({"#{FIND_STRATEGY_CLASS_CHAIN}": @class_chain_selector}) if @strategy.nil? || @strategy == FIND_STRATEGY_CLASS_CHAIN
37
+ ss.push({"#{FIND_STRATEGY_XPATH}": @xpath_selector}) if @strategy.nil? || @strategy == FIND_STRATEGY_XPATH
38
+ ss.push({"#{FIND_STRATEGY_IMAGE}": @image_selector}) if @strategy == FIND_STRATEGY_IMAGE
39
+ ss
32
40
  end
33
41
 
34
42
 
@@ -41,13 +49,23 @@ module TestaAppiumDriver
41
49
 
42
50
  locator = self.dup
43
51
  add_xpath_child_selectors(locator, selectors, single)
52
+ if @strategy.nil? || @strategy == FIND_STRATEGY_CLASS_CHAIN
53
+ add_class_chain_child_selectors(locator, selectors, single)
54
+ end
55
+
44
56
  if is_scrollable_selector?(selectors, single)
45
57
  locator.scrollable_locator.scroll_orientation = :vertical
46
- locator.scrollable_locator = self.dup
58
+ locator.scrollable_locator = locator
47
59
  end
48
60
 
49
61
  locator.last_selector_adjacent = false
50
62
  locator
51
63
  end
64
+
65
+
66
+ def add_class_chain_child_selectors(locator, selectors, single)
67
+ locator.single = false unless single # switching from single result to multiple
68
+ locator.class_chain_selector += "/" + hash_to_class_chain(selectors, single)
69
+ end
52
70
  end
53
71
  end
@@ -5,10 +5,15 @@ module TestaAppiumDriver
5
5
  # @return [TestaAppiumDriver::Locator]
6
6
  def add_selector(*args, &block)
7
7
  # if class selector is executed from driver, create new locator instance
8
- if self.kind_of?(TestaAppiumDriver::Driver) || self.kind_of(Selenium::WebDriver::Element)
8
+ if self.kind_of?(TestaAppiumDriver::Driver) || self.instance_of?(Selenium::WebDriver::Element)
9
9
  args.last[:default_find_strategy] = @default_find_strategy
10
10
  args.last[:default_scroll_strategy] = @default_scroll_strategy
11
- Locator.new(self, self, *args)
11
+ if self.instance_of?(Selenium::WebDriver::Element)
12
+ driver = self.get_driver
13
+ else
14
+ driver = self
15
+ end
16
+ Locator.new(driver, self, *args)
12
17
  else
13
18
  # class selector is executed from locator, just add child selector criteria
14
19
  self.add_child_selector(*args)
@@ -111,19 +116,6 @@ module TestaAppiumDriver
111
116
  end
112
117
 
113
118
 
114
- # @param params [Hash]
115
- # @return [TestaAppiumDriver::Locator] first scrollable element
116
- def scrollable(params = {})
117
- scroll_view(params)
118
- end
119
-
120
- # @param params [Hash]
121
- # @return [TestaAppiumDriver::Locator] first scrollable element
122
- def scrollables(params = {})
123
- scroll_views(params)
124
- end
125
-
126
-
127
119
  # @return [TestaAppiumDriver::Locator]
128
120
  def scroll_view(params = {})
129
121
  params[:type] = "XCUIElementTypeScrollView"
@@ -163,5 +155,34 @@ module TestaAppiumDriver
163
155
  params[:single] = false
164
156
  add_selector(params)
165
157
  end
158
+
159
+
160
+
161
+ # @return [TestaAppiumDriver::Locator]
162
+ def text_field(params = {})
163
+ params[:type] = "XCUIElementTypeTextField"
164
+ add_selector(params)
165
+ end
166
+
167
+ # @return [TestaAppiumDriver::Locator]
168
+ def text_fields(params = {})
169
+ params[:type] = "XCUIElementTypeTextField"
170
+ params[:single] = false
171
+ add_selector(params)
172
+ end
173
+
174
+
175
+ # @return [TestaAppiumDriver::Locator]
176
+ def secure_text_field(params = {})
177
+ params[:type] = "XCUIElementTypeSecureTextField"
178
+ add_selector(params)
179
+ end
180
+
181
+ # @return [TestaAppiumDriver::Locator]
182
+ def secure_text_fields(params = {})
183
+ params[:type] = "XCUIElementTypeSecureTextField"
184
+ params[:single] = false
185
+ add_selector(params)
186
+ end
166
187
  end
167
188
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TestaAppiumDriver
4
- VERSION = "0.1.5"
4
+ VERSION = "0.1.9"
5
5
  end
@@ -39,7 +39,41 @@
39
39
  </component>
40
40
  <component name="RakeTasksCache">
41
41
  <option name="myRootTask">
42
- <RakeTaskImpl id="rake" />
42
+ <RakeTaskImpl id="rake">
43
+ <subtasks>
44
+ <RakeTaskImpl description="Build testa_appium_driver-0.1.5.gem into the pkg directory" fullCommand="build" id="build" />
45
+ <RakeTaskImpl id="build">
46
+ <subtasks>
47
+ <RakeTaskImpl description="Generate SHA512 checksum if testa_appium_driver-0.1.5.gem into the checksums directory" fullCommand="build:checksum" id="checksum" />
48
+ </subtasks>
49
+ </RakeTaskImpl>
50
+ <RakeTaskImpl description="Remove any temporary products" fullCommand="clean" id="clean" />
51
+ <RakeTaskImpl description="Remove any generated files" fullCommand="clobber" id="clobber" />
52
+ <RakeTaskImpl description="Build and install testa_appium_driver-0.1.5.gem into system gems" fullCommand="install" id="install" />
53
+ <RakeTaskImpl id="install">
54
+ <subtasks>
55
+ <RakeTaskImpl description="Build and install testa_appium_driver-0.1.5.gem into system gems without network access" fullCommand="install:local" id="local" />
56
+ </subtasks>
57
+ </RakeTaskImpl>
58
+ <RakeTaskImpl description="Create tag v0.1.5 and build and push testa_appium_driver-0.1.5.gem to rubygems.org" fullCommand="release[remote]" id="release[remote]" />
59
+ <RakeTaskImpl description="Run RuboCop" fullCommand="rubocop" id="rubocop" />
60
+ <RakeTaskImpl id="rubocop">
61
+ <subtasks>
62
+ <RakeTaskImpl description="Auto-correct RuboCop offenses" fullCommand="rubocop:auto_correct" id="auto_correct" />
63
+ </subtasks>
64
+ </RakeTaskImpl>
65
+ <RakeTaskImpl description="Run RSpec code examples" fullCommand="spec" id="spec" />
66
+ <RakeTaskImpl description="" fullCommand="default" id="default" />
67
+ <RakeTaskImpl description="" fullCommand="release" id="release" />
68
+ <RakeTaskImpl id="release">
69
+ <subtasks>
70
+ <RakeTaskImpl description="" fullCommand="release:guard_clean" id="guard_clean" />
71
+ <RakeTaskImpl description="" fullCommand="release:rubygem_push" id="rubygem_push" />
72
+ <RakeTaskImpl description="" fullCommand="release:source_control_push" id="source_control_push" />
73
+ </subtasks>
74
+ </RakeTaskImpl>
75
+ </subtasks>
76
+ </RakeTaskImpl>
43
77
  </option>
44
78
  </component>
45
79
  </module>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: testa_appium_driver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - karlo.razumovic
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-08-24 00:00:00.000000000 Z
11
+ date: 2021-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: appium_lib_core
@@ -80,6 +80,7 @@ files:
80
80
  - ".idea/inspectionProfiles/Project_Default.xml"
81
81
  - ".idea/misc.xml"
82
82
  - ".idea/modules.xml"
83
+ - ".idea/runConfigurations.xml"
83
84
  - ".idea/runConfigurations/Android_Test.xml"
84
85
  - ".idea/sshConfigs.xml"
85
86
  - ".idea/vcs.xml"