testa_appium_driver 0.1.0
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 +7 -0
- data/.gitignore +15 -0
- data/.idea/deployment.xml +21 -0
- data/.idea/inspectionProfiles/Project_Default.xml +9 -0
- data/.idea/misc.xml +6 -0
- data/.idea/modules.xml +8 -0
- data/.idea/runConfigurations/Android_Test.xml +42 -0
- data/.idea/sshConfigs.xml +10 -0
- data/.idea/vcs.xml +6 -0
- data/.idea/webServers.xml +14 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +102 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +52 -0
- data/Rakefile +12 -0
- data/bin/console +17 -0
- data/bin/setup +8 -0
- data/lib/testa_appium_driver.rb +6 -0
- data/lib/testa_appium_driver/android/class_selectors.rb +353 -0
- data/lib/testa_appium_driver/android/driver.rb +52 -0
- data/lib/testa_appium_driver/android/locator.rb +115 -0
- data/lib/testa_appium_driver/android/locator/attributes.rb +116 -0
- data/lib/testa_appium_driver/android/scroll_actions/uiautomator_scroll_actions.rb +77 -0
- data/lib/testa_appium_driver/android/selenium_element.rb +8 -0
- data/lib/testa_appium_driver/common/bounds.rb +150 -0
- data/lib/testa_appium_driver/common/constants.rb +33 -0
- data/lib/testa_appium_driver/common/exceptions/strategy_mix_exception.rb +12 -0
- data/lib/testa_appium_driver/common/helpers.rb +242 -0
- data/lib/testa_appium_driver/common/locator.rb +371 -0
- data/lib/testa_appium_driver/common/locator/scroll_actions.rb +287 -0
- data/lib/testa_appium_driver/common/scroll_actions.rb +253 -0
- data/lib/testa_appium_driver/common/scroll_actions/json_wire_scroll_actions.rb +4 -0
- data/lib/testa_appium_driver/common/scroll_actions/w3c_scroll_actions.rb +261 -0
- data/lib/testa_appium_driver/driver.rb +226 -0
- data/lib/testa_appium_driver/ios/driver.rb +35 -0
- data/lib/testa_appium_driver/ios/locator.rb +40 -0
- data/lib/testa_appium_driver/ios/locator/attributes.rb +79 -0
- data/lib/testa_appium_driver/ios/selenium_element.rb +7 -0
- data/lib/testa_appium_driver/ios/type_selectors.rb +167 -0
- data/lib/testa_appium_driver/version.rb +5 -0
- data/testa_appium_driver.gemspec +40 -0
- data/testa_appium_driver.iml +79 -0
- metadata +147 -0
@@ -0,0 +1,261 @@
|
|
1
|
+
module TestaAppiumDriver
|
2
|
+
class ScrollActions
|
3
|
+
|
4
|
+
private
|
5
|
+
# @return [Array]
|
6
|
+
def w3c_each(skip_scroll_to_start, &block)
|
7
|
+
elements = []
|
8
|
+
begin
|
9
|
+
@driver.disable_wait_for_idle
|
10
|
+
@driver.disable_implicit_wait
|
11
|
+
default_deadzone!
|
12
|
+
|
13
|
+
iterations = 0
|
14
|
+
|
15
|
+
|
16
|
+
scroll_to_start unless skip_scroll_to_start
|
17
|
+
|
18
|
+
until is_end_of_scroll?
|
19
|
+
matches = @locator.execute(skip_cache: true)
|
20
|
+
matches.each_with_index do |m|
|
21
|
+
elements << m
|
22
|
+
if block_given? # block is given
|
23
|
+
block.call(m) # use call to execute the block
|
24
|
+
else # the value of block_argument becomes nil if you didn't give a block
|
25
|
+
# block was not given
|
26
|
+
end
|
27
|
+
end
|
28
|
+
iterations += 1
|
29
|
+
break if !@max_scrolls.nil? && iterations == @max_scrolls
|
30
|
+
end
|
31
|
+
rescue => e
|
32
|
+
raise e
|
33
|
+
ensure
|
34
|
+
@driver.enable_implicit_wait
|
35
|
+
@driver.enable_wait_for_idle
|
36
|
+
end
|
37
|
+
elements
|
38
|
+
end
|
39
|
+
|
40
|
+
def w3c_align(with)
|
41
|
+
@driver.disable_wait_for_idle
|
42
|
+
default_deadzone!
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
@locator.scroll_to unless @raise # called with !
|
47
|
+
|
48
|
+
element = @locator.execute
|
49
|
+
@driver.disable_implicit_wait
|
50
|
+
|
51
|
+
case with
|
52
|
+
when :top
|
53
|
+
page_down if is_aligned?(with, element)
|
54
|
+
when :bottom
|
55
|
+
page_up if is_aligned?(with, element)
|
56
|
+
when :right
|
57
|
+
page_right if is_aligned?(with, element)
|
58
|
+
when :left
|
59
|
+
page_left if is_aligned?(with, element)
|
60
|
+
else
|
61
|
+
raise "Unsupported align with option: #{with}"
|
62
|
+
end
|
63
|
+
|
64
|
+
timeout = 0
|
65
|
+
until is_aligned?(with, element) || timeout == 3
|
66
|
+
w3c_attempt_align(with)
|
67
|
+
timeout += 1
|
68
|
+
end
|
69
|
+
|
70
|
+
@driver.enable_implicit_wait
|
71
|
+
@driver.enable_wait_for_idle
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def w3c_attempt_align(with)
|
76
|
+
case with
|
77
|
+
when :top
|
78
|
+
y0 = @bounds.bottom_right.y - @deadzone[:bottom]
|
79
|
+
y1 = y0 - @align_offset
|
80
|
+
x0 = @bounds.width / 2
|
81
|
+
x1 = x0
|
82
|
+
scroll_direction = :down
|
83
|
+
when :bottom
|
84
|
+
y0 = @bounds.top_left.y + @deadzone[:top]
|
85
|
+
y1 = y0 + @align_offset
|
86
|
+
x0 = @bounds.width / 2
|
87
|
+
x1 = x0
|
88
|
+
scroll_direction = :up
|
89
|
+
when :left
|
90
|
+
x0 = @bounds.bottom_right.x - @deadzone[:right]
|
91
|
+
x1 = x0 - @align_offset
|
92
|
+
y0 = @bounds.height / 2
|
93
|
+
y1 = y0
|
94
|
+
scroll_direction = :right
|
95
|
+
when :right
|
96
|
+
x0 = @bounds.top_left.x + @deadzone[:top]
|
97
|
+
x1 = x0 + @align_offset
|
98
|
+
y0 = @bounds.height / 2
|
99
|
+
y1 = y0
|
100
|
+
scroll_direction = :left
|
101
|
+
else
|
102
|
+
raise "Unsupported align with option: #{with}"
|
103
|
+
end
|
104
|
+
|
105
|
+
x1, y1 = apply_w3c_correction(x1, y1, scroll_direction) if @driver.device == :android
|
106
|
+
w3c_action(x0, y0, x1, y1, SCROLL_ACTION_TYPE_SCROLL)
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
def w3c_scroll_to(direction)
|
111
|
+
|
112
|
+
rounds = 0
|
113
|
+
max_scrolls_reached = false
|
114
|
+
end_of_scroll_reached = false
|
115
|
+
until @locator.exists? || end_of_scroll_reached
|
116
|
+
end_of_scroll_reached = is_end_of_scroll?
|
117
|
+
case direction
|
118
|
+
when :down
|
119
|
+
page_down
|
120
|
+
when :right
|
121
|
+
page_right
|
122
|
+
when :left
|
123
|
+
page_left
|
124
|
+
when :up
|
125
|
+
page_up
|
126
|
+
else
|
127
|
+
scroll_to_start
|
128
|
+
@previous_elements = nil
|
129
|
+
if @scrollable.scroll_orientation == :vertical
|
130
|
+
direction = :down
|
131
|
+
else
|
132
|
+
direction = :right
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
rounds += 1
|
137
|
+
|
138
|
+
max_scrolls_reached = true if rounds == @max_scrolls
|
139
|
+
break if rounds == @max_scrolls
|
140
|
+
end
|
141
|
+
raise Selenium::WebDriver::Error::NoSuchElementError if max_scrolls_reached || end_of_scroll_reached
|
142
|
+
end
|
143
|
+
|
144
|
+
def w3c_scroll_to_start_or_end(type)
|
145
|
+
@driver.disable_wait_for_idle
|
146
|
+
@driver.disable_implicit_wait
|
147
|
+
default_deadzone!
|
148
|
+
|
149
|
+
@previous_elements = nil
|
150
|
+
|
151
|
+
|
152
|
+
if type == :start
|
153
|
+
if @scrollable.scroll_orientation == :vertical
|
154
|
+
method = "fling_up"
|
155
|
+
else
|
156
|
+
method = "fling_left"
|
157
|
+
end
|
158
|
+
else
|
159
|
+
if @scrollable.scroll_orientation == :vertical
|
160
|
+
method = "fling_down"
|
161
|
+
else
|
162
|
+
method = "fling_right"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
iterations = 0
|
167
|
+
until is_end_of_scroll? || iterations >= 3
|
168
|
+
self.send(method)
|
169
|
+
iterations += 1
|
170
|
+
end
|
171
|
+
|
172
|
+
# reset the flag for end of scroll elements
|
173
|
+
@previous_elements = nil
|
174
|
+
|
175
|
+
@driver.enable_implicit_wait
|
176
|
+
@driver.enable_wait_for_idle
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
def w3c_page_or_fling(type, direction)
|
181
|
+
@driver.disable_wait_for_idle
|
182
|
+
@driver.disable_implicit_wait
|
183
|
+
default_deadzone!
|
184
|
+
|
185
|
+
if direction == :down || direction == :up
|
186
|
+
if direction == :down
|
187
|
+
y0 = @bounds.bottom_right.y - @deadzone[:bottom].to_i
|
188
|
+
y1 = @bounds.top_left.y + @deadzone[:top].to_i
|
189
|
+
else
|
190
|
+
y0 = @bounds.top_left.y + @deadzone[:top].to_i
|
191
|
+
y1 = @bounds.bottom_right.y - @deadzone[:bottom].to_i
|
192
|
+
end
|
193
|
+
x0 = @bounds.width / 2
|
194
|
+
x1 = x0
|
195
|
+
else
|
196
|
+
if direction == :right
|
197
|
+
x0 = @bounds.bottom_right.x - @deadzone[:right].to_i
|
198
|
+
x1 = @bounds.top_left.x + @deadzone[:left].to_i
|
199
|
+
else
|
200
|
+
x0 = @bounds.top_left.x + @deadzone[:left].to_i
|
201
|
+
x1 = @bounds.bottom_right.x - @deadzone[:right].to_i
|
202
|
+
end
|
203
|
+
y0 = @bounds.height / 2
|
204
|
+
y1 = y0
|
205
|
+
end
|
206
|
+
x1, y1 = apply_w3c_correction(x1, y1, direction) if @driver.device == :android
|
207
|
+
|
208
|
+
|
209
|
+
w3c_action(x0, y0, x1, y1, type)
|
210
|
+
|
211
|
+
@driver.enable_implicit_wait
|
212
|
+
@driver.enable_wait_for_idle
|
213
|
+
end
|
214
|
+
|
215
|
+
|
216
|
+
def w3c_action(x0, y0, x1, y1, type)
|
217
|
+
if type == SCROLL_ACTION_TYPE_SCROLL
|
218
|
+
duration = 1.8
|
219
|
+
elsif type == SCROLL_ACTION_TYPE_FLING
|
220
|
+
duration = 0.1
|
221
|
+
elsif type == SCROLL_ACTION_TYPE_DRAG
|
222
|
+
duration = 3.5
|
223
|
+
else
|
224
|
+
raise "Unknown scroll action type #{type}"
|
225
|
+
end
|
226
|
+
|
227
|
+
action_builder = @driver.action
|
228
|
+
f1 = action_builder.add_pointer_input(:touch, "finger1")
|
229
|
+
f1.create_pointer_move(duration: 0, x: x0, y: y0, origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
|
230
|
+
f1.create_pointer_down(:left)
|
231
|
+
|
232
|
+
f1.create_pointer_move(duration: duration, x: x1, y: y1, origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
|
233
|
+
unless type == SCROLL_ACTION_TYPE_FLING
|
234
|
+
# with this move we prevent flinging/overscroll
|
235
|
+
f1.create_pointer_move(duration: 0.5, x: x1, y: y1, origin: ::Selenium::WebDriver::Interactions::PointerMove::VIEWPORT)
|
236
|
+
end
|
237
|
+
f1.create_pointer_up(:left)
|
238
|
+
puts "Scroll execute[w3c_action]: #{type}: {x0: #{x0}, y0: #{y0}} => {x1: #{x1}, y1: #{y1}}"
|
239
|
+
@driver.perform_actions [f1]
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
def apply_w3c_correction(x1, y1, direction)
|
244
|
+
y1 -= SCROLL_CORRECTION_W3C if direction == :down
|
245
|
+
y1 += SCROLL_CORRECTION_W3C if direction == :up
|
246
|
+
x1 -= SCROLL_CORRECTION_W3C if direction == :right
|
247
|
+
x1 += SCROLL_CORRECTION_W3C if direction == :left
|
248
|
+
[x1, y1]
|
249
|
+
end
|
250
|
+
|
251
|
+
|
252
|
+
|
253
|
+
def drag_to(x, y)
|
254
|
+
x0 = @bounds.center.x
|
255
|
+
y0 = @bounds.center.y
|
256
|
+
w3c_action(x0, y0, x, y, SCROLL_ACTION_TYPE_DRAG)
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|
260
|
+
|
261
|
+
end
|
@@ -0,0 +1,226 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'common/bounds'
|
4
|
+
require_relative 'common/exceptions/strategy_mix_exception'
|
5
|
+
require_relative 'common/helpers'
|
6
|
+
require_relative 'common/locator'
|
7
|
+
require_relative 'common/scroll_actions'
|
8
|
+
|
9
|
+
module TestaAppiumDriver
|
10
|
+
class Driver
|
11
|
+
attr_accessor :driver
|
12
|
+
attr_reader :device
|
13
|
+
attr_reader :automation_name
|
14
|
+
|
15
|
+
# custom options
|
16
|
+
# - default_strategy: default strategy to be used for finding elements. Available strategies :uiautomator or :xpath
|
17
|
+
def initialize(opts = {})
|
18
|
+
@testa_opts = opts[:testa_appium_driver] || {}
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
core = Appium::Core.for(opts)
|
23
|
+
extend_for(core.device, core.automation_name)
|
24
|
+
@device = core.device
|
25
|
+
@automation_name = core.automation_name
|
26
|
+
|
27
|
+
handle_testa_opts
|
28
|
+
|
29
|
+
@driver = core.start_driver
|
30
|
+
invalidate_cache!
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
extend_element_with_driver(opts[:caps][:udid])
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def invalidate_cache!
|
39
|
+
@cache = {
|
40
|
+
strategy: nil,
|
41
|
+
selector: nil,
|
42
|
+
element: nil,
|
43
|
+
from_element: nil,
|
44
|
+
time: Time.at(0)
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
#noinspection RubyClassVariableUsageInspection
|
49
|
+
def extend_element_with_driver(udid)
|
50
|
+
Selenium::WebDriver::Element.define_singleton_method(:set_driver) do |driver|
|
51
|
+
udid = "unknown" if udid.nil?
|
52
|
+
@@drivers ||={}
|
53
|
+
@@drivers[udid] = driver
|
54
|
+
end
|
55
|
+
|
56
|
+
Selenium::WebDriver::Element.set_driver(self)
|
57
|
+
Selenium::WebDriver::Element.define_method(:get_driver) do
|
58
|
+
udid = self.instance_variable_get(:@bridge).instance_variable_get(:@capabilities).instance_variable_get(:@capabilities)["udid"]
|
59
|
+
udid = "unknown" if udid.nil?
|
60
|
+
@@drivers[udid]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
#noinspection RubyScope
|
66
|
+
# @param [TestaAppiumDriver::Locator, TestaAppiumDriver::Driver] from_element element from which start the search
|
67
|
+
# @param [String] selector resolved string of a [TestaAppiumDriver::Locator] selector xpath for xpath strategy, java UiSelectors for uiautomator
|
68
|
+
# @param [Boolean] single fetch single or multiple results
|
69
|
+
# @param [Symbol, nil] strategy [TestaAppiumDriver:FIND_STRATEGY_UIAUTOMATOR] or [FIND_STRATEGY_XPATH]
|
70
|
+
# @param [Symbol] default_strategy if strategy is not enforced, default can be used
|
71
|
+
# @param [Boolean] skip_cache to skip checking and storing cache
|
72
|
+
# @return [Selenium::WebDriver::Element, Array] element is returned if single is true, array otherwise
|
73
|
+
def execute(from_element, selector, single, strategy, default_strategy, skip_cache = false)
|
74
|
+
|
75
|
+
# if user wants to wait for element to exist, he can use wait_until_present
|
76
|
+
disable_wait_for_idle
|
77
|
+
|
78
|
+
# if not restricted to a strategy, use the default one
|
79
|
+
strategy = default_strategy if strategy.nil?
|
80
|
+
|
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.selector : nil
|
83
|
+
|
84
|
+
puts "Executing #{from_element_id ? "from #{from_element.strategy}: #{from_element.selector} => " : ""}#{strategy}: #{selector}"
|
85
|
+
begin
|
86
|
+
if @cache[:selector] != selector || # cache miss, selector is different
|
87
|
+
@cache[:time] + 5 <= Time.now || # cache miss, older than 5 seconds
|
88
|
+
@cache[:strategy] != strategy || # cache miss, different find strategy
|
89
|
+
@cache[:from_element_id] != from_element_id || # cache miss, search is started from different element
|
90
|
+
skip_cache # cache is skipped
|
91
|
+
|
92
|
+
if strategy == FIND_STRATEGY_UIAUTOMATOR
|
93
|
+
if single
|
94
|
+
execute_result = from_element.find_element(uiautomator: selector)
|
95
|
+
else
|
96
|
+
execute_result = from_element.find_elements(uiautomator: selector)
|
97
|
+
end
|
98
|
+
|
99
|
+
elsif strategy == FIND_STRATEGY_XPATH
|
100
|
+
if single
|
101
|
+
execute_result = from_element.find_element(xpath: selector)
|
102
|
+
else
|
103
|
+
execute_result = from_element.find_elements(xpath: selector)
|
104
|
+
end
|
105
|
+
else
|
106
|
+
raise "Unknown find_element strategy"
|
107
|
+
end
|
108
|
+
|
109
|
+
unless skip_cache
|
110
|
+
@cache[:selector] = selector
|
111
|
+
@cache[:strategy] = strategy
|
112
|
+
@cache[:time] = Time.now
|
113
|
+
@cache[:from_element_id] = from_element_id
|
114
|
+
@cache[:element] = execute_result
|
115
|
+
end
|
116
|
+
else
|
117
|
+
# this is a cache hit, use the element from cache
|
118
|
+
execute_result = @cache[:element]
|
119
|
+
puts "Using cache from #{@cache[:time].strftime("%H:%M:%S.%L")}, strategy: #{@cache[:strategy]}"
|
120
|
+
end
|
121
|
+
rescue => e
|
122
|
+
raise e
|
123
|
+
ensure
|
124
|
+
enable_wait_for_idle
|
125
|
+
end
|
126
|
+
|
127
|
+
execute_result
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
# method missing is used to forward methods to the actual appium driver
|
132
|
+
def method_missing(method, *args, &block)
|
133
|
+
@driver.send(method, *args, &block)
|
134
|
+
end
|
135
|
+
|
136
|
+
def disable_implicit_wait
|
137
|
+
@implicit_wait_ms = @driver.get_timeouts["implicit"]
|
138
|
+
@driver.manage.timeouts.implicit_wait = 0
|
139
|
+
end
|
140
|
+
|
141
|
+
def enable_implicit_wait
|
142
|
+
raise "Implicit wait is not disabled" if @implicit_wait_ms.nil?
|
143
|
+
# get_timeouts always returns in milliseconds, but we should set in seconds
|
144
|
+
@driver.manage.timeouts.implicit_wait = @implicit_wait_ms / 1000
|
145
|
+
end
|
146
|
+
|
147
|
+
def disable_wait_for_idle
|
148
|
+
if @device == :android
|
149
|
+
@wait_for_idle_timeout = @driver.settings.get["waitForIdleTimeout"]
|
150
|
+
@driver.update_settings({waitForIdleTimeout: 0})
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def enable_wait_for_idle
|
155
|
+
if @device == :android
|
156
|
+
raise "Wait for idle is not disabled" if @wait_for_idle_timeout.nil?
|
157
|
+
@driver.update_settings({waitForIdleTimeout: @wait_for_idle_timeout})
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def current_package
|
162
|
+
@driver.current_package
|
163
|
+
end
|
164
|
+
|
165
|
+
def window_size(*args)
|
166
|
+
@driver.window_size(*args)
|
167
|
+
end
|
168
|
+
|
169
|
+
def back
|
170
|
+
@driver.back
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
def is_keyboard_shown?
|
175
|
+
@driver.is_keyboard_shown
|
176
|
+
end
|
177
|
+
|
178
|
+
def hide_keyboard
|
179
|
+
@driver.hide_keyboard
|
180
|
+
end
|
181
|
+
|
182
|
+
def press_keycode(code)
|
183
|
+
@driver.press_keycode(code)
|
184
|
+
end
|
185
|
+
|
186
|
+
def long_press_keycode(code)
|
187
|
+
@driver.long_press_keycode(code)
|
188
|
+
end
|
189
|
+
|
190
|
+
|
191
|
+
def first_and_last_leaf(from_element = @driver)
|
192
|
+
disable_wait_for_idle
|
193
|
+
disable_implicit_wait
|
194
|
+
elements = from_element.find_elements(xpath: "//*[not(*)]")
|
195
|
+
enable_implicit_wait
|
196
|
+
enable_wait_for_idle
|
197
|
+
return nil if elements.count == 0
|
198
|
+
[elements[0], elements[-1]]
|
199
|
+
end
|
200
|
+
|
201
|
+
private
|
202
|
+
def extend_for(device, automation_name)
|
203
|
+
case device
|
204
|
+
when :android
|
205
|
+
case automation_name
|
206
|
+
when :uiautomator2
|
207
|
+
require_relative 'android/driver'
|
208
|
+
else
|
209
|
+
raise "Testa appium driver not supported for #{automation_name} automation"
|
210
|
+
end
|
211
|
+
when :ios, :tvos
|
212
|
+
case automation_name
|
213
|
+
when :xcuitest
|
214
|
+
require_relative 'ios/driver'
|
215
|
+
else
|
216
|
+
raise "Testa appium driver not supported for #{automation_name} automation"
|
217
|
+
end
|
218
|
+
else
|
219
|
+
raise "Unknown device #{device}, should be either android, ios or tvos"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
|
224
|
+
end
|
225
|
+
|
226
|
+
end
|