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,173 @@
1
+ require 'pty'
2
+ # TODO: Removed pending PLAT-6322
3
+ # require 'boring'
4
+
5
+ module Maze
6
+ # Encapsulates a shell session, retaining state and input streams for interactive tests
7
+ class InteractiveCLI
8
+ # @!attribute [r] stdout_lines
9
+ # @return [Array] An array of output strings received from the terminals STDOUT pipe
10
+ attr_reader :stdout_lines
11
+
12
+ # @!attribute [r] stderr_lines
13
+ # @return [Array] An array of error strings received from the terminals STDERR pipe
14
+ attr_reader :stderr_lines
15
+
16
+ # @!attribute [r] pid
17
+ # @return [Number, nil] The PID of the running terminal
18
+ attr_reader :pid
19
+
20
+ # @!attribute [r] current_buffer
21
+ # @return [String] A string representation of the current output present in the terminal
22
+ attr_reader :current_buffer
23
+
24
+ # Creates an InteractiveCLI instance
25
+ #
26
+ # @param shell [String] A path to the shell to run, defaults to `/bin/sh`
27
+ # @param stop_command [String] The stop command, defaults to `exit`
28
+ def initialize(shell = '/bin/sh', stop_command = 'exit')
29
+ @shell = shell
30
+ @stop_command = stop_command
31
+ @stdout_lines = []
32
+ @stderr_lines = []
33
+ @on_exit_blocks = []
34
+ @current_buffer = ''
35
+ # TODO: Removed pending PLAT-6322
36
+ # @boring = Boring.new
37
+
38
+ start_threaded_shell(shell)
39
+ end
40
+
41
+ def start(threaded: true)
42
+ threaded ? start_threaded_shell(@shell) : start_shell(@shell)
43
+ end
44
+
45
+ # Attempts to stop the shell using the preset command and wait for it to exit
46
+ #
47
+ # @return [Boolean] If the shell stopped successfully
48
+ def stop
49
+ run_command(@stop_command)
50
+
51
+ @in_stream.close
52
+
53
+ maybe_thread = @thread.join(15)
54
+
55
+ # The thread did not exit!
56
+ return false if maybe_thread.nil?
57
+
58
+ @pid = nil
59
+ true
60
+ end
61
+
62
+ # @return [Boolean] Whether the shell is currently running
63
+ def running?
64
+ !@pid.nil?
65
+ end
66
+
67
+ # Runs the given command if the shell is running
68
+ #
69
+ # @param command [String] The command to run
70
+ #
71
+ # @return [Boolean] true if the command is executed, false otherwise
72
+ def run_command(command)
73
+ return false unless running?
74
+
75
+ @in_stream.puts(command)
76
+
77
+ true
78
+ rescue ::Errno::EIO => err
79
+ $logger.debug(pid) { "EIO error: #{err}" }
80
+ false
81
+ end
82
+
83
+ def on_exit(&block)
84
+ @on_exit_blocks << block
85
+ end
86
+
87
+ private
88
+
89
+ # Starts a shell on another thread
90
+ #
91
+ # @param shell [String] A path to the shell to run
92
+ def start_threaded_shell(shell)
93
+ @thread = Thread.new do
94
+ start_shell(shell)
95
+ end
96
+ end
97
+
98
+ # Starts a shell
99
+ #
100
+ # @param shell [String] A path to the shell to run
101
+ def start_shell(shell)
102
+ stderr_reader, stderr_writer = IO.pipe
103
+
104
+ PTY.spawn(shell, err: stderr_writer.fileno) do |stdout, stdin, pid|
105
+ # We don't need to write to stderr so close it ASAP
106
+ stderr_writer.close
107
+
108
+ $logger.debug(pid) { 'PTY spawned!' }
109
+ @pid = pid
110
+ @in_stream = stdin
111
+
112
+ stdout_thread = Thread.new do
113
+ stdout.each_char do |char|
114
+ if char == "\n"
115
+ line = format_line(@current_buffer)
116
+
117
+ $logger.debug("#{pid} STDOUT") { line.dump }
118
+ @stdout_lines << line
119
+ @current_buffer.clear
120
+ else
121
+ @current_buffer << char
122
+ end
123
+ end
124
+ rescue ::Errno::EIO => err
125
+ $logger.debug(pid) { "EIO error: #{err}" }
126
+ end
127
+
128
+ stderr_thread = Thread.new do
129
+ buffer = ''
130
+
131
+ stderr_reader.each_char do |char|
132
+ if char == "\n"
133
+ line = format_line(buffer)
134
+
135
+ $logger.debug("#{pid} STDERR") { line.dump }
136
+ @stderr_lines << line
137
+ buffer.clear
138
+ else
139
+ buffer << char
140
+ end
141
+ end
142
+ rescue ::Errno::EIO => err
143
+ $logger.debug(pid) { "EIO error: #{err}" }
144
+ end
145
+
146
+ _, status = Process.wait2(@pid)
147
+ @pid = nil
148
+
149
+ # Stop the thread that's reading from stdout
150
+ failed = stdout_thread.join(5).nil?
151
+ raise 'stdout is blocked!' if failed
152
+
153
+ # Stop the thread that's reading from stderr
154
+ failed = stderr_thread.join(5).nil?
155
+ raise 'stderr is blocked!' if failed
156
+
157
+ $logger.debug(pid) { "PTY exit status: #{status.exitstatus}" }
158
+ @on_exit_blocks.each do |block|
159
+ block.call(status.exitstatus)
160
+ end
161
+ end
162
+ ensure
163
+ stderr_reader.close unless stderr_reader.closed?
164
+ stderr_writer.close unless stderr_writer.closed?
165
+ end
166
+
167
+ def format_line(line)
168
+ # TODO: Removed pending PLAT-6322
169
+ # @boring.scrub(line.strip)
170
+ line.strip
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+ require 'singleton'
5
+
6
+ # Logger classes
7
+ module Maze
8
+ # A logger, with level configured according to the environment
9
+ class Logger < Logger
10
+
11
+ include Singleton
12
+ def initialize
13
+ if ENV['VERBOSE'] || ENV['DEBUG']
14
+ super(STDOUT, level: Logger::DEBUG)
15
+ elsif ENV['QUIET']
16
+ super(STDOUT, level: Logger::ERROR)
17
+ else
18
+ super(STDOUT, level: Logger::INFO)
19
+ end
20
+ self.datetime_format = '%Y-%m-%d %H:%M:%S'
21
+ end
22
+ end
23
+
24
+ $logger = Maze::Logger.instance
25
+
26
+ # A collection of logging utilities
27
+ class LogUtil
28
+ class << self
29
+ # Logs Hash-based data, accounting for things like file upload requests that are too big to log meaningfully.
30
+ #
31
+ # @param severity [Integer] A constant from Logger::Severity
32
+ # @param data [Hash] The data to log (currently needs to be a Hash)
33
+ def log_hash(severity, data)
34
+ return unless data.is_a? Hash
35
+
36
+ # Try to pretty print as JSON, if not too big
37
+ begin
38
+ json = JSON.pretty_generate data
39
+ if json.length < 128 * 1024
40
+ $logger.add severity, json
41
+ else
42
+ log_hash_by_field severity, data
43
+ end
44
+ rescue Encoding::UndefinedConversionError
45
+ log_hash_by_field severity, data
46
+ end
47
+ end
48
+
49
+ # Logs a hash field by field,
50
+ #
51
+ # @param severity [Integer] A Logger::Severity
52
+ # @param hash [Hash] The Hash
53
+ def log_hash_by_field(severity, hash)
54
+ hash.keys.each do |key|
55
+ value = hash[key].to_s
56
+ if value.length < 1024
57
+ $logger.add severity, " #{key}: #{value}"
58
+ else
59
+ $logger.add severity, " #{key} (length): #{value.length}"
60
+ $logger.add severity, " #{key} (start): #{value[0, 1024]}"
61
+ end
62
+ end
63
+ end
64
+
65
+ # Produces a clickable link when logged in Buildkite
66
+ # @param url [String] Link URL
67
+ # @param text [String] Link text
68
+ def linkify(url, text)
69
+ "\033]1339;url='#{url}';content='#{text}'\a"
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maze
4
+ class MacosUtils
5
+ class << self
6
+ def capture_screen(scenario)
7
+ path = File.join(File.join(Dir.pwd, 'maze_output'), 'failed', Maze::Helper.to_friendly_filename(scenario.name))
8
+ FileUtils.makedirs(path)
9
+
10
+ system("/usr/sbin/screencapture #{path}/#{Maze::Helper.to_friendly_filename(scenario.name)}-screenshot.jpg")
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'socket'
4
+
5
+ module Maze
6
+ # Sets the maximum number of times Maze runner will ping to see if a port is open, defaulting to 100
7
+ MAX_MAZE_CONNECT_ATTEMPTS = ENV.fetch('MAX_MAZE_CONNECT_ATTEMPTS', 100).to_i
8
+
9
+ # Provides network utility functionality
10
+ class Network
11
+ class << self
12
+ # Repeatedly pings a port to see if the host is ready for a connection.
13
+ # The maximum amount of attempts is determined by the MAX_MAZE_CONNECT_ATTEMPTS variable.
14
+ #
15
+ # @param host [String] The name of the host to connect to
16
+ # @param port [String] The port to attempt to connect to
17
+ #
18
+ # @raise [StandardError] When the port is not available for a connection
19
+ def wait_for_port(host, port)
20
+ attempts = 0
21
+ up = false
22
+ until (attempts >= MAX_MAZE_CONNECT_ATTEMPTS) || up
23
+ attempts += 1
24
+ up = port_open?(host, port)
25
+ sleep 0.1 unless up
26
+ end
27
+ raise "Port not ready in time!" unless up
28
+ end
29
+
30
+ # Attempts to connect to a port, timing out after a time.
31
+ #
32
+ # @param host [String] The name of the host to connect to
33
+ # @param port [String] The port to attempt to connect to
34
+ # @param seconds [Float] Optional. The length of time to wait before timing out.
35
+ def port_open?(host, port, seconds=0.1)
36
+ Timeout::timeout(seconds) do
37
+ begin
38
+ TCPSocket.new(host, port).close
39
+ true
40
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, SocketError
41
+ false
42
+ end
43
+ end
44
+ rescue Timeout::Error
45
+ false
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,245 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cucumber/cli/main'
4
+ require 'optimist'
5
+ require_relative '../option'
6
+ require_relative '../../maze'
7
+
8
+ module Maze
9
+ module Option
10
+ # Parses the command line options
11
+ class Parser
12
+ class << self
13
+ def parse(args)
14
+ parser = Optimist::Parser.new do
15
+ text 'Maze Runner extends the functionality of Cucumber, ' \
16
+ 'providing all of the command line arguments that it provides.'
17
+ text ''
18
+ text 'Usage [OPTIONS] <filenames>'
19
+ text ''
20
+ text 'Overridden Cucumber options:'
21
+ opt :help,
22
+ 'Print this help.'
23
+ opt :version,
24
+ 'Display Maze Runner and Cucumber versions'
25
+
26
+ text ''
27
+ text 'General options:'
28
+
29
+ opt Option::ENABLE_RETRIES,
30
+ 'Enables retrying failed scenarios when tagged',
31
+ type: :boolean,
32
+ default: true
33
+
34
+ opt Option::ENABLE_BUGSNAG,
35
+ 'Enables reporting to Bugsnag on scenario failure (Require MAZE_BUGSNAG_API_KEY)',
36
+ type: :boolean,
37
+ default: true
38
+
39
+ text ''
40
+ text 'Server options:'
41
+
42
+ opt Option::BIND_ADDRESS,
43
+ 'Mock server bind address',
44
+ type: :string
45
+ opt Option::PORT,
46
+ 'Mock server port',
47
+ default: 9339
48
+ opt Option::NULL_PORT,
49
+ 'Terminating connection port',
50
+ default: 9341
51
+
52
+ text ''
53
+ text 'Document server options:'
54
+
55
+ opt Option::DS_ROOT,
56
+ 'Document server root',
57
+ type: :string
58
+ opt Option::DS_BIND_ADDRESS,
59
+ 'Document server bind address',
60
+ type: :string
61
+ opt Option::DS_PORT,
62
+ 'Document server port',
63
+ default: 9340
64
+
65
+ text ''
66
+ text 'Appium options:'
67
+
68
+ opt Option::SEPARATE_SESSIONS,
69
+ 'Start a new Appium session for each scenario',
70
+ type: :boolean,
71
+ default: false
72
+ opt Option::FARM,
73
+ 'Device farm to use: "bs" (BrowserStack) or "local"',
74
+ type: :string
75
+ opt Option::APP,
76
+ 'The app to be installed and run against. Assumed to be contained in a file if prefixed with @.',
77
+ type: :string
78
+ opt Option::A11Y_LOCATOR,
79
+ 'Locate elements by accessibility id rather than id',
80
+ type: :boolean,
81
+ default: false
82
+ opt Option::RESILIENT,
83
+ 'Use the resilient Appium driver',
84
+ default: false
85
+ opt Option::CAPABILITIES,
86
+ 'Additional desired Appium capabilities as a JSON string',
87
+ default: '{}'
88
+
89
+ text ''
90
+ text 'Device farm options:'
91
+
92
+ opt Option::DEVICE,
93
+ 'Device to use. Can be listed multiple times to have a prioritised list of devices',
94
+ short: :none,
95
+ type: :string,
96
+ multi: true
97
+ opt Option::BROWSER,
98
+ 'Browser to use (an entry in browsers_<farm>.yml)',
99
+ short: :none,
100
+ type: :string
101
+ opt Option::USERNAME,
102
+ 'Device farm username. Consumes env var from environment based on farm set',
103
+ type: :string
104
+ opt Option::ACCESS_KEY,
105
+ 'Device farm access key. Consumes env var from environment based on farm set',
106
+ type: :string
107
+ opt Option::APPIUM_VERSION,
108
+ 'The Appium version to use',
109
+ type: :string
110
+ opt Option::LIST_DEVICES,
111
+ 'Lists the devices available for the configured device-farm, or all devices if none are specified',
112
+ default: false
113
+ opt Option::APP_BUNDLE_ID,
114
+ 'The bundle identifier of the test application',
115
+ type: :string
116
+
117
+ # SmartBear-only options
118
+ opt Option::SB_LOCAL,
119
+ '(SB only) Path to the SBSecureTunnel binary. MAZE_SB_LOCAL env var or "/SBSecureTunnel" by default',
120
+ type: :string
121
+
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
+ # BrowserStack-only options
128
+ opt Option::BS_LOCAL,
129
+ '(BS only) Path to the BrowserStackLocal binary. MAZE_BS_LOCAL env var or "/BrowserStackLocal" by default',
130
+ type: :string
131
+
132
+ # TMS options
133
+ opt Option::TMS_URI,
134
+ 'URI of the test management server root. MAZE_TMS_URI env var',
135
+ type: :string
136
+
137
+ opt Option::TMS_TOKEN,
138
+ 'Token used to access the test management server. MAZE_TMS_TOKEN env var',
139
+ type: :string
140
+
141
+ text ''
142
+ text 'Local device options:'
143
+
144
+ opt Option::OS,
145
+ 'OS type to use ("ios", "android")',
146
+ type: :string
147
+ opt Option::OS_VERSION,
148
+ 'The intended OS version when running on a local device',
149
+ type: :string
150
+ opt Option::APPIUM_SERVER,
151
+ 'Appium server URL, only used for --farm=local. MAZE_APPIUM_SERVER env var or "http://localhost:4723/wd/hub" by default',
152
+ type: :string
153
+ opt Option::START_APPIUM,
154
+ 'Whether a local Appium server should be start. Only used for --farm=local.',
155
+ default: true
156
+ opt Option::APPIUM_LOGFILE,
157
+ 'The file local appium server output is logged to, defaulting to "appium_server.log"',
158
+ default: 'appium_server.log'
159
+ opt Option::APPLE_TEAM_ID,
160
+ 'Apple Team Id, required for local iOS testing. MAZE_APPLE_TEAM_ID env var by default',
161
+ type: :string
162
+ opt Option::UDID,
163
+ 'Apple UDID, required for local iOS testing. MAZE_UDID env var by default',
164
+ type: :string
165
+
166
+ text ''
167
+ text 'Logging options:'
168
+
169
+ opt Option::FILE_LOG,
170
+ "Writes lists of received requests to the maze_output folder for all scenarios",
171
+ type: :boolean,
172
+ default: true
173
+
174
+ opt Option::LOG_REQUESTS,
175
+ "Log lists of received requests to the console in the event of scenario failure. Defaults to true if the BUILDKITE environment variable is set",
176
+ type: :boolean,
177
+ default: false
178
+
179
+ opt Option::ALWAYS_LOG,
180
+ "Always log all received requests at the end of a scenario, whether is passes or fails",
181
+ type: :boolean,
182
+ default: false
183
+
184
+ version "Maze Runner v#{Maze::VERSION} " \
185
+ "(Cucumber v#{Cucumber::VERSION.strip})"
186
+ text ''
187
+ text 'The Cucumber help follows:'
188
+ text ''
189
+ end
190
+
191
+ # Allow for options destined for Cucumber
192
+ parser.ignore_invalid_options = true
193
+ options = parser.parse args
194
+ populate_environmental_defaults(options)
195
+
196
+ rescue Optimist::HelpNeeded
197
+ parser.educate
198
+ Cucumber::Cli::Main.new(['--help']).execute!
199
+ exit
200
+ rescue Optimist::VersionNeeded
201
+ puts parser.version
202
+ exit
203
+ end
204
+
205
+ # Populates unset options with appropriate environment variables or default values if necessary
206
+ #
207
+ # @param options [Hash] The hash of already-parsed options
208
+ #
209
+ # @returns [Hash] The options hash with environment vars added
210
+ def populate_environmental_defaults(options)
211
+ case options.farm
212
+ when 'cbt'
213
+ options[Option::USERNAME] ||= ENV['CBT_USERNAME']
214
+ options[Option::ACCESS_KEY] ||= ENV['CBT_ACCESS_KEY']
215
+ when 'bs'
216
+ # Allow browser/device credentials to exist in separate accounts
217
+ if options[Option::BROWSER]
218
+ options[Option::USERNAME] ||= ENV['BROWSER_STACK_BROWSERS_USERNAME'] || ENV['BROWSER_STACK_USERNAME']
219
+ options[Option::ACCESS_KEY] ||= ENV['BROWSER_STACK_BROWSERS_ACCESS_KEY'] ||ENV['BROWSER_STACK_ACCESS_KEY']
220
+ else
221
+ options[Option::USERNAME] ||= ENV['BROWSER_STACK_DEVICES_USERNAME'] || ENV['BROWSER_STACK_USERNAME']
222
+ options[Option::ACCESS_KEY] ||= ENV['BROWSER_STACK_DEVICES_ACCESS_KEY'] ||ENV['BROWSER_STACK_ACCESS_KEY']
223
+ end
224
+ when 'sl'
225
+ options[Option::USERNAME] ||= ENV['SAUCE_LABS_USERNAME']
226
+ options[Option::ACCESS_KEY] ||= ENV['SAUCE_LABS_ACCESS_KEY']
227
+ when 'bb'
228
+ options[Option::USERNAME] ||= ENV['BITBAR_USERNAME']
229
+ options[Option::ACCESS_KEY] ||= ENV['BITBAR_ACCESS_KEY']
230
+ options[Option::TMS_URI] ||= ENV['MAZE_TMS_URI']
231
+ end
232
+ options[Option::SB_LOCAL] ||= ENV['MAZE_SB_LOCAL'] || '/SBSecureTunnel'
233
+ options[Option::TMS_URI] ||= ENV['MAZE_TMS_URI']
234
+ options[Option::TMS_TOKEN] ||= ENV['MAZE_TMS_TOKEN']
235
+ options[Option::BS_LOCAL] ||= ENV['MAZE_BS_LOCAL'] || '/BrowserStackLocal'
236
+ options[Option::SL_LOCAL] ||= ENV['MAZE_SL_LOCAL'] || '/sauce-connect/bin/sc'
237
+ options[Option::APPIUM_SERVER] ||= ENV['MAZE_APPIUM_SERVER'] || 'http://localhost:4723/wd/hub'
238
+ options[Option::APPLE_TEAM_ID] ||= ENV['MAZE_APPLE_TEAM_ID']
239
+ options[Option::UDID] ||= ENV['MAZE_UDID']
240
+ options
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,143 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../option'
4
+ require_relative '../browser_stack_devices'
5
+
6
+ module Maze
7
+ module Option
8
+ # Processes the parsed command line options
9
+ class Processor
10
+ class << self
11
+ # Populates config from the parsed options given
12
+ # @param config [Configuration] MazeRunner configuration to populate
13
+ # @param options [Hash] Parsed command line options
14
+ def populate(config, options)
15
+
16
+ # Server options
17
+ config.bind_address = options[Maze::Option::BIND_ADDRESS]
18
+ config.port = options[Maze::Option::PORT]
19
+ config.null_port = options[Maze::Option::NULL_PORT]
20
+
21
+ # General options
22
+ config.enable_retries = options[Maze::Option::ENABLE_RETRIES]
23
+ config.enable_bugsnag = options[Maze::Option::ENABLE_BUGSNAG]
24
+ config.tms_uri = options[Maze::Option::TMS_URI]
25
+ config.tms_token = options[Maze::Option::TMS_TOKEN]
26
+
27
+ # Document server options
28
+ config.document_server_root = options[Maze::Option::DS_ROOT]
29
+ config.document_server_bind_address = options[Maze::Option::DS_BIND_ADDRESS]
30
+ config.document_server_port = options[Maze::Option::DS_PORT]
31
+
32
+ # Logger options
33
+ config.file_log = options[Maze::Option::FILE_LOG]
34
+ config.log_requests = options[Maze::Option::LOG_REQUESTS] || !ENV['BUILDKITE'].nil?
35
+ config.always_log = options[Maze::Option::ALWAYS_LOG]
36
+
37
+ # General appium options
38
+ config.appium_session_isolation = options[Maze::Option::SEPARATE_SESSIONS]
39
+ config.app = Maze::Helper.read_at_arg_file options[Maze::Option::APP]
40
+ config.resilient = options[Maze::Option::RESILIENT]
41
+ farm = options[Maze::Option::FARM]
42
+ config.farm = case farm
43
+ when nil then :none
44
+ when 'cbt' then :cbt
45
+ when 'bs' then :bs
46
+ when 'sl' then :sl
47
+ when 'bb' then :bb
48
+ when 'local' then :local
49
+ else
50
+ raise "Unknown farm '#{farm}'"
51
+ end
52
+ config.locator = options[Maze::Option::A11Y_LOCATOR] ? :accessibility_id : :id
53
+ config.capabilities_option = options[Maze::Option::CAPABILITIES]
54
+
55
+ # Farm specific options
56
+ 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
+ when :bs
63
+ device_option = options[Maze::Option::DEVICE]
64
+ if device_option.nil? || device_option.empty?
65
+ config.browser = options[Maze::Option::BROWSER]
66
+ else
67
+ if device_option.is_a?(Array)
68
+ config.device = device_option.first
69
+ config.device_list = device_option.drop(1)
70
+ else
71
+ config.device = device_option
72
+ config.device_list = []
73
+ end
74
+ config.os_version = Maze::BrowserStackDevices::DEVICE_HASH[config.device]['os_version'].to_f
75
+ end
76
+ config.bs_local = Maze::Helper.expand_path(options[Maze::Option::BS_LOCAL])
77
+ config.appium_version = options[Maze::Option::APPIUM_VERSION]
78
+ username = config.username = options[Maze::Option::USERNAME]
79
+ access_key = config.access_key = options[Maze::Option::ACCESS_KEY]
80
+ 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
+ when :bb then
99
+ config.username = options[Maze::Option::USERNAME]
100
+ config.access_key = options[Maze::Option::ACCESS_KEY]
101
+ config.tms_uri = options[Maze::Option::TMS_URI]
102
+ 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)
106
+ else
107
+ config.device = device_option
108
+ config.device_list = []
109
+ end
110
+ config.os = options[Maze::Option::OS]
111
+ config.os_version = options[Maze::Option::OS_VERSION]
112
+ config.sb_local = Maze::Helper.expand_path(options[Maze::Option::SB_LOCAL])
113
+ config.appium_server_url = 'https://appium.bitbar.com/wd/hub'
114
+ config.app_bundle_id = options[Maze::Option::APP_BUNDLE_ID]
115
+ when :local then
116
+ if options[Maze::Option::BROWSER]
117
+ config.browser = options[Maze::Option::BROWSER]
118
+ else
119
+ os = config.os = options[Maze::Option::OS].downcase
120
+ config.os_version = options[Maze::Option::OS_VERSION].to_f unless options[Maze::Option::OS_VERSION].nil?
121
+ config.appium_server_url = options[Maze::Option::APPIUM_SERVER]
122
+ config.start_appium = options[Maze::Option::START_APPIUM]
123
+ config.appium_logfile = options[Maze::Option::APPIUM_LOGFILE]
124
+ if os == 'ios'
125
+ config.apple_team_id = options[Maze::Option::APPLE_TEAM_ID]
126
+ config.device_id = options[Maze::Option::UDID]
127
+ end
128
+ end
129
+ when :none
130
+ if options[Maze::Option::OS]
131
+ config.os = options[Maze::Option::OS].downcase
132
+ end
133
+ if options[Maze::Option::OS_VERSION]
134
+ config.os_version = options[Maze::Option::OS_VERSION].to_f
135
+ end
136
+ else
137
+ raise "Unexpected farm option #{config.farm}"
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end