testa_appium_driver 0.1.11 → 0.1.12
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.
- checksums.yaml +4 -4
- data/.gitignore +15 -15
- data/.idea/deployment.xml +21 -21
- data/.idea/inspectionProfiles/Project_Default.xml +8 -8
- data/.idea/misc.xml +5 -5
- data/.idea/modules.xml +7 -7
- data/.idea/runConfigurations/Android_Test.xml +41 -41
- data/.idea/runConfigurations.xml +9 -9
- data/.idea/sshConfigs.xml +12 -12
- data/.idea/vcs.xml +5 -5
- data/.idea/webServers.xml +20 -20
- data/.rspec +3 -3
- data/.rubocop.yml +13 -13
- data/CHANGELOG.md +5 -5
- data/CODE_OF_CONDUCT.md +102 -102
- data/Gemfile +12 -12
- data/LICENSE.txt +21 -21
- data/README.md +378 -378
- data/Rakefile +12 -12
- data/bin/console +17 -17
- data/bin/setup +8 -8
- data/lib/testa_appium_driver/android/class_selectors.rb +437 -437
- data/lib/testa_appium_driver/android/driver.rb +69 -69
- data/lib/testa_appium_driver/android/locator/attributes.rb +113 -113
- data/lib/testa_appium_driver/android/locator.rb +141 -141
- data/lib/testa_appium_driver/android/scroll_actions/uiautomator_scroll_actions.rb +61 -61
- data/lib/testa_appium_driver/android/selenium_element.rb +7 -7
- data/lib/testa_appium_driver/common/bounds.rb +149 -149
- data/lib/testa_appium_driver/common/constants.rb +36 -36
- data/lib/testa_appium_driver/common/exceptions/strategy_mix_exception.rb +11 -11
- data/lib/testa_appium_driver/common/helpers.rb +270 -270
- data/lib/testa_appium_driver/common/locator/scroll_actions.rb +397 -397
- data/lib/testa_appium_driver/common/locator.rb +610 -610
- data/lib/testa_appium_driver/common/scroll_actions/json_wire_scroll_actions.rb +3 -3
- data/lib/testa_appium_driver/common/scroll_actions/w3c_scroll_actions.rb +237 -237
- data/lib/testa_appium_driver/common/scroll_actions.rb +246 -246
- data/lib/testa_appium_driver/common/selenium_element.rb +19 -19
- data/lib/testa_appium_driver/driver.rb +312 -312
- data/lib/testa_appium_driver/ios/driver.rb +48 -48
- data/lib/testa_appium_driver/ios/locator/attributes.rb +80 -80
- data/lib/testa_appium_driver/ios/locator.rb +70 -70
- data/lib/testa_appium_driver/ios/selenium_element.rb +6 -6
- data/lib/testa_appium_driver/ios/type_selectors.rb +187 -187
- data/lib/testa_appium_driver/version.rb +5 -5
- data/lib/testa_appium_driver.rb +6 -6
- data/testa_appium_driver.gemspec +41 -41
- data/testa_appium_driver.iml +27 -78
- metadata +3 -3
@@ -1,312 +1,312 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'em/pure_ruby'
|
4
|
-
require 'appium_lib_core'
|
5
|
-
|
6
|
-
require_relative 'common/bounds'
|
7
|
-
require_relative 'common/exceptions/strategy_mix_exception'
|
8
|
-
require_relative 'common/helpers'
|
9
|
-
require_relative 'common/locator'
|
10
|
-
require_relative 'common/scroll_actions'
|
11
|
-
require_relative 'common/selenium_element'
|
12
|
-
|
13
|
-
module TestaAppiumDriver
|
14
|
-
class Driver
|
15
|
-
include Helpers
|
16
|
-
|
17
|
-
# @return [::Appium::Core::Base::Driver] the ruby_lib_core appium driver
|
18
|
-
attr_accessor :driver
|
19
|
-
|
20
|
-
# @return [String] iOS or Android
|
21
|
-
attr_reader :device
|
22
|
-
|
23
|
-
# @return [String] driver automation name (uiautomator2 or xcuitest)
|
24
|
-
attr_reader :automation_name
|
25
|
-
|
26
|
-
# custom options
|
27
|
-
# - default_find_strategy: default strategy to be used for finding elements. Available strategies :uiautomator or :xpath
|
28
|
-
# - default_scroll_strategy: default strategy to be used for scrolling. Available strategies: :uiautomator(android only), :w3c
|
29
|
-
def initialize(opts = {})
|
30
|
-
@testa_opts = opts[:testa_appium_driver] || {}
|
31
|
-
|
32
|
-
core = Appium::Core.for(opts)
|
33
|
-
extend_for(core.device, core.automation_name)
|
34
|
-
@device = core.device
|
35
|
-
@automation_name = core.automation_name
|
36
|
-
|
37
|
-
handle_testa_opts
|
38
|
-
|
39
|
-
@driver = core.start_driver
|
40
|
-
invalidate_cache
|
41
|
-
|
42
|
-
|
43
|
-
disable_wait_for_idle
|
44
|
-
disable_implicit_wait
|
45
|
-
|
46
|
-
Selenium::WebDriver::Element.set_driver(self, opts[:caps][:udid])
|
47
|
-
end
|
48
|
-
|
49
|
-
|
50
|
-
# invalidates current find_element cache
|
51
|
-
def invalidate_cache
|
52
|
-
@cache = {
|
53
|
-
strategy: nil,
|
54
|
-
selector: nil,
|
55
|
-
element: nil,
|
56
|
-
from_element: nil,
|
57
|
-
time: Time.at(0)
|
58
|
-
}
|
59
|
-
end
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
# Executes the find_element with the resolved locator strategy and selector. Find_element might be skipped if cache is hit.
|
65
|
-
# Cache stores last executed find_element with given selector, strategy and from_element. If given values are the same within
|
66
|
-
# last 5 seconds element is retrieved from cache.
|
67
|
-
# @param [TestaAppiumDriver::Locator, TestaAppiumDriver::Driver] from_element element from which start the search
|
68
|
-
# @param [Boolean] single fetch single or multiple results
|
69
|
-
# @param [Array<Hash>] strategies_and_selectors array of usable strategies and selectors
|
70
|
-
# @param [Boolean] skip_cache to skip checking and storing cache
|
71
|
-
# @return [Selenium::WebDriver::Element, Array] element is returned if single is true, array otherwise
|
72
|
-
def execute(from_element, single, strategies_and_selectors, skip_cache: false, ignore_implicit_wait: false)
|
73
|
-
|
74
|
-
# if user wants to wait for element to exist, he can use wait_until_present
|
75
|
-
start_time = Time.now.to_f
|
76
|
-
ss_index = 0
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
# resolve from_element unique id, so that we can cache it properly
|
82
|
-
from_element_id = from_element.instance_of?(TestaAppiumDriver::Locator) ? from_element.strategies_and_selectors : nil
|
83
|
-
|
84
|
-
begin
|
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
|
91
|
-
@cache[:time] + 5 <= Time.now || # cache miss, older than 5 seconds
|
92
|
-
@cache[:strategy] != ss.keys[0] || # cache miss, different find strategy
|
93
|
-
@cache[:from_element_id] != from_element_id || # cache miss, search is started from different element
|
94
|
-
skip_cache # cache is skipped
|
95
|
-
|
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
|
104
|
-
else
|
105
|
-
if single
|
106
|
-
execute_result = from_element.find_element(ss)
|
107
|
-
else
|
108
|
-
execute_result = from_element.find_elements(ss)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
unless skip_cache
|
115
|
-
@cache[:selector] = ss.values[0]
|
116
|
-
@cache[:strategy] = ss.keys[0]
|
117
|
-
@cache[:time] = Time.now
|
118
|
-
@cache[:from_element_id] = from_element_id
|
119
|
-
@cache[:element] = execute_result
|
120
|
-
end
|
121
|
-
else
|
122
|
-
# this is a cache hit, use the element from cache
|
123
|
-
execute_result = @cache[:element]
|
124
|
-
puts "Using cache from #{@cache[:time].strftime("%H:%M:%S.%L")}, strategy: #{@cache[:strategy]}"
|
125
|
-
end
|
126
|
-
rescue => e
|
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
|
134
|
-
end
|
135
|
-
|
136
|
-
execute_result
|
137
|
-
end
|
138
|
-
|
139
|
-
|
140
|
-
# method missing is used to forward methods to the actual appium driver
|
141
|
-
# after the method is executed, find element cache is invalidated
|
142
|
-
def method_missing(method, *args, &block)
|
143
|
-
r = @driver.send(method, *args, &block)
|
144
|
-
invalidate_cache
|
145
|
-
r
|
146
|
-
end
|
147
|
-
|
148
|
-
# disables implicit wait
|
149
|
-
def disable_implicit_wait
|
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})
|
155
|
-
end
|
156
|
-
|
157
|
-
|
158
|
-
# disables wait for idle, only executed for android devices
|
159
|
-
def disable_wait_for_idle
|
160
|
-
if @device == :android
|
161
|
-
@wait_for_idle_timeout = @driver.settings.get["waitForIdleTimeout"]
|
162
|
-
@driver.update_settings({waitForIdleTimeout: 1})
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
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
|
186
|
-
end
|
187
|
-
|
188
|
-
|
189
|
-
# @@return [String] current package under test
|
190
|
-
def current_package
|
191
|
-
@driver.current_package
|
192
|
-
end
|
193
|
-
|
194
|
-
|
195
|
-
def window_size
|
196
|
-
@driver.window_size
|
197
|
-
end
|
198
|
-
|
199
|
-
def back
|
200
|
-
@driver.back
|
201
|
-
end
|
202
|
-
|
203
|
-
|
204
|
-
def is_keyboard_shown?
|
205
|
-
@driver.is_keyboard_shown
|
206
|
-
end
|
207
|
-
|
208
|
-
def hide_keyboard
|
209
|
-
@driver.hide_keyboard
|
210
|
-
end
|
211
|
-
|
212
|
-
def home_key
|
213
|
-
@driver.press_keycode(3)
|
214
|
-
end
|
215
|
-
def tab_key
|
216
|
-
@driver.press_keycode(61)
|
217
|
-
end
|
218
|
-
|
219
|
-
def dpad_up_key
|
220
|
-
@driver.press_keycode(19)
|
221
|
-
end
|
222
|
-
|
223
|
-
def dpad_down_key
|
224
|
-
@driver.press_keycode(20)
|
225
|
-
end
|
226
|
-
|
227
|
-
def dpad_right_key
|
228
|
-
@driver.press_keycode(22)
|
229
|
-
end
|
230
|
-
|
231
|
-
def dpad_left_key
|
232
|
-
@driver.press_keycode(23)
|
233
|
-
end
|
234
|
-
|
235
|
-
def enter_key
|
236
|
-
@driver.press_keycode(66)
|
237
|
-
end
|
238
|
-
|
239
|
-
def press_keycode(code)
|
240
|
-
@driver.press_keycode(code)
|
241
|
-
end
|
242
|
-
|
243
|
-
def long_press_keycode(code)
|
244
|
-
@driver.long_press_keycode(code)
|
245
|
-
end
|
246
|
-
|
247
|
-
def click(x, y)
|
248
|
-
ws = driver.window_size
|
249
|
-
window_width = ws.width.to_i
|
250
|
-
window_height = ws.height.to_i
|
251
|
-
if x.kind_of? Integer
|
252
|
-
if x < 0
|
253
|
-
x = window_width + x
|
254
|
-
end
|
255
|
-
elsif x.kind_of? Float
|
256
|
-
x = window_width*x
|
257
|
-
else
|
258
|
-
raise "x value #{x} not supported"
|
259
|
-
end
|
260
|
-
|
261
|
-
if y.kind_of? Integer
|
262
|
-
if y < 0
|
263
|
-
y = window_height + y
|
264
|
-
end
|
265
|
-
elsif y.kind_of? Float
|
266
|
-
y = window_height*y
|
267
|
-
end
|
268
|
-
|
269
|
-
|
270
|
-
action_builder = @driver.action
|
271
|
-
f1 = action_builder.add_pointer_input(:touch, "finger1")
|
272
|
-
f1.create_pointer_move(duration: 0, x: x, y: y, origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
|
273
|
-
f1.create_pointer_down(:left)
|
274
|
-
f1.create_pointer_up(:left)
|
275
|
-
@driver.perform_actions [f1]
|
276
|
-
end
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
# @return [Array<Selenium::WebDriver::Element] array of 2 elements, the first element without children and the last element without children in the current page
|
281
|
-
def first_and_last_leaf(from_element = @driver)
|
282
|
-
elements = from_element.find_elements(xpath: "//*[not(*)]")
|
283
|
-
return nil if elements.count == 0
|
284
|
-
[elements[0], elements[-1]]
|
285
|
-
end
|
286
|
-
|
287
|
-
private
|
288
|
-
def extend_for(device, automation_name)
|
289
|
-
case device
|
290
|
-
when :android
|
291
|
-
case automation_name
|
292
|
-
when :uiautomator2
|
293
|
-
require_relative 'android/driver'
|
294
|
-
else
|
295
|
-
raise "Testa appium driver not supported for #{automation_name} automation"
|
296
|
-
end
|
297
|
-
when :ios, :tvos
|
298
|
-
case automation_name
|
299
|
-
when :xcuitest
|
300
|
-
require_relative 'ios/driver'
|
301
|
-
else
|
302
|
-
raise "Testa appium driver not supported for #{automation_name} automation"
|
303
|
-
end
|
304
|
-
else
|
305
|
-
raise "Unknown device #{device}, should be either android, ios or tvos"
|
306
|
-
end
|
307
|
-
end
|
308
|
-
|
309
|
-
|
310
|
-
end
|
311
|
-
|
312
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'em/pure_ruby'
|
4
|
+
require 'appium_lib_core'
|
5
|
+
|
6
|
+
require_relative 'common/bounds'
|
7
|
+
require_relative 'common/exceptions/strategy_mix_exception'
|
8
|
+
require_relative 'common/helpers'
|
9
|
+
require_relative 'common/locator'
|
10
|
+
require_relative 'common/scroll_actions'
|
11
|
+
require_relative 'common/selenium_element'
|
12
|
+
|
13
|
+
module TestaAppiumDriver
|
14
|
+
class Driver
|
15
|
+
include Helpers
|
16
|
+
|
17
|
+
# @return [::Appium::Core::Base::Driver] the ruby_lib_core appium driver
|
18
|
+
attr_accessor :driver
|
19
|
+
|
20
|
+
# @return [String] iOS or Android
|
21
|
+
attr_reader :device
|
22
|
+
|
23
|
+
# @return [String] driver automation name (uiautomator2 or xcuitest)
|
24
|
+
attr_reader :automation_name
|
25
|
+
|
26
|
+
# custom options
|
27
|
+
# - default_find_strategy: default strategy to be used for finding elements. Available strategies :uiautomator or :xpath
|
28
|
+
# - default_scroll_strategy: default strategy to be used for scrolling. Available strategies: :uiautomator(android only), :w3c
|
29
|
+
def initialize(opts = {})
|
30
|
+
@testa_opts = opts[:testa_appium_driver] || {}
|
31
|
+
|
32
|
+
core = Appium::Core.for(opts)
|
33
|
+
extend_for(core.device, core.automation_name)
|
34
|
+
@device = core.device
|
35
|
+
@automation_name = core.automation_name
|
36
|
+
|
37
|
+
handle_testa_opts
|
38
|
+
|
39
|
+
@driver = core.start_driver
|
40
|
+
invalidate_cache
|
41
|
+
|
42
|
+
|
43
|
+
disable_wait_for_idle
|
44
|
+
disable_implicit_wait
|
45
|
+
|
46
|
+
Selenium::WebDriver::Element.set_driver(self, opts[:caps][:udid])
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
# invalidates current find_element cache
|
51
|
+
def invalidate_cache
|
52
|
+
@cache = {
|
53
|
+
strategy: nil,
|
54
|
+
selector: nil,
|
55
|
+
element: nil,
|
56
|
+
from_element: nil,
|
57
|
+
time: Time.at(0)
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
# Executes the find_element with the resolved locator strategy and selector. Find_element might be skipped if cache is hit.
|
65
|
+
# Cache stores last executed find_element with given selector, strategy and from_element. If given values are the same within
|
66
|
+
# last 5 seconds element is retrieved from cache.
|
67
|
+
# @param [TestaAppiumDriver::Locator, TestaAppiumDriver::Driver] from_element element from which start the search
|
68
|
+
# @param [Boolean] single fetch single or multiple results
|
69
|
+
# @param [Array<Hash>] strategies_and_selectors array of usable strategies and selectors
|
70
|
+
# @param [Boolean] skip_cache to skip checking and storing cache
|
71
|
+
# @return [Selenium::WebDriver::Element, Array] element is returned if single is true, array otherwise
|
72
|
+
def execute(from_element, single, strategies_and_selectors, skip_cache: false, ignore_implicit_wait: false)
|
73
|
+
|
74
|
+
# if user wants to wait for element to exist, he can use wait_until_present
|
75
|
+
start_time = Time.now.to_f
|
76
|
+
ss_index = 0
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
# resolve from_element unique id, so that we can cache it properly
|
82
|
+
from_element_id = from_element.instance_of?(TestaAppiumDriver::Locator) ? from_element.strategies_and_selectors : nil
|
83
|
+
|
84
|
+
begin
|
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
|
91
|
+
@cache[:time] + 5 <= Time.now || # cache miss, older than 5 seconds
|
92
|
+
@cache[:strategy] != ss.keys[0] || # cache miss, different find strategy
|
93
|
+
@cache[:from_element_id] != from_element_id || # cache miss, search is started from different element
|
94
|
+
skip_cache # cache is skipped
|
95
|
+
|
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
|
104
|
+
else
|
105
|
+
if single
|
106
|
+
execute_result = from_element.find_element(ss)
|
107
|
+
else
|
108
|
+
execute_result = from_element.find_elements(ss)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
|
114
|
+
unless skip_cache
|
115
|
+
@cache[:selector] = ss.values[0]
|
116
|
+
@cache[:strategy] = ss.keys[0]
|
117
|
+
@cache[:time] = Time.now
|
118
|
+
@cache[:from_element_id] = from_element_id
|
119
|
+
@cache[:element] = execute_result
|
120
|
+
end
|
121
|
+
else
|
122
|
+
# this is a cache hit, use the element from cache
|
123
|
+
execute_result = @cache[:element]
|
124
|
+
puts "Using cache from #{@cache[:time].strftime("%H:%M:%S.%L")}, strategy: #{@cache[:strategy]}"
|
125
|
+
end
|
126
|
+
rescue => e
|
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
|
134
|
+
end
|
135
|
+
|
136
|
+
execute_result
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
# method missing is used to forward methods to the actual appium driver
|
141
|
+
# after the method is executed, find element cache is invalidated
|
142
|
+
def method_missing(method, *args, &block)
|
143
|
+
r = @driver.send(method, *args, &block)
|
144
|
+
invalidate_cache
|
145
|
+
r
|
146
|
+
end
|
147
|
+
|
148
|
+
# disables implicit wait
|
149
|
+
def disable_implicit_wait
|
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})
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
# disables wait for idle, only executed for android devices
|
159
|
+
def disable_wait_for_idle
|
160
|
+
if @device == :android
|
161
|
+
@wait_for_idle_timeout = @driver.settings.get["waitForIdleTimeout"]
|
162
|
+
@driver.update_settings({waitForIdleTimeout: 1})
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
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
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
# @@return [String] current package under test
|
190
|
+
def current_package
|
191
|
+
@driver.current_package
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
def window_size
|
196
|
+
@driver.window_size
|
197
|
+
end
|
198
|
+
|
199
|
+
def back
|
200
|
+
@driver.back
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
def is_keyboard_shown?
|
205
|
+
@driver.is_keyboard_shown
|
206
|
+
end
|
207
|
+
|
208
|
+
def hide_keyboard
|
209
|
+
@driver.hide_keyboard
|
210
|
+
end
|
211
|
+
|
212
|
+
def home_key
|
213
|
+
@driver.press_keycode(3)
|
214
|
+
end
|
215
|
+
def tab_key
|
216
|
+
@driver.press_keycode(61)
|
217
|
+
end
|
218
|
+
|
219
|
+
def dpad_up_key
|
220
|
+
@driver.press_keycode(19)
|
221
|
+
end
|
222
|
+
|
223
|
+
def dpad_down_key
|
224
|
+
@driver.press_keycode(20)
|
225
|
+
end
|
226
|
+
|
227
|
+
def dpad_right_key
|
228
|
+
@driver.press_keycode(22)
|
229
|
+
end
|
230
|
+
|
231
|
+
def dpad_left_key
|
232
|
+
@driver.press_keycode(23)
|
233
|
+
end
|
234
|
+
|
235
|
+
def enter_key
|
236
|
+
@driver.press_keycode(66)
|
237
|
+
end
|
238
|
+
|
239
|
+
def press_keycode(code)
|
240
|
+
@driver.press_keycode(code)
|
241
|
+
end
|
242
|
+
|
243
|
+
def long_press_keycode(code)
|
244
|
+
@driver.long_press_keycode(code)
|
245
|
+
end
|
246
|
+
|
247
|
+
def click(x, y)
|
248
|
+
ws = driver.window_size
|
249
|
+
window_width = ws.width.to_i
|
250
|
+
window_height = ws.height.to_i
|
251
|
+
if x.kind_of? Integer
|
252
|
+
if x < 0
|
253
|
+
x = window_width + x
|
254
|
+
end
|
255
|
+
elsif x.kind_of? Float
|
256
|
+
x = window_width*x
|
257
|
+
else
|
258
|
+
raise "x value #{x} not supported"
|
259
|
+
end
|
260
|
+
|
261
|
+
if y.kind_of? Integer
|
262
|
+
if y < 0
|
263
|
+
y = window_height + y
|
264
|
+
end
|
265
|
+
elsif y.kind_of? Float
|
266
|
+
y = window_height*y
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
action_builder = @driver.action
|
271
|
+
f1 = action_builder.add_pointer_input(:touch, "finger1")
|
272
|
+
f1.create_pointer_move(duration: 0, x: x, y: y, origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
|
273
|
+
f1.create_pointer_down(:left)
|
274
|
+
f1.create_pointer_up(:left)
|
275
|
+
@driver.perform_actions [f1]
|
276
|
+
end
|
277
|
+
|
278
|
+
|
279
|
+
|
280
|
+
# @return [Array<Selenium::WebDriver::Element] array of 2 elements, the first element without children and the last element without children in the current page
|
281
|
+
def first_and_last_leaf(from_element = @driver)
|
282
|
+
elements = from_element.find_elements(xpath: "//*[not(*)]")
|
283
|
+
return nil if elements.count == 0
|
284
|
+
[elements[0], elements[-1]]
|
285
|
+
end
|
286
|
+
|
287
|
+
private
|
288
|
+
def extend_for(device, automation_name)
|
289
|
+
case device
|
290
|
+
when :android
|
291
|
+
case automation_name
|
292
|
+
when :uiautomator2
|
293
|
+
require_relative 'android/driver'
|
294
|
+
else
|
295
|
+
raise "Testa appium driver not supported for #{automation_name} automation"
|
296
|
+
end
|
297
|
+
when :ios, :tvos
|
298
|
+
case automation_name
|
299
|
+
when :xcuitest
|
300
|
+
require_relative 'ios/driver'
|
301
|
+
else
|
302
|
+
raise "Testa appium driver not supported for #{automation_name} automation"
|
303
|
+
end
|
304
|
+
else
|
305
|
+
raise "Unknown device #{device}, should be either android, ios or tvos"
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
|
310
|
+
end
|
311
|
+
|
312
|
+
end
|