bugsnag-maze-runner 6.27.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/bugsnag-print-load-paths +6 -0
- data/bin/download-logs +76 -0
- data/bin/maze-runner +136 -0
- data/bin/upload-app +56 -0
- data/lib/features/scripts/await-android-emulator.sh +11 -0
- data/lib/features/scripts/clear-android-app-data.sh +8 -0
- data/lib/features/scripts/force-stop-android-app.sh +8 -0
- data/lib/features/scripts/install-android-app.sh +15 -0
- data/lib/features/scripts/launch-android-app.sh +38 -0
- data/lib/features/scripts/launch-android-emulator.sh +15 -0
- data/lib/features/steps/android_steps.rb +51 -0
- data/lib/features/steps/app_automator_steps.rb +228 -0
- data/lib/features/steps/aws_sam_steps.rb +212 -0
- data/lib/features/steps/breadcrumb_steps.rb +50 -0
- data/lib/features/steps/browser_steps.rb +93 -0
- data/lib/features/steps/build_api_steps.rb +25 -0
- data/lib/features/steps/document_server_steps.rb +7 -0
- data/lib/features/steps/error_reporting_steps.rb +342 -0
- data/lib/features/steps/feature_flag_steps.rb +190 -0
- data/lib/features/steps/header_steps.rb +72 -0
- data/lib/features/steps/log_steps.rb +29 -0
- data/lib/features/steps/multipart_request_steps.rb +142 -0
- data/lib/features/steps/network_steps.rb +75 -0
- data/lib/features/steps/payload_steps.rb +234 -0
- data/lib/features/steps/proxy_steps.rb +34 -0
- data/lib/features/steps/query_parameter_steps.rb +31 -0
- data/lib/features/steps/request_assertion_steps.rb +107 -0
- data/lib/features/steps/runner_steps.rb +406 -0
- data/lib/features/steps/session_tracking_steps.rb +116 -0
- data/lib/features/steps/value_steps.rb +119 -0
- data/lib/features/support/env.rb +7 -0
- data/lib/features/support/internal_hooks.rb +260 -0
- data/lib/maze/appium_server.rb +112 -0
- data/lib/maze/assertions/request_set_assertions.rb +97 -0
- data/lib/maze/aws/sam.rb +112 -0
- data/lib/maze/bitbar_devices.rb +84 -0
- data/lib/maze/bitbar_utils.rb +112 -0
- data/lib/maze/browser_stack_devices.rb +160 -0
- data/lib/maze/browser_stack_utils.rb +164 -0
- data/lib/maze/browsers_bs.yml +220 -0
- data/lib/maze/browsers_cbt.yml +100 -0
- data/lib/maze/bugsnag_config.rb +42 -0
- data/lib/maze/capabilities.rb +126 -0
- data/lib/maze/checks/assert_check.rb +91 -0
- data/lib/maze/checks/noop_check.rb +34 -0
- data/lib/maze/compare.rb +161 -0
- data/lib/maze/configuration.rb +174 -0
- data/lib/maze/docker.rb +108 -0
- data/lib/maze/document_server.rb +46 -0
- data/lib/maze/driver/appium.rb +217 -0
- data/lib/maze/driver/browser.rb +138 -0
- data/lib/maze/driver/resilient_appium.rb +51 -0
- data/lib/maze/errors.rb +20 -0
- data/lib/maze/helper.rb +118 -0
- data/lib/maze/hooks/appium_hooks.rb +216 -0
- data/lib/maze/hooks/browser_hooks.rb +68 -0
- data/lib/maze/hooks/command_hooks.rb +9 -0
- data/lib/maze/hooks/hooks.rb +61 -0
- data/lib/maze/interactive_cli.rb +173 -0
- data/lib/maze/logger.rb +73 -0
- data/lib/maze/macos_utils.rb +14 -0
- data/lib/maze/network.rb +49 -0
- data/lib/maze/option/parser.rb +245 -0
- data/lib/maze/option/processor.rb +143 -0
- data/lib/maze/option/validator.rb +184 -0
- data/lib/maze/option.rb +64 -0
- data/lib/maze/plugins/bugsnag_reporting_plugin.rb +49 -0
- data/lib/maze/plugins/cucumber_report_plugin.rb +101 -0
- data/lib/maze/plugins/global_retry_plugin.rb +38 -0
- data/lib/maze/proxy.rb +114 -0
- data/lib/maze/request_list.rb +82 -0
- data/lib/maze/retry_handler.rb +76 -0
- data/lib/maze/runner.rb +149 -0
- data/lib/maze/sauce_labs_utils.rb +96 -0
- data/lib/maze/server.rb +207 -0
- data/lib/maze/servlets/base_servlet.rb +22 -0
- data/lib/maze/servlets/command_servlet.rb +44 -0
- data/lib/maze/servlets/log_servlet.rb +64 -0
- data/lib/maze/servlets/reflective_servlet.rb +69 -0
- data/lib/maze/servlets/servlet.rb +160 -0
- data/lib/maze/smart_bear_utils.rb +71 -0
- data/lib/maze/store.rb +15 -0
- data/lib/maze/terminating_server.rb +129 -0
- data/lib/maze/timers.rb +51 -0
- data/lib/maze/wait.rb +35 -0
- data/lib/maze.rb +27 -0
- 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"
|