bugsnag-maze-runner 6.27.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/bin/bugsnag-print-load-paths +6 -0
  3. data/bin/download-logs +76 -0
  4. data/bin/maze-runner +136 -0
  5. data/bin/upload-app +56 -0
  6. data/lib/features/scripts/await-android-emulator.sh +11 -0
  7. data/lib/features/scripts/clear-android-app-data.sh +8 -0
  8. data/lib/features/scripts/force-stop-android-app.sh +8 -0
  9. data/lib/features/scripts/install-android-app.sh +15 -0
  10. data/lib/features/scripts/launch-android-app.sh +38 -0
  11. data/lib/features/scripts/launch-android-emulator.sh +15 -0
  12. data/lib/features/steps/android_steps.rb +51 -0
  13. data/lib/features/steps/app_automator_steps.rb +228 -0
  14. data/lib/features/steps/aws_sam_steps.rb +212 -0
  15. data/lib/features/steps/breadcrumb_steps.rb +50 -0
  16. data/lib/features/steps/browser_steps.rb +93 -0
  17. data/lib/features/steps/build_api_steps.rb +25 -0
  18. data/lib/features/steps/document_server_steps.rb +7 -0
  19. data/lib/features/steps/error_reporting_steps.rb +342 -0
  20. data/lib/features/steps/feature_flag_steps.rb +190 -0
  21. data/lib/features/steps/header_steps.rb +72 -0
  22. data/lib/features/steps/log_steps.rb +29 -0
  23. data/lib/features/steps/multipart_request_steps.rb +142 -0
  24. data/lib/features/steps/network_steps.rb +75 -0
  25. data/lib/features/steps/payload_steps.rb +234 -0
  26. data/lib/features/steps/proxy_steps.rb +34 -0
  27. data/lib/features/steps/query_parameter_steps.rb +31 -0
  28. data/lib/features/steps/request_assertion_steps.rb +107 -0
  29. data/lib/features/steps/runner_steps.rb +406 -0
  30. data/lib/features/steps/session_tracking_steps.rb +116 -0
  31. data/lib/features/steps/value_steps.rb +119 -0
  32. data/lib/features/support/env.rb +7 -0
  33. data/lib/features/support/internal_hooks.rb +260 -0
  34. data/lib/maze/appium_server.rb +112 -0
  35. data/lib/maze/assertions/request_set_assertions.rb +97 -0
  36. data/lib/maze/aws/sam.rb +112 -0
  37. data/lib/maze/bitbar_devices.rb +84 -0
  38. data/lib/maze/bitbar_utils.rb +112 -0
  39. data/lib/maze/browser_stack_devices.rb +160 -0
  40. data/lib/maze/browser_stack_utils.rb +164 -0
  41. data/lib/maze/browsers_bs.yml +220 -0
  42. data/lib/maze/browsers_cbt.yml +100 -0
  43. data/lib/maze/bugsnag_config.rb +42 -0
  44. data/lib/maze/capabilities.rb +126 -0
  45. data/lib/maze/checks/assert_check.rb +91 -0
  46. data/lib/maze/checks/noop_check.rb +34 -0
  47. data/lib/maze/compare.rb +161 -0
  48. data/lib/maze/configuration.rb +174 -0
  49. data/lib/maze/docker.rb +108 -0
  50. data/lib/maze/document_server.rb +46 -0
  51. data/lib/maze/driver/appium.rb +217 -0
  52. data/lib/maze/driver/browser.rb +138 -0
  53. data/lib/maze/driver/resilient_appium.rb +51 -0
  54. data/lib/maze/errors.rb +20 -0
  55. data/lib/maze/helper.rb +118 -0
  56. data/lib/maze/hooks/appium_hooks.rb +216 -0
  57. data/lib/maze/hooks/browser_hooks.rb +68 -0
  58. data/lib/maze/hooks/command_hooks.rb +9 -0
  59. data/lib/maze/hooks/hooks.rb +61 -0
  60. data/lib/maze/interactive_cli.rb +173 -0
  61. data/lib/maze/logger.rb +73 -0
  62. data/lib/maze/macos_utils.rb +14 -0
  63. data/lib/maze/network.rb +49 -0
  64. data/lib/maze/option/parser.rb +245 -0
  65. data/lib/maze/option/processor.rb +143 -0
  66. data/lib/maze/option/validator.rb +184 -0
  67. data/lib/maze/option.rb +64 -0
  68. data/lib/maze/plugins/bugsnag_reporting_plugin.rb +49 -0
  69. data/lib/maze/plugins/cucumber_report_plugin.rb +101 -0
  70. data/lib/maze/plugins/global_retry_plugin.rb +38 -0
  71. data/lib/maze/proxy.rb +114 -0
  72. data/lib/maze/request_list.rb +82 -0
  73. data/lib/maze/retry_handler.rb +76 -0
  74. data/lib/maze/runner.rb +149 -0
  75. data/lib/maze/sauce_labs_utils.rb +96 -0
  76. data/lib/maze/server.rb +207 -0
  77. data/lib/maze/servlets/base_servlet.rb +22 -0
  78. data/lib/maze/servlets/command_servlet.rb +44 -0
  79. data/lib/maze/servlets/log_servlet.rb +64 -0
  80. data/lib/maze/servlets/reflective_servlet.rb +69 -0
  81. data/lib/maze/servlets/servlet.rb +160 -0
  82. data/lib/maze/smart_bear_utils.rb +71 -0
  83. data/lib/maze/store.rb +15 -0
  84. data/lib/maze/terminating_server.rb +129 -0
  85. data/lib/maze/timers.rb +51 -0
  86. data/lib/maze/wait.rb +35 -0
  87. data/lib/maze.rb +27 -0
  88. metadata +371 -0
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+ require 'open3'
3
+ require 'fileutils'
4
+ require 'json'
5
+
6
+ module Maze
7
+ # Utils supporting the BitBar device farm integration
8
+ class BitBarUtils
9
+ BB_READY_FILE = 'bb.ready'
10
+ BB_KILL_FILE = 'bb.kill'
11
+ BB_USER_PREFIX = 'BB_USER_'
12
+ BB_KEY_PREFIX = 'BB_KEY_'
13
+
14
+ class << self
15
+
16
+ # Uploads an app to BitBar for later consumption
17
+ # @param api_key [String] The BitBar API key
18
+ # @param app [String] A path to the application file
19
+ def upload_app(api_key, app)
20
+ uuid_regex = /\A[0-9]+\z/
21
+
22
+ if uuid_regex.match? app
23
+ $logger.info "Using pre-uploaded app with ID #{app}"
24
+ app_uuid = app
25
+ else
26
+ expanded_app = Maze::Helper.expand_path(app)
27
+ $logger.info "Uploading app: #{expanded_app}"
28
+
29
+ # Upload the app to BitBar
30
+ uri = URI('https://cloud.bitbar.com/api/me/files')
31
+ request = Net::HTTP::Post.new(uri)
32
+ request.basic_auth(api_key, '')
33
+ request.set_form({ 'file' => File.new(expanded_app, 'rb') }, 'multipart/form-data')
34
+
35
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
36
+ http.request(request)
37
+ end
38
+
39
+ # Pull the UUID from the response
40
+ begin
41
+ response = JSON.parse res.body
42
+ if response.key?('id')
43
+ app_uuid = response['id']
44
+ $logger.info "Uploaded app ID: #{app_uuid}"
45
+ $logger.info 'You can use this ID to avoid uploading the same app more than once.'
46
+ else
47
+ $logger.error "Unexpected response body: #{response}"
48
+ raise 'App upload failed'
49
+ end
50
+ rescue JSON::ParserError
51
+ $logger.error "Expected JSON response, received: #{res}"
52
+ raise
53
+ end
54
+ end
55
+ app_uuid
56
+ end
57
+
58
+ # Requests an unused account id from the test-management-service
59
+ # @param tms_uri [String] The URI of the test-management-service
60
+ #
61
+ # @returns
62
+ def account_credentials(tms_uri)
63
+ Maze::Wait.new(interval: 10, timeout: 1800).until do
64
+ output = request_account_index(tms_uri)
65
+ case output.code
66
+ when '200'
67
+ body = JSON.parse(output.body, {symbolize_names: true})
68
+ @account_id = account_id = body[:id]
69
+ $logger.info "Using account #{account_id}, expiring at #{body[:expiry]}"
70
+ {
71
+ username: ENV["#{BB_USER_PREFIX}#{account_id}"],
72
+ access_key: ENV["#{BB_KEY_PREFIX}#{account_id}"]
73
+ }
74
+ when '409'
75
+ # All accounts are in use, wait for one to become available
76
+ $logger.info 'All accounts are currently in use, retrying in 30s'
77
+ false
78
+ else
79
+ # Something has gone wrong, throw an error
80
+ $logger.error "Unexpected status code received from test-management server"
81
+ raise
82
+ end
83
+ end
84
+ end
85
+
86
+ # Makes the HTTP call to acquire an account id
87
+ # @param tms_uri [String] The URI of the test-management-service
88
+ #
89
+ # @returns
90
+ def request_account_index(tms_uri)
91
+ uri = URI("#{tms_uri}/account/request")
92
+ request = Net::HTTP::Get.new(uri)
93
+ request['Authorization'] = Maze.config.tms_token
94
+ res = Net::HTTP.start(uri.hostname, uri.port) do |http|
95
+ http.request(request)
96
+ end
97
+ res
98
+ end
99
+
100
+ # Informs the test-management-service that in-use account id is no longer in use
101
+ # @param tms_uri [String] The URI of the test-management-service
102
+ def release_account(tms_uri)
103
+ uri = URI("#{tms_uri}/account/release?account_id=#{@account_id}")
104
+ request = Net::HTTP::Get.new(uri)
105
+ request['Authorization'] = Maze.config.tms_token
106
+ res = Net::HTTP.start(uri.hostname, uri.port) do |http|
107
+ http.request(request)
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maze
4
+ # Provides a source of capabilities used to run tests against specific BrowserStack devices
5
+ # noinspection RubyStringKeysInHashInspection
6
+ class BrowserStackDevices
7
+ APPIUM_1_7_0 = '1.7.0'
8
+ APPIUM_1_9_1 = '1.9.1'
9
+ APPIUM_1_15_0 = '1.15.0'
10
+ APPIUM_1_20_2 = '1.20.2'
11
+ APPIUM_1_21_0 = '1.21.0'
12
+
13
+ class << self
14
+
15
+ def list_devices(os)
16
+ puts "BrowserStack #{os} devices available"
17
+ devices = DEVICE_HASH.dup
18
+ devices.select { |key, device|
19
+ device['os'].eql?(os)
20
+ }.map { |key, device|
21
+ new_device = device.dup
22
+ new_device['key'] = key
23
+ new_device
24
+ }.sort { |dev_1, dev_2|
25
+ dev_1['os_version'].to_f <=> dev_2['os_version'].to_f
26
+ }.each{ |device|
27
+ puts '------------------------------'
28
+ puts "Device key: #{device['key']}"
29
+ puts "Device: #{device['device']}"
30
+ puts "OS: #{device['os']}"
31
+ puts "OS version: #{device['os_version']}"
32
+ }
33
+ end
34
+
35
+ def make_android_hash(device, version, appium_version = APPIUM_1_20_2)
36
+ hash = {
37
+ 'device' => device,
38
+ 'os_version' => version,
39
+ 'autoGrantPermissions' => 'true',
40
+ 'platformName' => 'Android',
41
+ 'os' => 'android',
42
+ 'browserstack.appium_version' => appium_version
43
+ }
44
+ # disableAnimations only allowed > Android 6
45
+ if version.to_i > 6
46
+ hash['disableAnimations'] = 'true'
47
+ end
48
+ hash.freeze
49
+ end
50
+
51
+ def add_android(device, version, hash, appium_version = APPIUM_1_20_2)
52
+ # Key format is "ANDROID_<version>_<device>", with:
53
+ # - dots in versions and all spaces replaced with underscores
54
+ # - device made upper case
55
+ name = device.upcase.gsub ' ', '_'
56
+ new_version = version.gsub '.', '_'
57
+ key = "ANDROID_#{new_version}_#{name}"
58
+ hash[key] = make_android_hash device, version, appium_version
59
+ end
60
+
61
+ def make_ios_hash(device, version, appium_version = APPIUM_1_21_0)
62
+ {
63
+ 'device' => device,
64
+ 'os_version' => version,
65
+ 'platformName' => 'iOS',
66
+ 'os' => 'ios',
67
+ 'disableAnimations' => 'true',
68
+ 'browserstack.appium_version' => appium_version
69
+ }.freeze
70
+ end
71
+
72
+ def add_ios(device, version, hash, appium_version = APPIUM_1_21_0)
73
+ # Key format is "IOS_<version>_<device>", with:
74
+ # - dots in versions and all spaces replaced with underscores
75
+ # - device made upper case
76
+ name = device.upcase.gsub ' ', '_'
77
+ name = name.gsub '.', '_'
78
+ new_version = version.gsub '.', '_'
79
+ key = "IOS_#{new_version}_#{name}"
80
+ hash[key] = make_ios_hash device, version, appium_version
81
+ end
82
+
83
+ def create_hash
84
+ hash = {
85
+ # Classic, non-specific devices for each Android version
86
+ 'ANDROID_13_0' => make_android_hash('Google Pixel 6 Pro', '13.0'),
87
+ 'ANDROID_13_0_BETA' => make_android_hash('Google Pixel 6 Pro', '13 Beta'),
88
+ 'ANDROID_12_0' => make_android_hash('Google Pixel 5', '12.0'),
89
+ 'ANDROID_11_0' => make_android_hash('Google Pixel 4', '11.0'),
90
+ 'ANDROID_10_0' => make_android_hash('Google Pixel 4', '10.0'),
91
+ 'ANDROID_9_0' => make_android_hash('Google Pixel 3', '9.0'),
92
+ 'ANDROID_8_1' => make_android_hash('Samsung Galaxy Note 9', '8.1'),
93
+ 'ANDROID_8_0' => make_android_hash('Google Pixel 2', '8.0'),
94
+ 'ANDROID_7_1' => make_android_hash('Google Pixel', '7.1'),
95
+ 'ANDROID_6_0' => make_android_hash('Google Nexus 6', '6.0'),
96
+ 'ANDROID_5_0' => make_android_hash('Google Nexus 6', '5.0'),
97
+ 'ANDROID_4_4' => make_android_hash('Google Nexus 5', '4.4', APPIUM_1_9_1),
98
+
99
+ # iOS devices
100
+ 'IOS_16' => make_ios_hash('iPhone 14', '16'),
101
+ 'IOS_15' => make_ios_hash('iPhone 11 Pro', '15'),
102
+ 'IOS_14' => make_ios_hash('iPhone 11', '14'),
103
+ 'IOS_13' => make_ios_hash('iPhone 8', '13'),
104
+ 'IOS_12' => make_ios_hash('iPhone 8', '12'),
105
+ 'IOS_11' => make_ios_hash('iPhone 8', '11', APPIUM_1_7_0),
106
+ 'IOS_10' => make_ios_hash('iPhone 7', '10', APPIUM_1_7_0)
107
+ }
108
+
109
+ # Specific Android devices
110
+ add_android 'Google Pixel 4', '11.0', hash # ANDROID_11_0_GOOGLE_PIXEL_4
111
+
112
+ add_android 'Xiaomi Redmi Note 9', '10.0', hash # ANDROID_10_0_XIAOMI_REDMI_NOTE_9
113
+ add_android 'Samsung Galaxy Note 20', '10.0', hash # ANDROID_10_0_SAMSUNG_GALAXY_NOTE_20
114
+ add_android 'Motorola Moto G9 Play', '10.0', hash # ANDROID_10_0_MOTOROLA_MOTO_G9_PLAY
115
+ add_android 'OnePlus 8', '10.0', hash # ANDROID_10_0_ONEPLUS_8
116
+
117
+ add_android 'Google Pixel 2', '9.0', hash # ANDROID_9_0_GOOGLE_PIXEL_2
118
+ add_android 'Samsung Galaxy Note 9', '8.1', hash # ANDROID_8_1_SAMSUNG_GALAXY_NOTE_9
119
+ add_android 'Samsung Galaxy J7 Prime', '8.1', hash # ANDROID_8_1_SAMSUNG_GALAXY_J7_PRIME
120
+ add_android 'Samsung Galaxy Tab S4', '8.1', hash # ANDROID_8_1_SAMSUNG_GALAXY_TAB_S4
121
+ add_android 'Samsung Galaxy Tab S3', '8.0', hash # ANDROID_8_0_SAMSUNG_GALAXY_TAB_S3
122
+ add_android 'Google Pixel', '8.0', hash # ANDROID_8_0_GOOGLE_PIXEL
123
+ add_android 'Google Pixel 2', '8.0', hash # ANDROID_8_0_GOOGLE_PIXEL_2
124
+ add_android 'Samsung Galaxy S9', '8.0', hash # ANDROID_8_0_SAMSUNG_GALAXY_S9
125
+ add_android 'Samsung Galaxy S9 Plus', '8.0', hash # ANDROID_8_0_SAMSUNG_GALAXY_S9_PLUS
126
+
127
+ add_android 'Samsung Galaxy A8', '7.1', hash # ANDROID_7_1_SAMSUNG_GALAXY_A8
128
+ add_android 'Samsung Galaxy Note 8', '7.1', hash # ANDROID_7_1_SAMSUNG_GALAXY_NOTE_8
129
+ add_android 'Samsung Galaxy S8', '7.0', hash # ANDROID_7_0_SAMSUNG_GALAXY_S8
130
+ add_android 'Samsung Galaxy S8 Plus', '7.0', hash # ANDROID_7_0_SAMSUNG_GALAXY_S8_PLUS
131
+
132
+ add_android 'Motorola Moto X 2nd Gen', '6.0', hash # ANDROID_6_0_MOTOROLA_MOTO_X_2ND_GEN
133
+ add_android 'Google Nexus 6', '6.0', hash # ANDROID_6_0_GOOGLE_NEXUS_6
134
+ add_android 'Samsung Galaxy S7', '6.0', hash # ANDROID_6_0_SAMSUNG_GALAXY_S7
135
+ add_android 'Google Nexus 6', '5.0', hash # ANDROID_5_0_GOOGLE_NEXUS_6
136
+ add_android 'Samsung Galaxy S6', '5.0', hash # ANDROID_5_0_SAMSUNG_GALAXY_S6
137
+ add_android 'Samsung Galaxy Note 4', '4.4', hash, APPIUM_1_9_1 # ANDROID_4_4_SAMSUNG_GALAXY_NOTE_4
138
+ add_android 'Samsung Galaxy Tab 4', '4.4', hash, APPIUM_1_9_1 # ANDROID_4_4_SAMSUNG_GALAXY_TAB_4
139
+
140
+ # Specific iOS devices
141
+ add_ios 'iPhone 8 Plus', '11.0', hash, APPIUM_1_7_0 # IOS_11_0_IPHONE_8_PLUS
142
+ add_ios 'iPhone X', '11.0', hash, APPIUM_1_7_0 # IOS_11_0_IPHONE_X
143
+ add_ios 'iPhone SE', '11.0', hash, APPIUM_1_7_0 # IOS_11_0_IPHONE_SE
144
+ add_ios 'iPhone 6', '11.0', hash, APPIUM_1_7_0 # IOS_11_0_IPHONE_6
145
+ add_ios 'iPhone 6S', '11.0', hash, APPIUM_1_7_0 # IOS_11_0_IPHONE_6S
146
+ add_ios 'iPhone 6S Plus', '11.0', hash, APPIUM_1_7_0 # IOS_11_0_IPHONE_6S_PLUS
147
+ add_ios 'iPad 5th', '11.0', hash, APPIUM_1_7_0 # IOS_11_0_IPAD_5TH
148
+ add_ios 'iPad Mini 4', '11.0', hash, APPIUM_1_7_0 # IOS_11_0_IPAD_MINI_4
149
+ add_ios 'iPad Pro 9.7 2016', '11.0', hash, APPIUM_1_7_0 # IOS_11_0_IPAD_PRO_9_7_2016
150
+ add_ios 'iPad 6th', '11.0', hash, APPIUM_1_7_0 # IOS_11_0_IPAD_6TH
151
+ add_ios 'iPad Pro 12.9', '11.0', hash, APPIUM_1_7_0 # IOS_11_0_IPAD_PRO_12_9
152
+
153
+ hash
154
+ end
155
+ end
156
+
157
+ # The hash of device capabilities, accessible by simple names
158
+ DEVICE_HASH = create_hash
159
+ end
160
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maze
4
+ # Utils supporting the BrowserStack device farm integration
5
+ class BrowserStackUtils
6
+ class << self
7
+
8
+ # Uploads an app to BrowserStack for later consumption
9
+ # @param username [String] the BrowserStack username
10
+ # @param access_key [String] the BrowserStack access key
11
+ # @param app_id_file [String] the file to write the uploaded app url to BrowserStack
12
+ def upload_app(username, access_key, app, app_id_file=nil)
13
+ if app.start_with? 'bs://'
14
+ app_url = app
15
+ $logger.info "Using pre-uploaded app from #{app}"
16
+ else
17
+ expanded_app = Maze::Helper.expand_path(app)
18
+
19
+ uri = URI('https://api-cloud.browserstack.com/app-automate/upload')
20
+ request = Net::HTTP::Post.new(uri)
21
+ request.basic_auth(username, access_key)
22
+ request.set_form({ 'file' => File.new(expanded_app, 'rb') }, 'multipart/form-data')
23
+
24
+ upload_tries = 0
25
+ allowed_tries = 10
26
+
27
+ while upload_tries < allowed_tries
28
+ $logger.info "Uploading app: #{expanded_app}"
29
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
30
+ http.request(request)
31
+ end
32
+
33
+ begin
34
+ body = res.body
35
+ response = JSON.parse body
36
+ if response.include?('error')
37
+ $logger.error "Upload failed due to error: #{response['error']}"
38
+ elsif !response.include?('app_url')
39
+ $logger.error "Upload failed, response did not include an app_url: #{res}"
40
+ else
41
+ # Successful upload
42
+ break
43
+ end
44
+ rescue JSON::ParserError
45
+ $logger.error "Error: expected JSON response, received: #{body}"
46
+ end
47
+
48
+ upload_tries += 1
49
+ if upload_tries < allowed_tries
50
+ $logger.info 'Retrying upload in 60s'
51
+ Kernel::sleep 60
52
+ end
53
+ end
54
+
55
+ if response.nil? || response.include?('error') || !response.include?('app_url')
56
+ raise "Failed to upload app after #{upload_tries} attempts"
57
+ end
58
+
59
+ app_url = response['app_url']
60
+ $logger.info "app uploaded to: #{app_url}"
61
+ $logger.info 'You can use this url to avoid uploading the same app more than once.'
62
+ end
63
+
64
+ unless app_id_file.nil?
65
+ $logger.info "Writing uploaded app url to #{app_id_file}"
66
+ File.write(Maze::Helper.expand_path(app_id_file), app_url)
67
+ end
68
+
69
+ app_url
70
+ end
71
+
72
+ # Starts the BrowserStack local tunnel
73
+ # @param bs_local [String] path to the BrowserStackLocal binary
74
+ # @param local_id [String] unique key for the tunnel instance
75
+ # @param access_key [String] BrowserStack access key
76
+ def start_local_tunnel(bs_local, local_id, access_key)
77
+ $logger.info 'Starting BrowserStack local tunnel'
78
+ command = "#{bs_local} -d start --key #{access_key} --local-identifier #{local_id} " \
79
+ '--force-local --only-automate --force'
80
+
81
+ # Extract the pid from the output so it gets killed at the end of the run
82
+ output = Runner.run_command(command)[0][0]
83
+ begin
84
+ @pid = JSON.parse(output)['pid']
85
+ $logger.info "BrowserStackLocal daemon running under pid #{@pid}"
86
+ rescue JSON::ParserError
87
+ $logger.warn 'Unable to parse pid from output, BrowserStackLocal will be left to die its own death'
88
+ end
89
+ end
90
+
91
+ # Stops the local tunnel
92
+ def stop_local_tunnel
93
+ if @pid
94
+ $logger.info "Stopping BrowserStack local tunnel"
95
+ Process.kill('TERM', @pid)
96
+ @pid = nil
97
+ end
98
+ rescue Errno::ESRCH
99
+ # ignored
100
+ end
101
+
102
+ # Gets the build/session info from BrowserStack
103
+ # @param username [String] the BrowserStack username
104
+ # @param access_key [String] the BrowserStack access key
105
+ # @param build_name [String] the name of the BrowserStack build
106
+ def build_info(username, access_key, build_name)
107
+ # Get the ID of a build
108
+ uri = URI("https://api.browserstack.com/app-automate/builds.json?name=#{build_name}")
109
+ request = Net::HTTP::Get.new(uri)
110
+ request.basic_auth(username, access_key)
111
+
112
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
113
+ http.request(request)
114
+ end
115
+
116
+ build_info = JSON.parse(res.body)
117
+
118
+ if !build_info.empty?
119
+ build_id = build_info[0]['automation_build']['hashed_id']
120
+
121
+ # Get the build info
122
+ uri = URI("https://api.browserstack.com/app-automate/builds/#{build_id}/sessions")
123
+ request = Net::HTTP::Get.new(uri)
124
+ request.basic_auth(username, access_key)
125
+
126
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
127
+ http.request(request)
128
+ end
129
+
130
+ build_json = JSON.parse(res.body)
131
+ else
132
+ raise "No build found for given ID: #{build_name}"
133
+ end
134
+ build_json
135
+ end
136
+
137
+ # @param username [String] the BrowserStack username
138
+ # @param access_key [String] the BrowserStack access key
139
+ # @param name [String] name of the build the log is being downloaded from
140
+ # @param log_url [String] url to the log
141
+ # @param log_type [Symbol] The type of log we are downloading
142
+ def download_log(username, access_key, name, log_url, log_type)
143
+ begin
144
+ path = File.join(Dir.pwd, 'maze_output', log_type.to_s)
145
+ FileUtils.makedirs(path)
146
+
147
+ uri = URI(log_url)
148
+ request = Net::HTTP::Get.new(uri)
149
+ request.basic_auth(username, access_key)
150
+
151
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
152
+ http.request(request)
153
+ end
154
+
155
+ $logger.info "Saving #{log_type.to_s} log to #{path}/#{name}.log"
156
+ File.open("#{path}/#{name}.log", 'w+') { |file| file.write(res.body) }
157
+ rescue StandardError => e
158
+ $logger.warn "Unable to save log from #{log_url}"
159
+ $logger.warn e
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,220 @@
1
+ # Selenium capabilities for browsers available on BrowserStack
2
+ ---
3
+ ie_8:
4
+ browser: "ie"
5
+ browser_version: "8"
6
+ os: "windows"
7
+ os_version: "7"
8
+
9
+ ie_9:
10
+ browser: "ie"
11
+ browser_version: "9"
12
+ os: "windows"
13
+ os_version: "7"
14
+
15
+ ie_10:
16
+ browser: "ie"
17
+ browser_version: "10"
18
+ os: "windows"
19
+ os_version: "8"
20
+
21
+ ie_11:
22
+ browser: "ie"
23
+ browser_version: "11"
24
+ os: "windows"
25
+ os_version: "10"
26
+
27
+ edge_14:
28
+ browser: "edge"
29
+ browser_version: "14"
30
+ os: "windows"
31
+ os_version: "10"
32
+
33
+ edge_15:
34
+ browser: "edge"
35
+ browser_version: "15"
36
+ os: "windows"
37
+ os_version: "10"
38
+
39
+ edge_17:
40
+ browser: "edge"
41
+ browser_version: "17"
42
+ os: "windows"
43
+ os_version: "10"
44
+
45
+ safari_6:
46
+ browser: "safari"
47
+ browser_version: "6"
48
+ os: "OS X"
49
+ os_version: "lion"
50
+
51
+ safari_10:
52
+ browser: "safari"
53
+ browser_version: "10.0"
54
+ os: "OS X"
55
+ os_version: "sierra"
56
+
57
+ safari_13:
58
+ browser: "Safari"
59
+ browser_version: "13.0"
60
+ os: "OS X"
61
+ os_version: "Catalina"
62
+
63
+ safari_14:
64
+ browser: "Safari"
65
+ browser_version: "14.0"
66
+ os: "OS X"
67
+ os_version: "Big Sur"
68
+
69
+ safari_15:
70
+ browser: "Safari"
71
+ browser_version: "15.0"
72
+ os: "OS X"
73
+ os_version: "Monterey"
74
+
75
+ iphone_6s:
76
+ browser: "iphone"
77
+ device: "iPhone 6S"
78
+ os: "ios"
79
+ os_version: "9.0"
80
+ real_mobile: true
81
+
82
+ iphone_7:
83
+ browser: "iphone"
84
+ device: "iPhone 7"
85
+ os: "ios"
86
+ os_version: "10.3"
87
+ real_mobile: true
88
+
89
+ iphone_13:
90
+ browser: "iphone"
91
+ device: "iPhone 13"
92
+ os: "ios"
93
+ os_version: "15.4"
94
+ real_mobile: true
95
+
96
+ android_nexus5:
97
+ browser: "Android Browser"
98
+ device: "Google Nexus 5"
99
+ os: "android"
100
+ os_version: "4.4"
101
+ real_mobile: true
102
+
103
+ android_s6:
104
+ browser: "Android Browser"
105
+ device: "Samsung Galaxy S6"
106
+ os: "android"
107
+ os_version: "5.0"
108
+ real_mobile: true
109
+
110
+ android_s7:
111
+ browser: "Android Browser"
112
+ device: "Samsung Galaxy S7"
113
+ os: "android"
114
+ os_version: "6.0"
115
+ real_mobile: true
116
+
117
+ android_s8:
118
+ browser: "Android Browser"
119
+ device: "Samsung Galaxy S8 Plus"
120
+ os: "android"
121
+ os_version: "7.0"
122
+ real_mobile: true
123
+
124
+ firefox_30:
125
+ browser: "firefox"
126
+ browser_version: "30"
127
+ os: "windows"
128
+ os_version: "7"
129
+
130
+ firefox_56:
131
+ browser: "firefox"
132
+ browser_version: "56"
133
+ os: "windows"
134
+ os_version: "10"
135
+
136
+ firefox_78:
137
+ browser: "firefox"
138
+ browser_version: "78"
139
+ os: "windows"
140
+ os_version: "10"
141
+
142
+ firefox_latest:
143
+ browser: "firefox"
144
+ browser_version: "latest"
145
+ os: "windows"
146
+ os_version: "10"
147
+ selenium_version: "3.10.0"
148
+
149
+ chrome_30:
150
+ browser: "chrome"
151
+ browser_version: "30.0"
152
+ os: "windows"
153
+ os_version: "7"
154
+
155
+ chrome_32:
156
+ browser: "chrome"
157
+ browser_version: "32.0"
158
+ os: "windows"
159
+ os_version: "7"
160
+
161
+ chrome_34:
162
+ browser: "chrome"
163
+ browser_version: "34.0"
164
+ os: "windows"
165
+ os_version: "7"
166
+
167
+ chrome_36:
168
+ browser: "chrome"
169
+ browser_version: "36.0"
170
+ os: "windows"
171
+ os_version: "7"
172
+
173
+ chrome_38:
174
+ browser: "chrome"
175
+ browser_version: "38.0"
176
+ os: "windows"
177
+ os_version: "7"
178
+
179
+ chrome_40:
180
+ browser: "chrome"
181
+ browser_version: "40.0"
182
+ os: "windows"
183
+ os_version: "7"
184
+
185
+ chrome_42:
186
+ browser: "chrome"
187
+ browser_version: "42.0"
188
+ os: "windows"
189
+ os_version: "7"
190
+
191
+ chrome_43:
192
+ browser: "chrome"
193
+ browser_version: "43.0"
194
+ os: "windows"
195
+ os_version: "7"
196
+
197
+ chrome_53:
198
+ browser: "chrome"
199
+ browser_version: "53.0"
200
+ os: "windows"
201
+ os_version: "10"
202
+
203
+ chrome_61:
204
+ browser: "chrome"
205
+ browser_version: "61.0"
206
+ os: "windows"
207
+ os_version: "10"
208
+
209
+ chrome_72:
210
+ browser: "chrome"
211
+ browser_version: "72.0"
212
+ os: "windows"
213
+ os_version: "10"
214
+
215
+ chrome_latest:
216
+ browser: "chrome"
217
+ browser_version: "latest"
218
+ os: "windows"
219
+ os_version: "10"
220
+ selenium_version: "3.14.0"