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,406 @@
1
+ require 'securerandom'
2
+ require_relative '../../maze/wait'
3
+
4
+ # @!group Runner steps
5
+
6
+ # Sets an environment variable for subsequent scripts or commands.
7
+ #
8
+ # @step_input key [String] The environment variable
9
+ # @step_input value [String] The intended value of the environment variable
10
+ When('I set environment variable {string} to {string}') do |key, value|
11
+ Maze::Runner.environment[key] = value
12
+ end
13
+
14
+ # Sets an environment variable to a given endpoint.
15
+ #
16
+ # @step_input endpoint [String] The endpoint to set
17
+ # @step_input name [String] The environment variable
18
+ When('I store the {word} endpoint in the environment variable {string}') do |endpoint, name|
19
+ steps %(
20
+ When I set environment variable "#{name}" to "http://maze-runner:#{Maze.config.port}/#{endpoint}"
21
+ )
22
+ end
23
+
24
+ # Sets an environment variable to the currently set API key.
25
+ #
26
+ # @step_input name [String] The environment variable
27
+ When('I store the api key in the environment variable {string}') do |name|
28
+ steps %Q{
29
+ When I set environment variable "#{name}" to "#{$api_key}"
30
+ }
31
+ end
32
+
33
+ # Runs a script, blocking until it returns.
34
+ #
35
+ # @step_input script_path [String] Path to the script to be run
36
+ When('I run the script {string} synchronously') do |script_path|
37
+ Maze::Runner.run_script(script_path, blocking: true)
38
+ end
39
+
40
+ # Runs a script with a given interpreter, blocking until it returns.
41
+ #
42
+ # @step_input script_path [String] Path to the script to be run
43
+ # @step_input command [String] The command to run the script with, e.g. 'ruby'
44
+ When('I run the script {string} using {word} synchronously') do |script_path, command|
45
+ Maze::Runner.run_script(script_path, blocking: true, command: command)
46
+ end
47
+
48
+ # Runs a script.
49
+ #
50
+ # @step_input script_path [String] Path to the script to be run
51
+ When('I run the script {string}') do |script_path|
52
+ Maze::Runner.run_script script_path
53
+ end
54
+
55
+ # Starts a docker-compose service.
56
+ #
57
+ # @step_input service [String] The name of the service to run
58
+ When('I start the service {string}') do |service|
59
+ Maze::Docker.start_service service
60
+ end
61
+
62
+ # Runs a docker-compose service using a specific command.
63
+ #
64
+ # @step_input service [String] The name of the service to run
65
+ # @step_input command [String] The command to run inside the service
66
+ When('I run the service {string} with the command {string}') do |service, command|
67
+ Maze::Docker.start_service(service, command: command)
68
+ end
69
+
70
+ # Runs a docker-compose service in an interactive CLI.
71
+ #
72
+ # @step_input service [String] The name of the service to run
73
+ When('I run the service {string} interactively') do |service|
74
+ # Stop the old session if one exists
75
+ step('I stop the current shell') if Maze::Runner.interactive_session?
76
+
77
+ Maze::Docker.start_service(service, interactive: true)
78
+ end
79
+
80
+ # Runs a docker-compose service using a specific command in an interactive CLI.
81
+ #
82
+ # @step_input service [String] The name of the service to run
83
+ # @step_input command [String] The command to run inside the service
84
+ When('I run the service {string} with the command {string} interactively') do |service, command|
85
+ # Stop the old session if one exists
86
+ step('I stop the current shell') if Maze::Runner.interactive_session?
87
+
88
+ Maze::Docker.start_service(service, command: command, interactive: true)
89
+ end
90
+
91
+ # Runs a docker-compose service using a specific command provided as a Gherkin multi-line string.
92
+ #
93
+ # @step_input service [String] The name of the service to run
94
+ # @step_input command [String] The command to run inside the service (as a Gherkin multi-line string)
95
+ When('I run the service {string} with the command') do |service, command|
96
+ one_line_cmd = command.gsub("\n", ' ').gsub(/ +/, ' ')
97
+ Maze::Docker.start_service(service, command: one_line_cmd)
98
+ end
99
+
100
+ # Allows validation of the last exit code of the last run docker-compose command.
101
+ # Will fail if no commands have been run.
102
+ #
103
+ # @step_input expected_code [Integer] The expected exit code
104
+ Then('the exit code of the last docker command was {int}') do |expected_code|
105
+ wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait)
106
+ success = wait.until { !Maze::Docker.last_exit_code.nil? }
107
+
108
+ Maze.check.true(success, 'No docker exit code available to verify')
109
+ Maze.check.equal(Maze::Docker.last_exit_code, expected_code)
110
+ end
111
+
112
+ # A shortcut for the above assuming 0 as a successful exit code
113
+ # Will fail if no commands have been run
114
+ Then('the last run docker command exited successfully') do
115
+ step('the exit code of the last docker command was 0')
116
+ end
117
+
118
+ # Allows testing that the last exit code was not 0
119
+ # Will fail if no commands have been run
120
+ Then('the last run docker command did not exit successfully') do
121
+ wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait)
122
+ success = wait.until { !Maze::Docker.last_exit_code.nil? }
123
+
124
+ Maze.check.true(success, 'No docker exit code available to verify')
125
+ Maze.check.not_equal(Maze::Docker.last_exit_code, 0)
126
+ end
127
+
128
+ # Allows testing a docker command output a specific string
129
+ # Will fail if no commands have been run
130
+ #
131
+ # @step_input expected_string [String] The string expected in a single log line
132
+ Then('the last run docker command output {string}') do |expected_string|
133
+ wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait)
134
+ success = wait.until { !Maze::Docker.last_command_logs.nil? }
135
+
136
+ Maze.check.true(success, 'No docker logs available to verify')
137
+
138
+ docker_output = Maze::Docker.last_command_logs
139
+ output_included = docker_output.any? { |line| line.include?(expected_string) }
140
+
141
+ Maze.check.true(output_included, %(
142
+ No line of output included '#{expected_string}'.
143
+ Full output:
144
+ #{docker_output.join('\n')}
145
+ ))
146
+ end
147
+
148
+ # Waits for a number of seconds, performing no actions.
149
+ #
150
+ # @step_input seconds [Integer] The number of seconds to sleep for
151
+ When('I wait for {int} second(s)') do |seconds|
152
+ $logger.warn 'Sleep was used! Please avoid using sleep in tests!'
153
+ sleep(seconds)
154
+ end
155
+
156
+ # Starts an interactive shell
157
+ When('I start a new shell') do
158
+ # Stop the old session if one exists
159
+ step('I stop the current shell') if Maze::Runner.interactive_session?
160
+
161
+ Maze::Runner.start_interactive_session
162
+ end
163
+
164
+ # Stops currently running interactive shell
165
+ When('I stop the current shell') do
166
+ shell = Maze::Runner.interactive_session
167
+ result = Maze::Runner.stop_interactive_session
168
+
169
+ Maze.check.true(result, 'The shell is still running when it should have exited')
170
+ Maze.check.false(shell.running?, 'The shell is still running when it should have exited')
171
+ end
172
+
173
+ # Run a command on the shell
174
+ #
175
+ # @step_input command [String] The command to run on the shell
176
+ When('I input {string} interactively') do |command|
177
+ current_shell = Maze::Runner.interactive_session
178
+ success = current_shell.run_command(command)
179
+ Maze.check.true(success, 'The terminal had already closed')
180
+ end
181
+
182
+ # Send a return or enter to the interactive session
183
+ When('I input a return interactively') do
184
+ step('I input "" interactively')
185
+ end
186
+
187
+ # Assert the current stdout line in the shell exactly matches the given string
188
+ #
189
+ # @step_input expected [String] The expected string
190
+ Then('the current stdout line is {string}') do |expected|
191
+ current_shell = Maze::Runner.interactive_session
192
+ Maze.check.equal(expected, current_shell.current_buffer)
193
+ end
194
+
195
+ # Assert the current stdout line in the shell includes the given string
196
+ #
197
+ # @step_input expected [String] The expected string
198
+ Then('the current stdout line contains {string}') do |expected|
199
+ current_shell = Maze::Runner.interactive_session
200
+ Maze.check.include(current_shell.current_buffer, expected)
201
+ end
202
+
203
+ # Waits for a line matching a regex to be present in the current stdout
204
+ # Times out after Maze.config.receive_requests_wait seconds.
205
+ #
206
+ # @step_input regex [String] The regex to match against
207
+ Then('I wait for the current stdout line to match the regex {string}') do |regex|
208
+ wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait)
209
+ shell = Maze::Runner.interactive_session
210
+
211
+ success = wait.until { shell.current_buffer.match?(regex) }
212
+
213
+ Maze.check.true(success, "The current output line \"#{shell.current_buffer}\" did not match \"#{regex}\"")
214
+ end
215
+
216
+ # Waits for a specific shell prompt to be present in the buffered stdout line,
217
+ # timing out after Maze.config.receive_requests_wait seconds.
218
+ #
219
+ # @step_input expected_prompt [String] The prompt expected in the current buffer
220
+ Then('I wait for the shell prompt {string}') do |expected_prompt|
221
+ wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait)
222
+ shell = Maze::Runner.interactive_session
223
+
224
+ success = wait.until { shell.current_buffer == expected_prompt }
225
+
226
+ Maze.check.true(success, "The current output line \"#{shell.current_buffer}\" did not match \"#{expected_prompt}\"")
227
+ end
228
+
229
+ # Verify a string appears in the stdout logs
230
+ #
231
+ # @step_input expected_line [String] The string present in stdout logs
232
+ Then('the shell has output {string} to stdout') do |expected_line|
233
+ current_shell = Maze::Runner.interactive_session
234
+ match = current_shell.stdout_lines.any? { |line| line == expected_line }
235
+ Maze.check.true(match, "No output lines from #{current_shell.stdout_lines} matched #{expected_line}")
236
+ end
237
+
238
+ # Wait for a string to appear in the stdout logs, timing out after Maze.config.receive_requests_wait seconds.
239
+ #
240
+ # @step_input expected_line [String] The string present in stdout logs
241
+ Then('I wait for the shell to output {string} to stdout') do |expected_line|
242
+ wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait)
243
+ current_shell = Maze::Runner.interactive_session
244
+
245
+ success = wait.until do
246
+ current_shell.stdout_lines.any? { |line| line == expected_line }
247
+ end
248
+
249
+ Maze.check.true(success, "No output lines from #{current_shell.stdout_lines} matched #{expected_line}")
250
+ end
251
+
252
+ # Verify a string using a regex in the stdout logs
253
+ #
254
+ # @step_input regex_matcher [String] The regex expected to match a line in stdout logs
255
+ Then('the shell has output a match for the regex {string} to stdout') do |regex_matcher|
256
+ current_shell = Maze::Runner.interactive_session
257
+ match = current_shell.stdout_lines.any? { |line| line.match?(regex_matcher) }
258
+ Maze.check.true(match, "No output lines from #{current_shell.stdout_lines} matched #{regex_matcher}")
259
+ end
260
+
261
+ # Wait for a string matching a regex in the stdout logs, timing out after Maze.config.receive_requests_wait seconds.
262
+ #
263
+ # @step_input regex_matcher [String] The regex expected to match a line in stdout logs
264
+ Then('I wait for the shell to output a match for the regex {string} to stdout') do |regex_matcher|
265
+ wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait)
266
+ current_shell = Maze::Runner.interactive_session
267
+
268
+ success = wait.until do
269
+ current_shell.stdout_lines.any? { |line| line.match?(regex_matcher) }
270
+ end
271
+
272
+ Maze.check.true(success, "No output lines from #{current_shell.stdout_lines} matched #{regex_matcher}")
273
+ end
274
+
275
+ # Wait for the shell to output a number of strings in STDOUT, as defined by a table.
276
+ # This step will time out after Maze.config.receive_requests_wait seconds.
277
+ #
278
+ # @step_input expected_lines [Array] An array of strings expected in STDOUT
279
+ Then('I wait for the interactive shell to output the following lines in stdout') do |expected_lines|
280
+ wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait)
281
+ current_shell = Maze::Runner.interactive_session
282
+
283
+ success = wait.until do
284
+ current_stdout = current_shell.stdout_lines.join("\n")
285
+ current_stdout.include?(expected_lines)
286
+ end
287
+
288
+ Maze.check.true(
289
+ success,
290
+ "Lines present in stdout: #{current_shell.stdout_lines} did not include all of: #{expected_lines}"
291
+ )
292
+ end
293
+
294
+ # Verify a string appears in the stderr logs
295
+ #
296
+ # @step_input expected_err [String] The string present in stderr logs
297
+ Then('the shell has output {string} to stderr') do |expected_err|
298
+ current_shell = Maze::Runner.interactive_session
299
+ match = current_shell.stderr_lines.any? { |line| line == expected_err }
300
+ Maze.check.true(match, "No output lines from #{current_shell.stderr_lines} matched #{expected_err}")
301
+ end
302
+
303
+ # Wait for a string to appear in the stderr logs
304
+ #
305
+ # @step_input expected_line [String] The string present in stderr logs
306
+ Then('I wait for the shell to output {string} to stderr') do |expected_line|
307
+ wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait)
308
+ current_shell = Maze::Runner.interactive_session
309
+
310
+ success = wait.until do
311
+ current_shell.stderr_lines.any? { |line| line == expected_line }
312
+ end
313
+
314
+ Maze.check.true(success, "No output lines from #{current_shell.stderr_lines} matched #{expected_line}")
315
+ end
316
+
317
+ # Verify the last interactive command exited successfully (assuming a 0 is a success)
318
+ Then('the last interactive command exited successfully') do
319
+ Maze.check.true(
320
+ Maze::Runner.interactive_session?,
321
+ 'No interactive session is running so the exit code cannot be checked'
322
+ )
323
+
324
+ uuid = SecureRandom.uuid
325
+
326
+ steps %Q{
327
+ When I input "[ $? = 0 ] && echo '#{uuid} exited with 0' || echo '#{uuid} exited with error'" interactively
328
+ Then I wait for the shell to output a match for the regex "#{uuid} exited with 0" to stdout
329
+ }
330
+ end
331
+
332
+ # Verify the exit code of the last interactive command
333
+ #
334
+ # @step_input exit_code [Integer] The expected exit code
335
+ Then('the last interactive command exit code is {int}') do |exit_code|
336
+ Maze.check.true(
337
+ Maze::Runner.interactive_session?,
338
+ 'No interactive session is running so the exit code cannot be checked'
339
+ )
340
+
341
+ uuid = SecureRandom.uuid
342
+
343
+ steps %Q{
344
+ When I input "echo #{uuid} $?" interactively
345
+ Then I wait for the shell to output a match for the regex "#{uuid} #{exit_code}" to stdout
346
+ }
347
+ end
348
+
349
+ # Assert that the last interactive command exited with an error code (assuming non-0 is an error)
350
+ Then('the last interactive command exited with an error code') do
351
+ Maze.check.true(
352
+ Maze::Runner.interactive_session?,
353
+ 'No interactive session is running so the exit code cannot be checked'
354
+ )
355
+
356
+ uuid = SecureRandom.uuid
357
+
358
+ steps %Q{
359
+ When I input "[ $? = 0 ] && echo '#{uuid} exited with 0' || echo '#{uuid} exited with error'" interactively
360
+ Then I wait for the shell to output a match for the regex "#{uuid} exited with error" to stdout
361
+ }
362
+ end
363
+
364
+ # Assert that an expected_line is present in a file located relative to the interactive terminal's CWD
365
+ #
366
+ # @step_input filename [String] The file tested, relative to the CWD of the interactive terminal
367
+ # @step_input expected_line [String] The line expected in the file
368
+ Then('the interactive file {string} contains {string}') do |filename, expected_line|
369
+ steps %(
370
+ When I input "fgrep '#{expected_line.gsub(/"/, '\"')}' #{filename}" interactively
371
+ And I wait for the current stdout line to match the regex "[#>$]\\s?"
372
+ Then the last interactive command exited successfully
373
+ )
374
+ end
375
+
376
+ # Assert that a line is not present in a file located relative to the interactive terminal's CWD
377
+ #
378
+ # @step_input filename [String] The file tested, relative to the CWD of the interactive terminal
379
+ # @step_input excluded_line [String] The line that should not be present be in the file
380
+ Then('the interactive file {string} does not contain {string}') do |filename, excluded_line|
381
+ steps %(
382
+ When I input "fgrep '#{excluded_line.gsub(/"/, '\"')}' #{filename}" interactively
383
+ And I wait for the current stdout line to match the regex "[#>$]\\s?"
384
+ Then the last interactive command exited with an error code
385
+ )
386
+ end
387
+
388
+ # Assert that a file located relative to the CWD of the interactive terminal contains all of the expected lines
389
+ #
390
+ # @step_input filename [String] The file tested, relative to the CWD of the interactive terminal
391
+ # @step_input expected_lines [String] The lines expected in the file as a multi-line string
392
+ Then('the interactive file {string} contains:') do |filename, expected_lines|
393
+ expected_lines.each_line do |line|
394
+ step("the interactive file '#{filename}' contains '#{line.chomp}'")
395
+ end
396
+ end
397
+
398
+ # Assert that a file located relative to the CWD of the interactive terminal does not contain any of the excluded lines
399
+ #
400
+ # @step_input filename [String] The file tested, relative to the CWD of the interactive terminal
401
+ # @step_input excluded_lines [String] The lines that should not be present in the file, as a multi-line string
402
+ Then('the interactive file {string} does not contain:') do |filename, excluded_lines|
403
+ excluded_lines.each_line do |line|
404
+ step("the interactive file '#{filename}' does not contain '#{line.chomp}'")
405
+ end
406
+ end
@@ -0,0 +1,116 @@
1
+ # @!group Session tracking steps
2
+
3
+ # Verifies that generic elements of a session payload are present.
4
+ # APIKey fields and headers are tested against the '$api_key' global variable.
5
+ #
6
+ # @step_input payload_version [String] The payload version expected
7
+ # @step_input notifier_name [String] The expected name of the notifier
8
+ Then('the session is valid for the session reporting API version {string} for the {string} notifier') \
9
+ do |payload_version, notifier_name|
10
+ steps %(
11
+ Then the session "bugsnag-api-key" header equals "#{$api_key}"
12
+ And the session "bugsnag-payload-version" header equals "#{payload_version}"
13
+ And the session "Content-Type" header equals "application/json"
14
+ And the session "Bugsnag-Sent-At" header is a timestamp
15
+
16
+ And the session payload field "notifier.name" equals "#{notifier_name}"
17
+ And the session payload field "notifier.url" is not null
18
+ And the session payload field "notifier.version" is not null
19
+
20
+ And the session payload field "app" is not null
21
+ And the session payload field "device" is not null
22
+ )
23
+ end
24
+
25
+ # Verifies that generic elements of a session payload are present for the React Native notifier
26
+ # APIKey fields and headers are tested against the '$api_key' global variable.
27
+ #
28
+ # @step_input payload_version [String] The payload version expected
29
+ # @step_input notifier_name [String] The expected name of the notifier
30
+ # TODO: I'm reluctant to risk changing the previous step implementation right now, but we should consider
31
+ # refactoring the two at some point to avoid duplication.
32
+ Then('the session is valid for the session reporting API version {string} for the React Native notifier') do |payload_version|
33
+ steps %{
34
+ Then the session "bugsnag-api-key" header equals "#{$api_key}"
35
+ And the session "bugsnag-payload-version" header equals "#{payload_version}"
36
+ And the session "Content-Type" header equals "application/json"
37
+ And the session "Bugsnag-Sent-At" header is a timestamp
38
+
39
+ And the session payload field "notifier.name" matches the regex "(Android|iOS) Bugsnag Notifier"
40
+ And the session payload field "notifier.url" is not null
41
+ And the session payload field "notifier.version" is not null
42
+
43
+ And the session payload field "app" is not null
44
+ And the session payload field "device" is not null
45
+ }
46
+ end
47
+
48
+ # Tests whether a value in the first session entry matches a literal.
49
+ #
50
+ # @step_input field [String] The relative location of the value to test
51
+ # @step_input literal [Enum] The literal to test against, one of: true, false, null, not null
52
+ Then(/^the session "(.+)" is (true|false|null|not null)$/) do |field, literal|
53
+ step "the session payload field \"sessions.0.#{field}\" is #{literal}"
54
+ end
55
+
56
+ # Tests whether a value in the first session entry matches a string.
57
+ #
58
+ # @step_input field [String] The relative location of the value to test
59
+ # @step_input string_value [String] The string to match against
60
+ Then('the session {string} equals {string}') do |field, string_value|
61
+ step "the session payload field \"sessions.0.#{field}\" equals \"#{string_value}\""
62
+ end
63
+
64
+ # Tests whether a value in the first session entry is a timestamp.
65
+ #
66
+ # @step_input field [String] The relative location of the value to test
67
+ Then('the session {string} is a timestamp') do |field|
68
+ step "the session payload field \"sessions.0.#{field}\" matches the regex \"#{TIMESTAMP_REGEX}\""
69
+ end
70
+
71
+ # Tests whether a value in the first sessionCount entry matches a literal.
72
+ #
73
+ # @step_input field [String] The relative location of the value to test
74
+ # @step_input literal [Enum] The literal to test against, one of: true, false, null, not null
75
+ Then(/^the sessionCount "(.+)" is (true|false|null|not null)$/) do |field, literal|
76
+ step "the session payload field \"sessionCounts.0.#{field}\" is #{literal}"
77
+ end
78
+
79
+ # Tests whether a value in the first sessionCount entry matches a string.
80
+ #
81
+ # @step_input field [String] The relative location of the value to test
82
+ # @step_input string_value [String] The string to match against
83
+ Then('the sessionCount {string} equals {string}') do |field, string_value|
84
+ step "the session payload field \"sessionCounts.0.#{field}\" equals \"#{string_value}\""
85
+ end
86
+
87
+ # Tests whether a value in the first sessionCount entry equals an integer.
88
+ #
89
+ # @step_input field [String] The relative location of the value to test
90
+ # @step_input int_value [Integer] The integer to test against
91
+ Then('the sessionCount {string} equals {int}') do |field, int_value|
92
+ step "the session payload field \"sessionCounts.0.#{field}\" equals #{int_value}"
93
+ end
94
+
95
+ # Tests whether a value in the first sessionCount entry is a timestamp.
96
+ #
97
+ # @step_input field [String] The relative location of the value to test
98
+ Then('the sessionCount {string} is a timestamp') do |field|
99
+ step "the session payload field \"sessionCounts.0.#{field}\" matches the regex \"#{TIMESTAMP_REGEX}\""
100
+ end
101
+
102
+ # Tests that a payload has an appropriately structured session array
103
+ Then('the session payload has a valid sessions array') do
104
+ if sessions = Maze::Server.sessions.current[:body]['sessions']
105
+ steps %(
106
+ Then the session "id" is not null
107
+ And the session "startedAt" is a timestamp
108
+ )
109
+ else
110
+ steps %(
111
+ Then the sessionCount "sessionsStarted" is not null
112
+ And the sessionCount "startedAt" is a timestamp
113
+ )
114
+ end
115
+ end
116
+
@@ -0,0 +1,119 @@
1
+ require 'date'
2
+
3
+ # @!group Value steps
4
+
5
+ # Stores a payload value against a key for cross-request comparisons.
6
+ #
7
+ # @step_input request_type [String] The type of request (error, session, build, etc)
8
+ # @step_input field [String] The payload field to store
9
+ # @step_input key [String] The key to store the value against
10
+ Then('the {word} payload field {string} is stored as the value {string}') do |request_type, field, key|
11
+ list = Maze::Server.list_for request_type
12
+ value = Maze::Helper.read_key_path(list.current[:body], field)
13
+ Maze::Store.values[key] = value.dup
14
+ end
15
+
16
+ # Tests whether a payload field matches a previously stored payload value
17
+ #
18
+ # @step_input request_type [String] The type of request (error, session, build, etc)
19
+ # @step_input field [String] The payload field to test
20
+ # @step_input key [String] The key indicating a previously stored value
21
+ Then('the {word} payload field {string} equals the stored value {string}') do |request_type, field, key|
22
+ list = Maze::Server.list_for request_type
23
+ payload_value = Maze::Helper.read_key_path(list.current[:body], field)
24
+ stored_value = Maze::Store.values[key]
25
+ result = Maze::Compare.value(payload_value, stored_value)
26
+ Maze.check.true(result.equal?, "Payload value: #{payload_value} does not equal stored value: #{stored_value}")
27
+ end
28
+
29
+ # Tests whether a payload field is distinct from a previously stored payload value
30
+ #
31
+ # @step_input request_type [String] The type of request (error, session, build, etc)
32
+ # @step_input field [String] The payload field to test
33
+ # @step_input key [String] The key indicating a previously stored value
34
+ Then('the {word} payload field {string} does not equal the stored value {string}') do |request_type, field, key|
35
+ list = Maze::Server.list_for request_type
36
+ payload_value = Maze::Helper.read_key_path(list.current[:body], field)
37
+ stored_value = Maze::Store.values[key]
38
+ result = Maze::Compare.value(payload_value, stored_value)
39
+ Maze.check.false(result.equal?, "Payload value: #{payload_value} equals stored value: #{stored_value}")
40
+ end
41
+
42
+ # Tests whether a payload field matches a previously stored payload value, ignoring case
43
+ #
44
+ # @step_input request_type [String] The type of request (error, session, build, etc)
45
+ # @step_input field [String] The payload field to test
46
+ # @step_input key [String] The key indicating a previously stored value
47
+ Then('the {word} payload field {string} equals the stored value {string} ignoring case') do |request_type, field, key|
48
+ list = Maze::Server.list_for request_type
49
+ payload_value = Maze::Helper.read_key_path(list.current[:body], field)
50
+ stored_value = Maze::Store.values[key]
51
+ payload_value.downcase!
52
+ stored_value.downcase!
53
+ result = Maze::Compare.value(payload_value, stored_value)
54
+ Maze.check.true(result.equal?, "Payload value: #{payload_value} does not equal stored value: #{stored_value}")
55
+ end
56
+
57
+ # Tests whether a payload field is distinct from a previously stored payload value, ignoring case
58
+ #
59
+ # @step_input request_type [String] The type of request (error, session, build, etc)
60
+ # @step_input field [String] The payload field to test
61
+ # @step_input key [String] The key indicating a previously stored value
62
+ Then('the {word} payload field {string} does not equal the stored value {string} ignoring case') do |request_type, field, key|
63
+ list = Maze::Server.list_for request_type
64
+ payload_value = Maze::Helper.read_key_path(list.current[:body], field)
65
+ stored_value = Maze::Store.values[key]
66
+ payload_value.downcase!
67
+ stored_value.downcase!
68
+ result = Maze::Compare.value(payload_value, stored_value)
69
+ Maze.check.false(result.equal?, "Payload value: #{payload_value} equals stored value: #{stored_value}")
70
+ end
71
+
72
+ # Tests whether a payload field is a number (Numeric according to Ruby)
73
+ #
74
+ # @step_input request_type [String] The type of request (error, session, build, etc)
75
+ # @step_input field [String] The payload field to test
76
+ Then('the {word} payload field {string} is a number') do |request_type, field|
77
+ list = Maze::Server.list_for request_type
78
+ value = Maze::Helper.read_key_path(list.current[:body], field)
79
+ Maze.check.kind_of Numeric, value
80
+ end
81
+
82
+ # Tests whether a payload field is an integer (Integer according to Ruby)
83
+ #
84
+ # @step_input request_type [String] The type of request (error, session, build, etc)
85
+ # @step_input field [String] The payload field to test
86
+ Then('the {word} payload field {string} is an integer') do |request_type, field|
87
+ list = Maze::Server.list_for request_type
88
+ value = Maze::Helper.read_key_path(list.current[:body], field)
89
+ Maze.check.kind_of Integer, value
90
+ end
91
+
92
+ # Tests whether a payload field is a date (parseable as a Date, according to Ruby)
93
+ #
94
+ # @step_input request_type [String] The type of request (error, session, build, etc)
95
+ # @step_input field [String] The payload field to test
96
+ Then('the {word} payload field {string} is a date') do |request_type, field|
97
+ list = Maze::Server.list_for request_type
98
+ value = Maze::Helper.read_key_path(list.current[:body], field)
99
+ date = begin
100
+ Date.parse(value)
101
+ rescue StandardError
102
+ nil
103
+ end
104
+ Maze.check.kind_of Date, date
105
+ end
106
+
107
+ # Tests whether a payload field (loosely) matches a UUID regex (/[a-fA-F0-9-]!{36}/)
108
+ #
109
+ # @step_input request_type [String] The type of request (error, session, build, etc)
110
+ # @step_input field [String] The payload field to test
111
+ Then('the {word} payload field {string} is a UUID') do |request_type, field|
112
+ list = Maze::Server.list_for request_type
113
+ value = Maze::Helper.read_key_path(list.current[:body], field)
114
+ Maze.check.not_nil(value, "Expected UUID, got nil for #{field}")
115
+ match = /[a-fA-F0-9-]{36}/.match(value).size > 0
116
+ Maze.check.true(match, "Field #{field} is not a UUID, received #{value}")
117
+ end
118
+
119
+ # @!endgroup
@@ -0,0 +1,7 @@
1
+ require 'securerandom'
2
+
3
+ # The apikey that should be used on this test run
4
+ $api_key = SecureRandom.hex(16).tr('+/=', 'xyz')
5
+
6
+ # A regex providing the pattern expected from timestamps
7
+ TIMESTAMP_REGEX = /^\d{4}\-\d{2}\-\d{2}T\d{2}:\d{2}:[\d\.]+(Z|[+-]\d{2}:\d{2})?$/