bugsnag-maze-runner 6.27.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/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"
|