testa_appium_driver 0.1.5 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -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"