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.
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})?$/