bugsnag-maze-runner 6.27.0 → 7.22.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/bin/download-logs +14 -16
  3. data/bin/maze-runner +53 -15
  4. data/bin/upload-app +6 -6
  5. data/lib/features/steps/breadcrumb_steps.rb +44 -14
  6. data/lib/features/steps/error_reporting_steps.rb +16 -0
  7. data/lib/features/steps/network_steps.rb +66 -6
  8. data/lib/features/steps/payload_steps.rb +23 -0
  9. data/lib/features/steps/request_assertion_steps.rb +87 -8
  10. data/lib/features/steps/runner_steps.rb +22 -0
  11. data/lib/features/steps/session_tracking_steps.rb +1 -1
  12. data/lib/features/steps/trace_steps.rb +206 -0
  13. data/lib/features/support/internal_hooks.rb +31 -84
  14. data/lib/maze/api/appium/file_manager.rb +29 -0
  15. data/lib/maze/aws_public_ip.rb +53 -0
  16. data/lib/maze/checks/assert_check.rb +9 -31
  17. data/lib/maze/client/appium/base_client.rb +131 -0
  18. data/lib/maze/client/appium/bb_client.rb +102 -0
  19. data/lib/maze/client/appium/bb_devices.rb +127 -0
  20. data/lib/maze/client/appium/bs_client.rb +91 -0
  21. data/lib/maze/client/appium/bs_devices.rb +141 -0
  22. data/lib/maze/client/appium/bs_legacy_client.rb +31 -0
  23. data/lib/maze/client/appium/local_client.rb +67 -0
  24. data/lib/maze/client/appium.rb +23 -0
  25. data/lib/maze/client/bb_api_client.rb +102 -0
  26. data/lib/maze/client/bb_client_utils.rb +181 -0
  27. data/lib/maze/client/bs_client_utils.rb +168 -0
  28. data/lib/maze/client/selenium/base_client.rb +15 -0
  29. data/lib/maze/client/selenium/bb_browsers.yml +188 -0
  30. data/lib/maze/client/selenium/bb_client.rb +38 -0
  31. data/lib/maze/client/selenium/bs_browsers.yml +257 -0
  32. data/lib/maze/client/selenium/bs_client.rb +89 -0
  33. data/lib/maze/client/selenium/local_client.rb +16 -0
  34. data/lib/maze/client/selenium.rb +16 -0
  35. data/lib/maze/configuration.rb +18 -10
  36. data/lib/maze/docker.rb +40 -1
  37. data/lib/maze/driver/appium.rb +5 -24
  38. data/lib/maze/driver/browser.rb +12 -26
  39. data/lib/maze/errors.rb +32 -0
  40. data/lib/maze/generator.rb +55 -0
  41. data/lib/maze/helper.rb +7 -3
  42. data/lib/maze/hooks/appium_hooks.rb +29 -190
  43. data/lib/maze/hooks/browser_hooks.rb +2 -55
  44. data/lib/maze/hooks/error_code_hook.rb +49 -0
  45. data/lib/maze/hooks/hooks.rb +2 -2
  46. data/lib/maze/http_request.rb +21 -0
  47. data/lib/maze/logger.rb +16 -3
  48. data/lib/maze/maze_output.rb +88 -0
  49. data/lib/maze/option/parser.rb +17 -22
  50. data/lib/maze/option/processor.rb +21 -34
  51. data/lib/maze/option/validator.rb +38 -67
  52. data/lib/maze/option.rb +16 -18
  53. data/lib/maze/plugins/cucumber_report_plugin.rb +1 -1
  54. data/lib/maze/plugins/error_code_plugin.rb +21 -0
  55. data/lib/maze/request_list.rb +10 -5
  56. data/lib/maze/request_repeater.rb +49 -0
  57. data/lib/maze/retry_handler.rb +4 -13
  58. data/lib/maze/schemas/OtelTraceSchema.json +390 -0
  59. data/lib/maze/schemas/trace_schema.rb +7 -0
  60. data/lib/maze/schemas/trace_validator.rb +98 -0
  61. data/lib/maze/server.rb +74 -30
  62. data/lib/maze/servlets/base_servlet.rb +10 -5
  63. data/lib/maze/servlets/command_servlet.rb +10 -7
  64. data/lib/maze/servlets/log_servlet.rb +2 -2
  65. data/lib/maze/servlets/reflective_servlet.rb +12 -11
  66. data/lib/maze/servlets/servlet.rb +47 -8
  67. data/lib/maze/servlets/temp.rb +0 -0
  68. data/lib/maze/servlets/trace_servlet.rb +13 -0
  69. data/lib/maze.rb +2 -2
  70. data/lib/utils/deep_merge.rb +17 -0
  71. data/lib/utils/selenium_money_patch.rb +17 -0
  72. metadata +101 -21
  73. data/lib/maze/bitbar_devices.rb +0 -84
  74. data/lib/maze/bitbar_utils.rb +0 -112
  75. data/lib/maze/browser_stack_devices.rb +0 -160
  76. data/lib/maze/browser_stack_utils.rb +0 -164
  77. data/lib/maze/browsers_bs.yml +0 -220
  78. data/lib/maze/browsers_cbt.yml +0 -100
  79. data/lib/maze/capabilities.rb +0 -126
  80. data/lib/maze/driver/resilient_appium.rb +0 -51
  81. data/lib/maze/sauce_labs_utils.rb +0 -96
  82. data/lib/maze/smart_bear_utils.rb +0 -71
@@ -26,16 +26,25 @@ module Maze
26
26
  text ''
27
27
  text 'General options:'
28
28
 
29
+ opt Option::AWS_PUBLIC_IP,
30
+ 'Intended for use on Buildkite with the Elastic CI Stack for CI. Enables awareness of being run with a public IP address.',
31
+ type: :boolean,
32
+ default: false
33
+
29
34
  opt Option::ENABLE_RETRIES,
30
35
  'Enables retrying failed scenarios when tagged',
31
36
  type: :boolean,
32
37
  default: true
33
38
 
34
39
  opt Option::ENABLE_BUGSNAG,
35
- 'Enables reporting to Bugsnag on scenario failure (Require MAZE_BUGSNAG_API_KEY)',
40
+ 'Enables reporting to Bugsnag on scenario failure (requires MAZE_BUGSNAG_API_KEY)',
36
41
  type: :boolean,
37
42
  default: true
38
43
 
44
+ opt Option::REPEATER_API_KEY,
45
+ 'Enables forwarding of all received POST requests to Bugsnag, using the API key provided. MAZE_REPEATER_API_KEY may also be set.',
46
+ type: :string
47
+
39
48
  text ''
40
49
  text 'Server options:'
41
50
 
@@ -65,10 +74,6 @@ module Maze
65
74
  text ''
66
75
  text 'Appium options:'
67
76
 
68
- opt Option::SEPARATE_SESSIONS,
69
- 'Start a new Appium session for each scenario',
70
- type: :boolean,
71
- default: false
72
77
  opt Option::FARM,
73
78
  'Device farm to use: "bs" (BrowserStack) or "local"',
74
79
  type: :string
@@ -79,9 +84,6 @@ module Maze
79
84
  'Locate elements by accessibility id rather than id',
80
85
  type: :boolean,
81
86
  default: false
82
- opt Option::RESILIENT,
83
- 'Use the resilient Appium driver',
84
- default: false
85
87
  opt Option::CAPABILITIES,
86
88
  'Additional desired Appium capabilities as a JSON string',
87
89
  default: '{}'
@@ -95,7 +97,7 @@ module Maze
95
97
  type: :string,
96
98
  multi: true
97
99
  opt Option::BROWSER,
98
- 'Browser to use (an entry in browsers_<farm>.yml)',
100
+ 'Browser to use (an entry in <farm>_browsers.yml)',
99
101
  short: :none,
100
102
  type: :string
101
103
  opt Option::USERNAME,
@@ -108,22 +110,20 @@ module Maze
108
110
  'The Appium version to use',
109
111
  type: :string
110
112
  opt Option::LIST_DEVICES,
111
- 'Lists the devices available for the configured device-farm, or all devices if none are specified',
113
+ 'Lists the devices available for the configured device farm, or all devices if none are specified',
112
114
  default: false
113
115
  opt Option::APP_BUNDLE_ID,
114
116
  'The bundle identifier of the test application',
115
117
  type: :string
118
+ opt Option::TUNNEL,
119
+ 'Start the device farm secure tunnel',
120
+ default: true
116
121
 
117
122
  # SmartBear-only options
118
123
  opt Option::SB_LOCAL,
119
124
  '(SB only) Path to the SBSecureTunnel binary. MAZE_SB_LOCAL env var or "/SBSecureTunnel" by default',
120
125
  type: :string
121
126
 
122
- # Sauce Labs-only options
123
- opt Option::SL_LOCAL,
124
- '(SL only) Path to the Sauce Connect binary. MAZE_SL_LOCAL env var or "/sauce-connect/bin/sc" by default',
125
- type: :string
126
-
127
127
  # BrowserStack-only options
128
128
  opt Option::BS_LOCAL,
129
129
  '(BS only) Path to the BrowserStackLocal binary. MAZE_BS_LOCAL env var or "/BrowserStackLocal" by default',
@@ -209,9 +209,6 @@ module Maze
209
209
  # @returns [Hash] The options hash with environment vars added
210
210
  def populate_environmental_defaults(options)
211
211
  case options.farm
212
- when 'cbt'
213
- options[Option::USERNAME] ||= ENV['CBT_USERNAME']
214
- options[Option::ACCESS_KEY] ||= ENV['CBT_ACCESS_KEY']
215
212
  when 'bs'
216
213
  # Allow browser/device credentials to exist in separate accounts
217
214
  if options[Option::BROWSER]
@@ -221,19 +218,17 @@ module Maze
221
218
  options[Option::USERNAME] ||= ENV['BROWSER_STACK_DEVICES_USERNAME'] || ENV['BROWSER_STACK_USERNAME']
222
219
  options[Option::ACCESS_KEY] ||= ENV['BROWSER_STACK_DEVICES_ACCESS_KEY'] ||ENV['BROWSER_STACK_ACCESS_KEY']
223
220
  end
224
- when 'sl'
225
- options[Option::USERNAME] ||= ENV['SAUCE_LABS_USERNAME']
226
- options[Option::ACCESS_KEY] ||= ENV['SAUCE_LABS_ACCESS_KEY']
227
221
  when 'bb'
228
222
  options[Option::USERNAME] ||= ENV['BITBAR_USERNAME']
229
223
  options[Option::ACCESS_KEY] ||= ENV['BITBAR_ACCESS_KEY']
230
224
  options[Option::TMS_URI] ||= ENV['MAZE_TMS_URI']
231
225
  end
226
+
227
+ options[Option::REPEATER_API_KEY] ||= ENV['MAZE_REPEATER_API_KEY']
232
228
  options[Option::SB_LOCAL] ||= ENV['MAZE_SB_LOCAL'] || '/SBSecureTunnel'
233
229
  options[Option::TMS_URI] ||= ENV['MAZE_TMS_URI']
234
230
  options[Option::TMS_TOKEN] ||= ENV['MAZE_TMS_TOKEN']
235
231
  options[Option::BS_LOCAL] ||= ENV['MAZE_BS_LOCAL'] || '/BrowserStackLocal'
236
- options[Option::SL_LOCAL] ||= ENV['MAZE_SL_LOCAL'] || '/sauce-connect/bin/sc'
237
232
  options[Option::APPIUM_SERVER] ||= ENV['MAZE_APPIUM_SERVER'] || 'http://localhost:4723/wd/hub'
238
233
  options[Option::APPLE_TEAM_ID] ||= ENV['MAZE_APPLE_TEAM_ID']
239
234
  options[Option::UDID] ||= ENV['MAZE_UDID']
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../option'
4
- require_relative '../browser_stack_devices'
5
4
 
6
5
  module Maze
7
6
  module Option
@@ -19,10 +18,12 @@ module Maze
19
18
  config.null_port = options[Maze::Option::NULL_PORT]
20
19
 
21
20
  # General options
21
+ config.aws_public_ip = options[Maze::Option::AWS_PUBLIC_IP]
22
22
  config.enable_retries = options[Maze::Option::ENABLE_RETRIES]
23
23
  config.enable_bugsnag = options[Maze::Option::ENABLE_BUGSNAG]
24
24
  config.tms_uri = options[Maze::Option::TMS_URI]
25
25
  config.tms_token = options[Maze::Option::TMS_TOKEN]
26
+ config.repeater_api_key = options[Maze::Option::REPEATER_API_KEY]
26
27
 
27
28
  # Document server options
28
29
  config.document_server_root = options[Maze::Option::DS_ROOT]
@@ -35,15 +36,11 @@ module Maze
35
36
  config.always_log = options[Maze::Option::ALWAYS_LOG]
36
37
 
37
38
  # General appium options
38
- config.appium_session_isolation = options[Maze::Option::SEPARATE_SESSIONS]
39
39
  config.app = Maze::Helper.read_at_arg_file options[Maze::Option::APP]
40
- config.resilient = options[Maze::Option::RESILIENT]
41
40
  farm = options[Maze::Option::FARM]
42
41
  config.farm = case farm
43
42
  when nil then :none
44
- when 'cbt' then :cbt
45
43
  when 'bs' then :bs
46
- when 'sl' then :sl
47
44
  when 'bb' then :bb
48
45
  when 'local' then :local
49
46
  else
@@ -51,14 +48,11 @@ module Maze
51
48
  end
52
49
  config.locator = options[Maze::Option::A11Y_LOCATOR] ? :accessibility_id : :id
53
50
  config.capabilities_option = options[Maze::Option::CAPABILITIES]
51
+ config.legacy_driver = !ENV['USE_LEGACY_DRIVER'].nil?
52
+ config.start_tunnel = options[Maze::Option::TUNNEL]
54
53
 
55
54
  # Farm specific options
56
55
  case config.farm
57
- when :cbt
58
- config.browser = options[Maze::Option::BROWSER]
59
- config.sb_local = Maze::Helper.expand_path(options[Maze::Option::SB_LOCAL])
60
- username = config.username = options[Maze::Option::USERNAME]
61
- access_key = config.access_key = options[Maze::Option::ACCESS_KEY]
62
56
  when :bs
63
57
  device_option = options[Maze::Option::DEVICE]
64
58
  if device_option.nil? || device_option.empty?
@@ -71,46 +65,39 @@ module Maze
71
65
  config.device = device_option
72
66
  config.device_list = []
73
67
  end
74
- config.os_version = Maze::BrowserStackDevices::DEVICE_HASH[config.device]['os_version'].to_f
68
+ if config.legacy_driver?
69
+ config.os_version = Maze::Client::Appium::BrowserStackDevices::DEVICE_HASH[config.device]['os_version'].to_f
70
+ else
71
+ config.os_version = Maze::Client::Appium::BrowserStackDevices::DEVICE_HASH[config.device]['platformVersion'].to_f
72
+ end
75
73
  end
76
74
  config.bs_local = Maze::Helper.expand_path(options[Maze::Option::BS_LOCAL])
77
75
  config.appium_version = options[Maze::Option::APPIUM_VERSION]
78
76
  username = config.username = options[Maze::Option::USERNAME]
79
77
  access_key = config.access_key = options[Maze::Option::ACCESS_KEY]
80
78
  config.appium_server_url = "http://#{username}:#{access_key}@hub-cloud.browserstack.com/wd/hub"
81
- when :sl
82
- device_option = options[Maze::Option::DEVICE]
83
- if device_option.is_a?(Array)
84
- config.device = device_option.first
85
- config.device_list = device_option.drop(1)
86
- else
87
- config.device = device_option
88
- config.device_list = []
89
- end
90
- config.browser = options[Maze::Option::BROWSER]
91
- config.os = options[Maze::Option::OS]
92
- config.os_version = options[Maze::Option::OS_VERSION].to_f
93
- config.sl_local = Maze::Helper.expand_path(options[Maze::Option::SL_LOCAL])
94
- config.appium_version = options[Maze::Option::APPIUM_VERSION]
95
- username = config.username = options[Maze::Option::USERNAME]
96
- access_key = config.access_key = options[Maze::Option::ACCESS_KEY]
97
- config.appium_server_url = "https://#{username}:#{access_key}@ondemand.us-west-1.saucelabs.com/wd/hub"
98
79
  when :bb then
99
80
  config.username = options[Maze::Option::USERNAME]
100
81
  config.access_key = options[Maze::Option::ACCESS_KEY]
101
82
  config.tms_uri = options[Maze::Option::TMS_URI]
102
83
  device_option = options[Maze::Option::DEVICE]
103
- if device_option.is_a?(Array)
104
- config.device = device_option.first
105
- config.device_list = device_option.drop(1)
84
+ if device_option.nil? || device_option.empty?
85
+ # BitBar Web
86
+ config.browser = options[Maze::Option::BROWSER]
106
87
  else
107
- config.device = device_option
108
- config.device_list = []
88
+ # BitBar Devices
89
+ if device_option.is_a?(Array)
90
+ config.device = device_option.first
91
+ config.device_list = device_option.drop(1)
92
+ else
93
+ config.device = device_option
94
+ config.device_list = []
95
+ end
109
96
  end
110
97
  config.os = options[Maze::Option::OS]
111
98
  config.os_version = options[Maze::Option::OS_VERSION]
112
99
  config.sb_local = Maze::Helper.expand_path(options[Maze::Option::SB_LOCAL])
113
- config.appium_server_url = 'https://appium.bitbar.com/wd/hub'
100
+ config.appium_server_url = 'https://us-west-mobile-hub.bitbar.com/wd/hub'
114
101
  config.app_bundle_id = options[Maze::Option::APP_BUNDLE_ID]
115
102
  when :local then
116
103
  if options[Maze::Option::BROWSER]
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'yaml'
4
4
  require_relative '../option'
5
- require_relative '../browser_stack_devices'
6
5
 
7
6
  module Maze
8
7
  module Option
@@ -13,21 +12,32 @@ module Maze
13
12
  def validate(options)
14
13
  errors = []
15
14
 
15
+ #
16
16
  # Common options
17
+ #
18
+
19
+ # --farm
17
20
  farm = options[Option::FARM]
18
- if farm && !%w[bs cbt sl local bb].include?(farm)
19
- errors << "--#{Option::FARM} must be 'bs', 'cbt', 'sl', 'bb' or 'local' if provided"
21
+ if farm && !%w[bs local bb].include?(farm)
22
+ errors << "--#{Option::FARM} must be 'bs', 'bb' or 'local' if provided"
20
23
  end
21
24
 
25
+ # --capabilities
22
26
  begin
23
27
  JSON.parse(options[Option::CAPABILITIES])
24
28
  rescue JSON::ParserError
25
29
  errors << "--#{Option::CAPABILITIES} must be valid JSON (given #{options[Option::CAPABILITIES]})"
26
30
  end
27
31
 
32
+ # --repeater-api-key
33
+ key = options[Option::REPEATER_API_KEY]
34
+ key_regex = /^[0-9a-fA-F]{32}$/
35
+ if key && !key_regex.match?(key)
36
+ errors << "--#{Option::REPEATER_API_KEY} must be set to a 32-character hex value"
37
+ end
38
+
28
39
  # Farm specific options
29
40
  validate_bs options, errors if farm == 'bs'
30
- validate_sl options, errors if farm == 'sl'
31
41
  validate_bitbar options, errors if farm == 'bb'
32
42
  validate_local options, errors if farm == 'local'
33
43
 
@@ -47,7 +57,7 @@ module Maze
47
57
  errors << "Either --#{Option::BROWSER} or --#{Option::DEVICE} must be specified"
48
58
  elsif browser
49
59
 
50
- browsers = YAML.safe_load(File.read("#{__dir__}/../browsers_bs.yml"))
60
+ browsers = YAML.safe_load(File.read("#{__dir__}/../client/selenium/bs_browsers.yml"))
51
61
 
52
62
  unless browsers.include? browser
53
63
  browser_list = browsers.keys.join ', '
@@ -55,15 +65,14 @@ module Maze
55
65
  end
56
66
  elsif device
57
67
  device.each do |device_key|
58
- next if Maze::BrowserStackDevices::DEVICE_HASH.key? device_key
59
- errors << "Device type '#{device_key}' unknown on BrowserStack. Must be one of #{Maze::BrowserStackDevices::DEVICE_HASH.keys}"
68
+ next if Maze::Client::Appium::BrowserStackDevices::DEVICE_HASH.key? device_key
69
+ errors << "Device type '#{device_key}' unknown on BrowserStack. Must be one of #{Maze::Client::Appium::BrowserStackDevices::DEVICE_HASH.keys}"
60
70
  end
61
71
  # App
62
72
  app = Maze::Helper.read_at_arg_file options[Option::APP]
63
73
  if app.nil?
64
74
  errors << "--#{Option::APP} must be provided when running on a device"
65
75
  else
66
- # TODO: What about Sauce Labs URLs?
67
76
  unless app.start_with?('bs://')
68
77
  app = Maze::Helper.expand_path app
69
78
  errors << "app file '#{app}' not found" unless File.exist?(app)
@@ -76,77 +85,39 @@ module Maze
76
85
  errors << "--#{Option::ACCESS_KEY} must be specified" if options[Option::ACCESS_KEY].nil?
77
86
  end
78
87
 
79
- # Validates Sauce Labs options
80
- def validate_sl(options, errors)
81
- # SL local binary
82
- sl_local = Maze::Helper.expand_path options[Option::SL_LOCAL]
83
- errors << "Sauce Connect binary '#{sl_local}' not found" unless File.exist? sl_local
84
-
85
- # Device
88
+ # Validates BitBar device options
89
+ def validate_bitbar(options, errors)
86
90
  browser = options[Option::BROWSER]
87
91
  device = options[Option::DEVICE]
88
- os = options[Option::OS]
89
- os_version = options[Option::OS_VERSION]
90
- if browser.nil? && device.nil? && os.nil? && os_version.nil?
91
- errors << 'A device or browser option must be specified'
92
- elsif browser
93
- errors << 'Browsers not yet implemented on Sauce Labs'
92
+
93
+ if ENV['BUILDKITE'] && browser
94
+ errors << "--#{Option::TMS_URI} must be specified when running on Buildkite" if options[Option::TMS_URI].nil?
94
95
  else
95
- # App
96
+ errors << "--#{Option::USERNAME} must be specified" if options[Option::USERNAME].nil?
97
+ errors << "--#{Option::ACCESS_KEY} must be specified" if options[Option::ACCESS_KEY].nil?
98
+ end
99
+
100
+ # Device
101
+ if browser.nil? && device.empty?
102
+ errors << "Either --#{Option::BROWSER} or --#{Option::DEVICE} must be specified"
103
+ elsif browser
104
+ browsers = YAML.safe_load(File.read("#{__dir__}/../client/selenium/bb_browsers.yml"))
105
+
106
+ unless browsers.include? browser
107
+ browser_list = browsers.keys.join ', '
108
+ errors << "Browser type '#{browser}' unknown on BitBar. Must be one of: #{browser_list}."
109
+ end
110
+ elsif device
96
111
  app = options[Option::APP]
97
112
  if app.nil?
98
113
  errors << "--#{Option::APP} must be provided when running on a device"
99
114
  else
100
- uuid_regex = /\A[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}\z/
115
+ uuid_regex = /\A[0-9]+\z/
101
116
  unless uuid_regex.match? app
102
117
  app = Maze::Helper.expand_path app
103
118
  errors << "app file '#{app}' not found" unless File.exist?(app)
104
119
  end
105
120
  end
106
-
107
- # OS
108
- if options[Option::OS].nil?
109
- errors << "--#{Option::OS} must be specified"
110
- else
111
- os = options[Option::OS].downcase
112
- errors << 'os must be android or ios' unless %w[android ios].include? os
113
- end
114
-
115
- # OS Version
116
- if options[Option::OS_VERSION].nil?
117
- errors << "--#{Option::OS_VERSION} must be specified"
118
- else
119
- # Ensure OS version is a valid float so that notifier tests can perform numeric checks
120
- # e.g 'Maze.config.os_version > 7'
121
- unless /^[1-9][0-9]*(\.[0-9])?/.match? options[Option::OS_VERSION]
122
- errors << "--#{Option::OS_VERSION} must be a valid version matching '/^[1-9][0-9]*(\\.[0-9])?/'"
123
- end
124
- end
125
- end
126
-
127
- # Credentials
128
- errors << "--#{Option::USERNAME} must be specified" if options[Option::USERNAME].nil?
129
- errors << "--#{Option::ACCESS_KEY} must be specified" if options[Option::ACCESS_KEY].nil?
130
- end
131
-
132
- # Validates BitBar device options
133
- def validate_bitbar(options, errors)
134
- if ENV['BUILDKITE']
135
- errors << "--#{Option::TMS_URI} must be specified when running on Buildkite" if options[Option::TMS_URI].nil?
136
- else
137
- errors << "--#{Option::USERNAME} must be specified" if options[Option::USERNAME].nil?
138
- errors << "--#{Option::ACCESS_KEY} must be specified" if options[Option::ACCESS_KEY].nil?
139
- end
140
-
141
- app = options[Option::APP]
142
- if app.nil?
143
- errors << "--#{Option::APP} must be provided when running on a device"
144
- else
145
- uuid_regex = /\A[0-9]+\z/
146
- unless uuid_regex.match? app
147
- app = Maze::Helper.expand_path app
148
- errors << "app file '#{app}' not found" unless File.exist?(app)
149
- end
150
121
  end
151
122
  end
152
123
 
data/lib/maze/option.rb CHANGED
@@ -10,55 +10,53 @@ module Maze
10
10
 
11
11
  # Server options
12
12
  BIND_ADDRESS = 'bind-address'
13
- PORT = 'port'
14
13
  NULL_PORT = 'null-port'
14
+ PORT = 'port'
15
15
 
16
16
  # Appium options
17
- SEPARATE_SESSIONS = 'separate-sessions'
18
- FARM = 'farm'
19
- APP = 'app'
20
17
  A11Y_LOCATOR = 'a11y-locator'
21
- RESILIENT = 'resilient'
18
+ APP = 'app'
22
19
  CAPABILITIES = 'capabilities'
20
+ FARM = 'farm'
23
21
 
24
22
  # Generic device farm options
25
- USERNAME = 'username'
26
23
  ACCESS_KEY = 'access-key'
24
+ APP_BUNDLE_ID = 'app-bundle-id'
27
25
  APPIUM_VERSION = 'appium-version'
28
- DEVICE = 'device'
29
26
  BROWSER = 'browser'
27
+ DEVICE = 'device'
28
+ LIST_DEVICES = 'list-devices'
30
29
  OS = 'os'
31
30
  OS_VERSION = 'os-version'
32
- LIST_DEVICES = 'list-devices'
33
- APP_BUNDLE_ID = 'app-bundle-id'
31
+ TUNNEL = 'tunnel'
32
+ USERNAME = 'username'
34
33
 
35
- # CrossBrowserTesting/Bitbar options
34
+ # BitBar options
36
35
  SB_LOCAL = 'sb-local'
37
36
 
38
37
  # BrowserStack-only options
39
38
  BS_LOCAL = 'bs-local'
40
39
 
41
- # Sauce Labs-only options
42
- SL_LOCAL = 'sl-local'
43
-
44
40
  # BitBar-only options
45
41
  TMS_URI = 'tms-uri'
46
42
  TMS_TOKEN = 'tms-token'
47
43
 
48
44
  # Local-only options
49
- APPIUM_SERVER = 'appium-server'
50
- START_APPIUM = 'start-appium'
51
45
  APPIUM_LOGFILE = 'appium-logfile'
46
+ APPIUM_SERVER = 'appium-server'
52
47
  APPLE_TEAM_ID = 'apple-team-id'
48
+ START_APPIUM = 'start-appium'
53
49
  UDID = 'udid'
54
50
 
55
51
  # Logging options
52
+ ALWAYS_LOG = 'always-log'
56
53
  FILE_LOG = 'file-log'
57
54
  LOG_REQUESTS = 'log-requests'
58
- ALWAYS_LOG = 'always-log'
59
55
 
60
- # Runtime options
61
- ENABLE_RETRIES = 'enable-retries'
56
+ # General options
57
+ AWS_PUBLIC_IP = 'aws-public-ip'
58
+ REPEATER_API_KEY = 'repeater-api-key'
62
59
  ENABLE_BUGSNAG = 'enable-bugsnag'
60
+ ENABLE_RETRIES = 'enable-retries'
63
61
  end
64
62
  end
@@ -88,7 +88,7 @@ module Maze
88
88
 
89
89
  begin
90
90
  http = Net::HTTP.new(uri.hostname, uri.port)
91
- response = http.request(request)
91
+ http.request(request)
92
92
  rescue => e
93
93
  $logger.warn 'Report delivery attempt failed'
94
94
  $logger.warn e.message
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maze
4
+ module Plugins
5
+ class ErrorCodePlugin < Cucumber::Core::Filter.new(:configuration)
6
+
7
+ def test_case(test_case)
8
+ configuration.on_event(:test_case_finished) do |event|
9
+
10
+ # Ensure we're in the correct test case, and the test failed
11
+ next unless event.test_case == test_case
12
+
13
+ error = event.result.failed? ? event.result.exception.class : nil
14
+ Maze::Hooks::ErrorCodeHook.last_test_error_class = error
15
+ end
16
+
17
+ super
18
+ end
19
+ end
20
+ end
21
+ end
@@ -10,12 +10,8 @@ module Maze
10
10
  @count = 0
11
11
  end
12
12
 
13
- def empty?
14
- @requests.empty?
15
- end
16
-
17
13
  # The number of unprocessed/remaining requests in the list (not the total number actually held)
18
- def size
14
+ def size_remaining
19
15
  @count
20
16
  end
21
17
 
@@ -78,5 +74,14 @@ module Maze
78
74
  sub_list.sort_by! { |r| DateTime.parse(r[:request][header]) }
79
75
  sub_list.each_with_index { |r, i| @requests[@current + i] = r }
80
76
  end
77
+
78
+ # Sorts the remaining elements of the list by the field given, if present in all of those elements
79
+ def sort_by!(key_path)
80
+ list = remaining
81
+
82
+ # Sort the list and overwrite in the main list
83
+ list.sort_by! { |r| Maze::Helper.read_key_path(r[:body], key_path) }
84
+ list.each_with_index { |r, i| @requests[@current + i] = r }
85
+ end
81
86
  end
82
87
  end
@@ -0,0 +1,49 @@
1
+ module Maze
2
+ # Repeats POST requests
3
+ module RequestRepeater
4
+
5
+ def do_POST(request, response)
6
+ repeat(request) if enabled?
7
+
8
+ super(request, response)
9
+ end
10
+
11
+ private
12
+
13
+ def enabled?
14
+ # enabled if the config option is on and this request type should be repeated
15
+ Maze.config.repeater_api_key && url_for_request_type
16
+ end
17
+
18
+ # @param request [HTTPRequest] The request to be repeated
19
+ def repeat(request)
20
+
21
+ # TODO Forwarding of internal errors to be considered later
22
+ return if request.header.keys.any? { |key| key.downcase == 'bugsnag-internal-error' }
23
+
24
+ url = url_for_request_type
25
+ http = Net::HTTP.new(url.host)
26
+ bugsnag_request = Net::HTTP::Post.new(url.path)
27
+
28
+ # Set all headers that are present
29
+ bugsnag_request.body = request.body
30
+ request.header.each {|key,value| bugsnag_request[key] = value }
31
+ bugsnag_request['bugsnag-api-key'] = Maze.config.repeater_api_key
32
+
33
+ # TODO Also overwrite apiKey in the payload, if present, recalculate the integrity header (handling
34
+ # compressed payloads if the content-encoding header is set accordingly)
35
+
36
+ http.request(bugsnag_request)
37
+ end
38
+
39
+ def url_for_request_type
40
+ url = case @request_type
41
+ when :errors then 'https://notify.bugsnag.com/'
42
+ when :sessions then 'https://sessions.bugsnag.com/'
43
+ when :traces then 'https://otlp.bugsnag.com/v1/traces'
44
+ else return nil
45
+ end
46
+ URI.parse(url)
47
+ end
48
+ end
49
+ end
@@ -8,17 +8,6 @@ module Maze
8
8
  class RetryHandler
9
9
  class << self
10
10
 
11
- # Errors which indicate a selenium/appium driver has crashed and needs to be restarted
12
- DRIVER_ERRORS = [
13
- Maze::Error::AppiumElementNotFoundError,
14
-
15
- Selenium::WebDriver::Error::NoSuchElementError,
16
- Selenium::WebDriver::Error::StaleElementReferenceError,
17
- Selenium::WebDriver::Error::TimeoutError,
18
- Selenium::WebDriver::Error::UnknownError,
19
- Selenium::WebDriver::Error::WebDriverError
20
- ].freeze
21
-
22
11
  # Acceptable tags to indicate a test should be restarted
23
12
  RETRY_TAGS = %w[@retry @retryable @retriable].freeze
24
13
 
@@ -32,7 +21,7 @@ module Maze
32
21
 
33
22
  if retry_on_driver_error?(event)
34
23
  $logger.warn "Retrying #{test_case.name} due to driver error: #{event.result.exception}"
35
- if Maze.driver.is_a?(Maze::Driver::Appium) || Maze.driver.is_a?(Maze::Driver::ResilientAppium)
24
+ if Maze.driver.is_a?(Maze::Driver::Appium)
36
25
  Maze.driver.restart
37
26
  elsif Maze.driver.is_a?(Maze::Driver::Browser)
38
27
  Maze.driver.refresh
@@ -59,7 +48,9 @@ module Maze
59
48
  end
60
49
 
61
50
  def retry_on_driver_error?(event)
62
- Maze.driver && DRIVER_ERRORS.include?(event.result.exception.class)
51
+ error_class = event.result.exception.class
52
+ maze_errors = Maze::Error::ERROR_CODES
53
+ Maze.driver && maze_errors.include?(error_class) && maze_errors[error_class][:retry]
63
54
  end
64
55
 
65
56
  def retry_on_tag?(test_case)