testcentricity_mobile 4.0.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.
@@ -0,0 +1,634 @@
1
+ require 'appium_lib'
2
+ require 'json'
3
+ require 'net/http'
4
+ require 'net/https'
5
+ require 'uri'
6
+
7
+
8
+ module TestCentricity
9
+ module AppiumConnect
10
+ attr_accessor :driver
11
+ attr_accessor :running
12
+ attr_accessor :endpoint
13
+ attr_accessor :capabilities
14
+ attr_accessor :global_scope
15
+
16
+ # Create a new driver instance with capabilities specified in the options parameter, or via Environment Variables.
17
+ # Refer to the `Connecting to a Mobile Simulator or Device` section of the ruby docs for this gem.
18
+ #
19
+ def self.initialize_appium(options = nil)
20
+ @endpoint = nil
21
+ @capabilities = nil
22
+ @running = false
23
+ @global_scope = false
24
+ Environ.driver_name = nil
25
+ Environ.device_orientation = nil
26
+ Environ.platform = :mobile
27
+ Environ.device = :simulator
28
+
29
+ if options.is_a?(Hash)
30
+ @endpoint = options[:endpoint] if options.key?(:endpoint)
31
+ @capabilities = options[:capabilities] if options.key?(:capabilities)
32
+ @global_scope = options[:global_driver] if options.key?(:global_driver)
33
+ Environ.driver = options[:driver] if options.key?(:driver)
34
+ Environ.driver_name = options[:driver_name] if options.key?(:driver_name)
35
+ Environ.device_type = options[:device_type] if options.key?(:device_type)
36
+ end
37
+ if @capabilities.nil?
38
+ Environ.driver = ENV['DRIVER'].downcase.to_sym if ENV['DRIVER']
39
+ Environ.device_type = ENV['DEVICE_TYPE'] if ENV['DEVICE_TYPE']
40
+ Environ.device_orientation = ENV['ORIENTATION'] if ENV['ORIENTATION']
41
+ else
42
+ raise ':browserName is specified in capabilities' if @capabilities[:browserName]
43
+ end
44
+
45
+ raise 'You must specify a driver' if Environ.driver.nil?
46
+ raise 'You must specify a device type - :phone or :tablet' if Environ.device_type.nil?
47
+
48
+ driver_caps = case Environ.driver
49
+ when :appium
50
+ appium_local_capabilities
51
+ when :custom
52
+ raise 'User-defined cloud driver requires that you define options' if options.nil?
53
+ custom_capabilities
54
+ when :browserstack
55
+ browserstack_capabilities
56
+ when :testingbot
57
+ testingbot_capabilities
58
+ # :nocov:
59
+ when :saucelabs
60
+ sauce_capabilities
61
+ else
62
+ raise "#{Environ.driver} is not a supported driver"
63
+ # :nocov:
64
+ end
65
+ driver_opts = {
66
+ caps: driver_caps,
67
+ appium_lib: { server_url: @endpoint }
68
+ }
69
+ @driver = Appium::Driver.new(driver_opts, global_driver = @global_scope)
70
+ @driver.start_driver
71
+ Environ.appium_driver = @driver
72
+ @running = true
73
+ Appium.promote_appium_methods(TestCentricity::ScreenObject, driver = @driver)
74
+ Appium.promote_appium_methods(TestCentricity::ScreenSection, driver = @driver)
75
+ Appium.promote_appium_methods(TestCentricity::AppElements::AppUIElement, driver = @driver)
76
+
77
+ Environ.screen_size = window_size
78
+ unless Environ.driver_name
79
+ Environ.driver_name = "#{Environ.driver}_#{Environ.device_os}_#{Environ.device_type}".downcase.to_sym
80
+ end
81
+ Environ.session_state = :running
82
+ end
83
+
84
+ # Quit the running driver instance. Sets Environ.session_state to :quit.
85
+ #
86
+ def self.quit_driver
87
+ if @running
88
+ driver.driver_quit
89
+ @running = false
90
+ end
91
+ Environ.session_state = :quit
92
+ end
93
+
94
+ # Return a reference to last created Appium driver
95
+ #
96
+ def self.driver
97
+ @driver
98
+ end
99
+
100
+ # Save a screenshot in .png format to the specified file path.
101
+ #
102
+ # @param png_save_path [String] path to save the screenshot
103
+ # @example
104
+ # AppiumConnect.take_screenshot('reports/screenshots/login_screen.png')
105
+ #
106
+ def self.take_screenshot(png_save_path)
107
+ FileUtils.mkdir_p(File.dirname(png_save_path))
108
+ driver.driver.save_screenshot(png_save_path)
109
+ end
110
+
111
+ # Install app on device. If bundle_id is not specified, then bundle id will be retrieved from Environ.current.ios_bundle_id
112
+ # or Environ.current.android_app_id dependent on which platform is being tested.
113
+ #
114
+ # @param bundle_id [String] OPTIONAL bundle id of app
115
+ # @example
116
+ # AppiumConnect.install_app('com.saucelabs.mydemoapp.rn')
117
+ #
118
+ def self.install_app(app_path)
119
+ driver.install_app(app_path)
120
+ end
121
+
122
+ # Remove app from device. If bundle_id is not specified, then bundle id will be retrieved from Environ.current.ios_bundle_id
123
+ # or Environ.current.android_app_id dependent on which platform is being tested.
124
+ #
125
+ # @param bundle_id [String] OPTIONAL bundle id of app
126
+ # @example
127
+ # AppiumConnect.remove_app('com.saucelabs.mydemoapp.rn')
128
+ #
129
+ def self.remove_app(bundle_id = nil)
130
+ driver.remove_app(get_app_id(bundle_id))
131
+ end
132
+
133
+ # Is the app installed? If bundle_id is not specified, then bundle id will be retrieved from Environ.current.ios_bundle_id
134
+ # or Environ.current.android_app_id dependent on which platform is being tested.
135
+ #
136
+ # @param bundle_id [String] OPTIONAL bundle id of app
137
+ # @return [Boolean] TRUE if app is installed
138
+ # @example
139
+ # AppiumConnect.app_installed?('com.saucelabs.mydemoapp.rn')
140
+ #
141
+ def self.app_installed?(bundle_id = nil)
142
+ driver.app_installed?(get_app_id(bundle_id))
143
+ end
144
+
145
+ # Backgrounds the app for a specified number of seconds.
146
+ #
147
+ # @param duration [Integer] number of seconds to background the app for
148
+ # @example
149
+ # AppiumConnect.background_app(20)
150
+ #
151
+ def self.background_app(duration = 0)
152
+ driver.background_app(duration)
153
+ end
154
+
155
+ # Launch the app. If bundle_id is not specified, then bundle id will be retrieved from Environ.current.ios_bundle_id
156
+ # or Environ.current.android_app_id dependent on which platform is being tested.
157
+ #
158
+ # @param bundle_id [String] OPTIONAL bundle id of app
159
+ # @example
160
+ # AppiumConnect.activate_app('com.saucelabs.mydemoapp.rn')
161
+ #
162
+ def self.activate_app(bundle_id = nil)
163
+ driver.activate_app(get_app_id(bundle_id))
164
+ if Environ.is_android?
165
+ sleep(1.5) if app_state == :running_in_foreground
166
+ end
167
+ Environ.new_app_session
168
+ end
169
+
170
+ # Get status of app. If bundle_id is not specified, then bundle id will be retrieved from Environ.current.ios_bundle_id
171
+ # or Environ.current.android_app_id dependent on which platform is being tested. Returns the following statuses:
172
+ # :not_installed - The current application state cannot be determined/is unknown
173
+ # :not_running - The application is not running
174
+ # :running_in_background_suspended - The application is running in the background and is suspended
175
+ # :running_in_background - The application is running in the background and is not suspended
176
+ # :running_in_foreground - The application is running in the foreground
177
+ #
178
+ # @param bundle_id [String] OPTIONAL bundle id of app
179
+ # @return [Symbol] status of app
180
+ # @example
181
+ # AppiumConnect.app_state('com.saucelabs.mydemoapp.rn')
182
+ #
183
+ def self.app_state(bundle_id = nil)
184
+ driver.app_state(get_app_id(bundle_id))
185
+ end
186
+
187
+ # Terminate the app. If bundle_id is not specified, then bundle id will be retrieved from Environ.current.ios_bundle_id
188
+ # or Environ.current.android_app_id dependent on which platform is being tested.
189
+ #
190
+ # @param bundle_id [String] OPTIONAL bundle id of app
191
+ # @example
192
+ # AppiumConnect.terminate_app('com.saucelabs.mydemoapp.rn')
193
+ #
194
+ def self.terminate_app(bundle_id = nil)
195
+ driver.terminate_app(get_app_id(bundle_id))
196
+ end
197
+
198
+ # Set the amount of time the driver should wait when searching for elements.
199
+ #
200
+ # @param timeout [Integer] number of seconds to wait
201
+ #
202
+ def self.implicit_wait(timeout)
203
+ driver.manage.timeouts.implicit_wait = timeout
204
+ end
205
+
206
+ # Hide the onscreen keyboard
207
+ #
208
+ def self.hide_keyboard
209
+ driver.hide_keyboard
210
+ end
211
+
212
+ # Is onscreen keyboard displayed?
213
+ #
214
+ # @return [Boolean] TRUE if keyboard is shown. Return false if keyboard is hidden.
215
+ #
216
+ def self.keyboard_shown?
217
+ driver.driver.keyboard_shown?
218
+ end
219
+
220
+ # Get the current screen orientation
221
+ #
222
+ # @return [Symbol] :landscape or :portrait
223
+ #
224
+ def self.orientation
225
+ driver.driver.orientation
226
+ end
227
+
228
+ # Change the screen orientation
229
+ #
230
+ # @param orientation [Symbol or String] :landscape or :portrait
231
+ #
232
+ def self.rotation(orientation)
233
+ orientation.downcase.to_sym if orientation.is_a?(String)
234
+ driver.driver.rotation = orientation
235
+ end
236
+
237
+ # Get the device's window size.
238
+ #
239
+ # @return [Array] window size as [width, height]
240
+ #
241
+ def self.window_size
242
+ size = driver.window_size
243
+ [size.width, size.height]
244
+ end
245
+
246
+ # Get the device's window rectangle.
247
+ #
248
+ # @return (Selenium::WebDriver::Rectangle)
249
+ #
250
+ def self.window_rect
251
+ driver.window_rect
252
+ end
253
+
254
+ def self.geolocation
255
+ driver.driver.location
256
+ end
257
+
258
+ def self.set_geolocation(location_data)
259
+ driver.set_location(location_data)
260
+ end
261
+
262
+ def self.current_context
263
+ driver.current_context
264
+ end
265
+
266
+ def self.set_context(context)
267
+ driver.set_context(context)
268
+ end
269
+
270
+ def self.available_contexts
271
+ driver.available_contexts
272
+ end
273
+
274
+ def self.is_webview?
275
+ driver.current_context.start_with?('WEBVIEW')
276
+ end
277
+
278
+ def self.is_native_app?
279
+ driver.current_context.start_with?('NATIVE_APP')
280
+ end
281
+
282
+ def self.webview_context
283
+ contexts = driver.available_contexts
284
+ puts "Contexts = #{contexts}" if ENV['DEBUG']
285
+ set_context(contexts.last)
286
+ puts "Current context = #{driver.current_context}" if ENV['DEBUG']
287
+ end
288
+
289
+ def self.is_biometric_enrolled?
290
+ if Environ.is_ios?
291
+ @driver.execute_script('mobile: isBiometricEnrolled')
292
+ else
293
+ puts 'biometric_enrollment is not supported for Android'
294
+ end
295
+ end
296
+
297
+ def self.set_biometric_enrollment(state)
298
+ if Environ.is_ios?
299
+ @driver.execute_script('mobile: enrollBiometric', { isEnabled: state })
300
+ else
301
+ puts 'biometric_enrollment is not supported for Android'
302
+ end
303
+ end
304
+
305
+ def self.biometric_match(type, match)
306
+ if Environ.is_ios?
307
+ @driver.execute_script('mobile: sendBiometricMatch', { type: type, match: match })
308
+ else
309
+ raise 'biometric_match is not supported for Android'
310
+ end
311
+ end
312
+
313
+ # :nocov:
314
+ def self.upload_app(service)
315
+ # determine app custom test id (if specified)
316
+ custom_id = if ENV['APP_ID']
317
+ ENV['APP_ID']
318
+ else
319
+ Environ.is_android? ? Environ.current.android_test_id : Environ.current.ios_test_id
320
+ end
321
+ # determine endpoint url, user id, and auth key for specified cloud service provider
322
+ case service
323
+ when :browserstack
324
+ url = 'https://api-cloud.browserstack.com/app-automate/upload'
325
+ user_id = ENV['BS_USERNAME']
326
+ auth_key = ENV['BS_AUTHKEY']
327
+ when :testingbot
328
+ url = 'https://api.testingbot.com/v1/storage'
329
+ url = "#{url}/#{custom_id}" unless custom_id.nil?
330
+ user_id = ENV['TB_USERNAME']
331
+ auth_key = ENV['TB_AUTHKEY']
332
+ else
333
+ raise "#{service} is not supported"
334
+ end
335
+ # determine file path of app to be uploaded to cloud service
336
+ file_path = if Environ.is_android?
337
+ Environ.current.android_apk_path
338
+ elsif Environ.is_ios?
339
+ Environ.is_device? ? Environ.current.ios_ipa_path : Environ.current.ios_app_path
340
+ end
341
+
342
+ request = Net::HTTP::Post.new(url)
343
+ boundary = "WebAppBoundary"
344
+ post_body = []
345
+ post_body << "--#{boundary}\r\n"
346
+ post_body << "Content-Disposition: form-data; name=\"file\"; filename=\"#{file_path}\"\r\n"
347
+ post_body << "\r\n"
348
+ post_body << File.open(file_path) {|io| io.read}
349
+ # add custom id form data to post body if a custom test id has been provided
350
+ if !custom_id.nil? && service == :browserstack
351
+ post_body << "\r\n--#{boundary}\r\n"
352
+ post_body << "Content-Disposition: form-data; name=\"custom_id\"\r\n"
353
+ post_body << "\r\n"
354
+ post_body << "#{custom_id}"
355
+ end
356
+ post_body << "\r\n--#{boundary}--\r\n"
357
+ request.body = post_body.join
358
+ request["Content-Type"] = "multipart/form-data; boundary=#{boundary}"
359
+ request.basic_auth(user_id, auth_key)
360
+ # send the request to upload to cloud service provider
361
+ uri = URI.parse(url)
362
+ conn = Net::HTTP.new(uri.host, uri.port)
363
+ if uri.scheme == 'https'
364
+ conn.use_ssl = true
365
+ conn.verify_mode = OpenSSL::SSL::VERIFY_NONE
366
+ end
367
+ response = conn.request(request)
368
+ result = JSON.parse(response.body)
369
+ if response.code.to_i > 202
370
+ raise "An error has occurred while attempting to upload #{file_path} to the #{service} service\n#{result}"
371
+ else
372
+ puts "Successfully uploaded #{file_path} to the #{service} service\n#{result}"
373
+ end
374
+ end
375
+ # :nocov:
376
+
377
+ private
378
+
379
+ def self.get_app_id(bundle_id = nil)
380
+ return bundle_id unless bundle_id.nil?
381
+
382
+ if Environ.is_ios?
383
+ Environ.current.ios_bundle_id
384
+ elsif Environ.is_android?
385
+ Environ.current.android_app_id
386
+ else
387
+ nil
388
+ end
389
+ end
390
+
391
+ def self.appium_local_capabilities
392
+ # specify endpoint url
393
+ @endpoint = 'http://127.0.0.1:4723/wd/hub' if @endpoint.nil?
394
+ # define local Appium capabilities
395
+ if @capabilities.nil?
396
+ Environ.device_name = ENV['APP_DEVICE']
397
+ Environ.device_os = ENV['APP_PLATFORM_NAME']
398
+ Environ.device_os_version = ENV['APP_VERSION']
399
+ Environ.device = ENV['UDID'] ? :device : :simulator
400
+
401
+ caps = {
402
+ platformName: ENV['APP_PLATFORM_NAME'],
403
+ 'appium:platformVersion': ENV['APP_VERSION'],
404
+ 'appium:deviceName': ENV['APP_DEVICE'],
405
+ 'appium:automationName': ENV['AUTOMATION_ENGINE']
406
+ }
407
+ caps[:'appium:avd'] = ENV['APP_DEVICE'] if ENV['APP_PLATFORM_NAME'].downcase.to_sym == :android
408
+ caps[:'appium:orientation'] = ENV['ORIENTATION'].upcase if ENV['ORIENTATION']
409
+ if ENV['LOCALE'] && ENV['LANGUAGE']
410
+ caps[:'appium:language'] = ENV['LANGUAGE']
411
+ caps[:'appium:locale'] = if Environ.is_android?
412
+ locale = ENV['LOCALE'].gsub('-', '_')
413
+ locale.split('_')[1]
414
+ else
415
+ ENV['LOCALE'].gsub('-', '_')
416
+ end
417
+ end
418
+ caps[:'appium:newCommandTimeout'] = ENV['NEW_COMMAND_TIMEOUT'] if ENV['NEW_COMMAND_TIMEOUT']
419
+ caps[:'appium:noReset'] = ENV['APP_NO_RESET'] if ENV['APP_NO_RESET']
420
+ caps[:'appium:fullReset'] = ENV['APP_FULL_RESET'] if ENV['APP_FULL_RESET']
421
+ caps[:'appium:autoLaunch'] = ENV['AUTO_LAUNCH'] if ENV['AUTO_LAUNCH']
422
+ caps[:'appium:webkitDebugProxyPort'] = ENV['WEBKIT_DEBUG_PROXY_PORT'] if ENV['WEBKIT_DEBUG_PROXY_PORT']
423
+ caps[:'appium:webDriverAgentUrl'] = ENV['WEBDRIVER_AGENT_URL'] if ENV['WEBDRIVER_AGENT_URL']
424
+ caps[:'appium:wdaLocalPort'] = ENV['WDA_LOCAL_PORT'] if ENV['WDA_LOCAL_PORT']
425
+ caps[:'appium:usePrebuiltWDA'] = ENV['USE_PREBUILT_WDA'] if ENV['USE_PREBUILT_WDA']
426
+ caps[:'appium:useNewWDA'] = ENV['USE_NEW_WDA'] if ENV['USE_NEW_WDA']
427
+ caps[:'appium:autoWebview'] = ENV['AUTO_WEBVIEW'] if ENV['AUTO_WEBVIEW']
428
+ caps[:'appium:chromedriverExecutable'] = ENV['CHROMEDRIVER_EXECUTABLE'] if ENV['CHROMEDRIVER_EXECUTABLE']
429
+ caps[:'appium:autoWebviewTimeout'] = ENV['AUTO_WEBVIEW_TIMEOUT'] if ENV['AUTO_WEBVIEW_TIMEOUT']
430
+ caps[:'appium:udid'] = ENV['UDID'] if ENV['UDID']
431
+ caps[:'appium:xcodeOrgId'] = ENV['TEAM_ID'] if ENV['TEAM_ID']
432
+ caps[:'appium:xcodeSigningId'] = ENV['TEAM_NAME'] if ENV['TEAM_NAME']
433
+ caps[:'appium:appActivity'] = ENV['APP_ACTIVITY'] if ENV['APP_ACTIVITY']
434
+ caps[:'appium:appPackage'] = ENV['APP_PACKAGE'] if ENV['APP_PACKAGE']
435
+ caps[:'appium:forceSimulatorSoftwareKeyboardPresence'] = ENV['SHOW_SIM_KEYBOARD'] if ENV['SHOW_SIM_KEYBOARD']
436
+ caps[:'appium:webviewConnectTimeout'] = '90000'
437
+
438
+ if ENV['BUNDLE_ID']
439
+ caps[:'appium:bundleId'] = ENV['BUNDLE_ID']
440
+ else
441
+ app_id = get_app_id
442
+ caps[:'appium:bundleId'] = app_id unless app_id.nil?
443
+ end
444
+
445
+ caps[:'appium:app'] = if ENV['APP']
446
+ ENV['APP']
447
+ else
448
+ if Environ.is_android?
449
+ Environ.current.android_apk_path
450
+ elsif Environ.is_ios?
451
+ Environ.is_device? ?
452
+ Environ.current.ios_ipa_path :
453
+ Environ.current.ios_app_path
454
+ end
455
+ end
456
+ caps
457
+ else
458
+ Environ.device_os = @capabilities[:platformName]
459
+ Environ.device_name = @capabilities[:'appium:deviceName']
460
+ Environ.device_os_version = @capabilities[:'appium:platformVersion']
461
+ Environ.device_orientation = @capabilities[:'appium:orientation']
462
+ Environ.device = if @capabilities[:'appium:udid']
463
+ :device
464
+ else
465
+ :simulator
466
+ end
467
+ @capabilities
468
+ end
469
+ end
470
+
471
+ def self.custom_capabilities
472
+ raise 'User-defined cloud driver requires that you provide capabilities' if @capabilities.nil?
473
+ raise 'User-defined cloud driver requires that you provide an endpoint' if @endpoint.nil?
474
+
475
+ Environ.device_os = @capabilities[:platformName]
476
+ Environ.device_name = @capabilities[:'appium:deviceName']
477
+ Environ.device_os_version = @capabilities[:'appium:platformVersion']
478
+ @capabilities
479
+ end
480
+
481
+ def self.browserstack_capabilities
482
+ # specify endpoint url
483
+ @endpoint = "https://#{ENV['BS_USERNAME']}:#{ENV['BS_AUTHKEY']}@hub-cloud.browserstack.com/wd/hub" if @endpoint.nil?
484
+ # define BrowserStack options
485
+ options = if @capabilities.nil?
486
+ Environ.device_name = ENV['BS_DEVICE']
487
+ Environ.device_os = ENV['BS_OS']
488
+ Environ.device_os_version = ENV['BS_OS_VERSION']
489
+ # define the required set of BrowserStack options
490
+ bs_options = {
491
+ userName: ENV['BS_USERNAME'],
492
+ accessKey: ENV['BS_AUTHKEY'],
493
+ sessionName: test_context_message
494
+ }
495
+ # define the optional BrowserStack options
496
+ bs_options[:projectName] = ENV['AUTOMATE_PROJECT'] if ENV['AUTOMATE_PROJECT']
497
+ bs_options[:buildName] = ENV['AUTOMATE_BUILD'] if ENV['AUTOMATE_BUILD']
498
+ bs_options[:geoLocation] = ENV['IP_GEOLOCATION'] if ENV['IP_GEOLOCATION']
499
+ bs_options[:timezone] = ENV['TIME_ZONE'] if ENV['TIME_ZONE']
500
+ bs_options[:video] = ENV['RECORD_VIDEO'] if ENV['RECORD_VIDEO']
501
+ bs_options[:debug] = ENV['SCREENSHOTS'] if ENV['SCREENSHOTS']
502
+ bs_options[:local] = ENV['TUNNELING'] if ENV['TUNNELING']
503
+ bs_options[:deviceOrientation] = ENV['ORIENTATION'] if ENV['ORIENTATION']
504
+ bs_options[:appiumLogs] = ENV['APPIUM_LOGS'] if ENV['APPIUM_LOGS']
505
+ bs_options[:networkLogs] = ENV['NETWORK_LOGS'] if ENV['NETWORK_LOGS']
506
+ bs_options[:deviceLogs] = ENV['DEVICE_LOGS'] if ENV['DEVICE_LOGS']
507
+ bs_options[:networkProfile] = ENV['NETWORK_PROFILE'] if ENV['NETWORK_PROFILE']
508
+ bs_options[:idleTimeout] = ENV['IDLE_TIMEOUT'] if ENV['IDLE_TIMEOUT']
509
+ bs_options[:resignApp] = ENV['RESIGN_APP'] if ENV['RESIGN_APP']
510
+ bs_options[:gpsLocation] = ENV['GPS_LOCATION'] if ENV['GPS_LOCATION']
511
+ bs_options[:acceptInsecureCerts] = ENV['ACCEPT_INSECURE_CERTS'] if ENV['ACCEPT_INSECURE_CERTS']
512
+ bs_options[:disableAnimations] = ENV['DISABLE_ANIMATION'] if ENV['DISABLE_ANIMATION']
513
+ bs_options[:appiumVersion] = ENV['APPIUM_VERSION'] ? ENV['APPIUM_VERSION'] : '2.4.1'
514
+
515
+ capabilities = {
516
+ platformName: ENV['BS_OS'],
517
+ 'appium:platformVersion': ENV['BS_OS_VERSION'],
518
+ 'appium:deviceName': ENV['BS_DEVICE'],
519
+ 'appium:automationName': ENV['AUTOMATION_ENGINE'],
520
+ 'appium:app': ENV['APP'],
521
+ 'bstack:options': bs_options
522
+ }
523
+ capabilities[:language] = ENV['LANGUAGE'] if ENV['LANGUAGE']
524
+ capabilities[:locale] = ENV['LOCALE'] if ENV['LOCALE']
525
+ capabilities
526
+ else
527
+ Environ.device_os = @capabilities[:platformName]
528
+ Environ.device_name = @capabilities[:'appium:deviceName']
529
+ Environ.device_os_version = @capabilities[:'appium:platformVersion']
530
+ @capabilities
531
+ end
532
+ # BrowserStack uses only real devices
533
+ Environ.device = :device
534
+ upload_app(:browserstack) if ENV['UPLOAD_APP']
535
+ options
536
+ end
537
+
538
+ # :nocov:
539
+ def self.testingbot_capabilities
540
+ Environ.device = :simulator
541
+ # specify endpoint url
542
+ @endpoint = "http://#{ENV['TB_USERNAME']}:#{ENV['TB_AUTHKEY']}@hub.testingbot.com/wd/hub" if @endpoint.nil?
543
+ # define TestingBot options
544
+ options = if @capabilities.nil?
545
+ Environ.device_name = ENV['TB_DEVICE']
546
+ Environ.device_os = ENV['TB_OS']
547
+ Environ.device_os_version = ENV['TB_OS_VERSION']
548
+ Environ.device = :device if ENV['REAL_DEVICE'] == 'true'
549
+ # define the required set of TestingBot options
550
+ tb_options = { build: test_context_message }
551
+ # define the optional TestingBot options
552
+ tb_options[:name] = ENV['AUTOMATE_PROJECT'] if ENV['AUTOMATE_PROJECT']
553
+ tb_options[:timeZone] = ENV['TIME_ZONE'] if ENV['TIME_ZONE']
554
+ tb_options['testingbot.geoCountryCode'] = ENV['IP_GEOLOCATION'] if ENV['IP_GEOLOCATION']
555
+ tb_options[:screenrecorder] = ENV['RECORD_VIDEO'] if ENV['RECORD_VIDEO']
556
+ tb_options[:screenshot] = ENV['SCREENSHOTS'] if ENV['SCREENSHOTS']
557
+ tb_options[:appiumVersion] = ENV['APPIUM_VERSION'] ? ENV['APPIUM_VERSION'] : '2.4.1'
558
+
559
+ capabilities = {
560
+ platformName: ENV['TB_OS'],
561
+ 'appium:platformVersion': ENV['TB_OS_VERSION'],
562
+ 'appium:deviceName': ENV['TB_DEVICE'],
563
+ 'appium:automationName': ENV['AUTOMATION_ENGINE'],
564
+ 'appium:app': ENV['APP'],
565
+ 'tb:options': tb_options
566
+ }
567
+ capabilities[:'appium:realDevice'] = ENV['REAL_DEVICE'] if ENV['REAL_DEVICE']
568
+ capabilities
569
+ else
570
+ Environ.device_os = @capabilities[:platformName]
571
+ Environ.device_name = @capabilities[:'appium:deviceName']
572
+ Environ.device_os_version = @capabilities[:'appium:platformVersion']
573
+ if @capabilities.key?(:'appium:realDevice') && @capabilities[:'appium:realDevice'] == true
574
+ Environ.device = :device
575
+ end
576
+ @capabilities
577
+ end
578
+
579
+ upload_app(:testingbot) if ENV['UPLOAD_APP']
580
+ options
581
+ end
582
+
583
+ def self.sauce_capabilities
584
+ # specify endpoint url
585
+ if @endpoint.nil?
586
+ @endpoint = "https://#{ENV['SL_USERNAME']}:#{ENV['SL_AUTHKEY']}@ondemand.#{ENV['SL_DATA_CENTER']}.saucelabs.com:443/wd/hub"
587
+ end
588
+ # define SauceLabs options
589
+ options = if @capabilities.nil?
590
+ Environ.device_name = ENV['SL_DEVICE']
591
+ Environ.device_os = ENV['SL_OS']
592
+ Environ.device_os_version = ENV['SL_OS_VERSION']
593
+ # define the required set of SauceLabs options
594
+ sl_options = { build: test_context_message }
595
+ # define the optional SauceLabs options
596
+ sl_options[:name] = ENV['AUTOMATE_PROJECT'] if ENV['AUTOMATE_PROJECT']
597
+ sl_options[:deviceOrientation] = ENV['ORIENTATION'].upcase if ENV['ORIENTATION']
598
+ sl_options[:recordVideo] = ENV['RECORD_VIDEO'] if ENV['RECORD_VIDEO']
599
+ sl_options[:recordScreenshots] = ENV['SCREENSHOTS'] if ENV['SCREENSHOTS']
600
+ sl_options[:appiumVersion] = ENV['APPIUM_VERSION'] ? ENV['APPIUM_VERSION'] : '2.1.3'
601
+ capabilities = {
602
+ platformName: ENV['SL_OS'],
603
+ 'appium:platformVersion': ENV['SL_OS_VERSION'],
604
+ 'appium:deviceName': ENV['SL_DEVICE'],
605
+ 'appium:automationName': ENV['AUTOMATION_ENGINE'],
606
+ 'appium:app': ENV['APP'],
607
+ 'sauce:options': sl_options
608
+ }
609
+ capabilities
610
+ else
611
+ Environ.device_os = @capabilities[:platformName]
612
+ Environ.device_name = @capabilities[:'appium:deviceName']
613
+ Environ.device_os_version = @capabilities[:'appium:platformVersion']
614
+ @capabilities
615
+ end
616
+ options
617
+ end
618
+
619
+ def self.test_context_message
620
+ context_message = if ENV['TEST_CONTEXT']
621
+ "#{Environ.test_environment.to_s.upcase} - #{ENV['TEST_CONTEXT']}"
622
+ else
623
+ Environ.test_environment.to_s.upcase
624
+ end
625
+ if ENV['PARALLEL']
626
+ thread_num = ENV['TEST_ENV_NUMBER']
627
+ thread_num = 1 if thread_num.blank?
628
+ context_message = "#{context_message} - Thread ##{thread_num}"
629
+ end
630
+ context_message
631
+ end
632
+ # :nocov:
633
+ end
634
+ end