bugsnag-maze-runner 7.22.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/bugsnag-print-load-paths +6 -0
- data/bin/download-logs +74 -0
- data/bin/maze-runner +174 -0
- data/bin/upload-app +56 -0
- data/lib/features/scripts/await-android-emulator.sh +11 -0
- data/lib/features/scripts/clear-android-app-data.sh +8 -0
- data/lib/features/scripts/force-stop-android-app.sh +8 -0
- data/lib/features/scripts/install-android-app.sh +15 -0
- data/lib/features/scripts/launch-android-app.sh +38 -0
- data/lib/features/scripts/launch-android-emulator.sh +15 -0
- data/lib/features/steps/android_steps.rb +51 -0
- data/lib/features/steps/app_automator_steps.rb +228 -0
- data/lib/features/steps/aws_sam_steps.rb +212 -0
- data/lib/features/steps/breadcrumb_steps.rb +80 -0
- data/lib/features/steps/browser_steps.rb +93 -0
- data/lib/features/steps/build_api_steps.rb +25 -0
- data/lib/features/steps/document_server_steps.rb +7 -0
- data/lib/features/steps/error_reporting_steps.rb +358 -0
- data/lib/features/steps/feature_flag_steps.rb +190 -0
- data/lib/features/steps/header_steps.rb +72 -0
- data/lib/features/steps/log_steps.rb +29 -0
- data/lib/features/steps/multipart_request_steps.rb +142 -0
- data/lib/features/steps/network_steps.rb +135 -0
- data/lib/features/steps/payload_steps.rb +257 -0
- data/lib/features/steps/proxy_steps.rb +34 -0
- data/lib/features/steps/query_parameter_steps.rb +31 -0
- data/lib/features/steps/request_assertion_steps.rb +186 -0
- data/lib/features/steps/runner_steps.rb +428 -0
- data/lib/features/steps/session_tracking_steps.rb +116 -0
- data/lib/features/steps/trace_steps.rb +206 -0
- data/lib/features/steps/value_steps.rb +119 -0
- data/lib/features/support/env.rb +7 -0
- data/lib/features/support/internal_hooks.rb +207 -0
- data/lib/maze/api/appium/file_manager.rb +29 -0
- data/lib/maze/appium_server.rb +112 -0
- data/lib/maze/assertions/request_set_assertions.rb +97 -0
- data/lib/maze/aws/sam.rb +112 -0
- data/lib/maze/aws_public_ip.rb +53 -0
- data/lib/maze/bugsnag_config.rb +42 -0
- data/lib/maze/checks/assert_check.rb +69 -0
- data/lib/maze/checks/noop_check.rb +34 -0
- data/lib/maze/client/appium/base_client.rb +131 -0
- data/lib/maze/client/appium/bb_client.rb +102 -0
- data/lib/maze/client/appium/bb_devices.rb +127 -0
- data/lib/maze/client/appium/bs_client.rb +91 -0
- data/lib/maze/client/appium/bs_devices.rb +141 -0
- data/lib/maze/client/appium/bs_legacy_client.rb +31 -0
- data/lib/maze/client/appium/local_client.rb +67 -0
- data/lib/maze/client/appium.rb +23 -0
- data/lib/maze/client/bb_api_client.rb +102 -0
- data/lib/maze/client/bb_client_utils.rb +181 -0
- data/lib/maze/client/bs_client_utils.rb +168 -0
- data/lib/maze/client/selenium/base_client.rb +15 -0
- data/lib/maze/client/selenium/bb_browsers.yml +188 -0
- data/lib/maze/client/selenium/bb_client.rb +38 -0
- data/lib/maze/client/selenium/bs_browsers.yml +257 -0
- data/lib/maze/client/selenium/bs_client.rb +89 -0
- data/lib/maze/client/selenium/local_client.rb +16 -0
- data/lib/maze/client/selenium.rb +16 -0
- data/lib/maze/compare.rb +161 -0
- data/lib/maze/configuration.rb +182 -0
- data/lib/maze/docker.rb +147 -0
- data/lib/maze/document_server.rb +46 -0
- data/lib/maze/driver/appium.rb +198 -0
- data/lib/maze/driver/browser.rb +124 -0
- data/lib/maze/errors.rb +52 -0
- data/lib/maze/generator.rb +55 -0
- data/lib/maze/helper.rb +122 -0
- data/lib/maze/hooks/appium_hooks.rb +55 -0
- data/lib/maze/hooks/browser_hooks.rb +15 -0
- data/lib/maze/hooks/command_hooks.rb +9 -0
- data/lib/maze/hooks/error_code_hook.rb +49 -0
- data/lib/maze/hooks/hooks.rb +61 -0
- data/lib/maze/http_request.rb +21 -0
- data/lib/maze/interactive_cli.rb +173 -0
- data/lib/maze/logger.rb +86 -0
- data/lib/maze/macos_utils.rb +14 -0
- data/lib/maze/maze_output.rb +88 -0
- data/lib/maze/network.rb +49 -0
- data/lib/maze/option/parser.rb +240 -0
- data/lib/maze/option/processor.rb +130 -0
- data/lib/maze/option/validator.rb +155 -0
- data/lib/maze/option.rb +62 -0
- data/lib/maze/plugins/bugsnag_reporting_plugin.rb +49 -0
- data/lib/maze/plugins/cucumber_report_plugin.rb +101 -0
- data/lib/maze/plugins/error_code_plugin.rb +21 -0
- data/lib/maze/plugins/global_retry_plugin.rb +38 -0
- data/lib/maze/proxy.rb +114 -0
- data/lib/maze/request_list.rb +87 -0
- data/lib/maze/request_repeater.rb +49 -0
- data/lib/maze/retry_handler.rb +67 -0
- data/lib/maze/runner.rb +149 -0
- data/lib/maze/schemas/OtelTraceSchema.json +390 -0
- data/lib/maze/schemas/trace_schema.rb +7 -0
- data/lib/maze/schemas/trace_validator.rb +98 -0
- data/lib/maze/server.rb +251 -0
- data/lib/maze/servlets/base_servlet.rb +27 -0
- data/lib/maze/servlets/command_servlet.rb +47 -0
- data/lib/maze/servlets/log_servlet.rb +64 -0
- data/lib/maze/servlets/reflective_servlet.rb +70 -0
- data/lib/maze/servlets/servlet.rb +199 -0
- data/lib/maze/servlets/temp.rb +0 -0
- data/lib/maze/servlets/trace_servlet.rb +13 -0
- data/lib/maze/store.rb +15 -0
- data/lib/maze/terminating_server.rb +129 -0
- data/lib/maze/timers.rb +51 -0
- data/lib/maze/wait.rb +35 -0
- data/lib/maze.rb +27 -0
- data/lib/utils/deep_merge.rb +17 -0
- data/lib/utils/selenium_money_patch.rb +17 -0
- metadata +451 -0
@@ -0,0 +1,358 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @!group Error reporting steps
|
4
|
+
|
5
|
+
# Verifies that generic elements of an error payload are present.
|
6
|
+
# APIKey fields and headers are tested against the '$api_key' global variable.
|
7
|
+
#
|
8
|
+
# @step_input version [String] The payload version expected
|
9
|
+
# @step_input name [String] The expected name of the notifier
|
10
|
+
Then('the error is valid for the error reporting API version {string} for the {string} notifier') do |version, name|
|
11
|
+
step "the error is valid for the error reporting API version \"#{version}\"" \
|
12
|
+
" for the \"#{name}\" notifier with the apiKey \"#{$api_key}\""
|
13
|
+
end
|
14
|
+
|
15
|
+
# Verifies that generic elements of an error payload are present.
|
16
|
+
#
|
17
|
+
# @step_input version [String] The payload version expected
|
18
|
+
# @step_input name [String] The expected name of the notifier
|
19
|
+
# @step_input api_key [String] The API key expected
|
20
|
+
Then('the error is valid for the error reporting API version {string}' \
|
21
|
+
' for the {string} notifier with the apiKey {string}') do |payload_version, notifier_name, api_key|
|
22
|
+
steps %(
|
23
|
+
Then the error "Bugsnag-Api-Key" header equals "#{api_key}"
|
24
|
+
And the error payload field "apiKey" equals "#{api_key}"
|
25
|
+
And the error "Bugsnag-Payload-Version" header equals "#{payload_version}"
|
26
|
+
And the error payload contains the payloadVersion "#{payload_version}"
|
27
|
+
And the error "Content-Type" header equals "application/json"
|
28
|
+
And the error "Bugsnag-Sent-At" header is a timestamp
|
29
|
+
And the error Bugsnag-Integrity header is valid
|
30
|
+
|
31
|
+
And the error payload field "notifier.name" equals "#{notifier_name}"
|
32
|
+
And the error payload field "notifier.url" is not null
|
33
|
+
And the error payload field "notifier.version" is not null
|
34
|
+
And the error payload field "events" is a non-empty array
|
35
|
+
|
36
|
+
And each element in error payload field "events" has "severity"
|
37
|
+
And each element in error payload field "events" has "severityReason.type"
|
38
|
+
And each element in error payload field "events" has "unhandled"
|
39
|
+
And each element in error payload field "events" has "exceptions"
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
# Verifies that an event is correct for an unhandled error
|
45
|
+
# This checks various elements of the payload matching an unhandled error including:
|
46
|
+
# The unhandled flag
|
47
|
+
# Any attached session information
|
48
|
+
# Severity
|
49
|
+
#
|
50
|
+
# @param event [Integer] The event to verify
|
51
|
+
Then('event {int} is unhandled') do |event|
|
52
|
+
test_unhandled_state(event, true)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Verifies that an event is correct for an unhandled error
|
56
|
+
# This checks various elements of the payload matching an unhandled error including:
|
57
|
+
# The unhandled flag
|
58
|
+
# Any attached session information
|
59
|
+
# Severity
|
60
|
+
#
|
61
|
+
# @param event [Integer] The event to verify
|
62
|
+
# @param severity [String] An expected severity different to the default "error"
|
63
|
+
Then('event {int} is unhandled with the severity {string}') do |event, severity|
|
64
|
+
test_unhandled_state(event, true, severity)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Verifies that an event is correct for an handled error
|
68
|
+
# This checks various elements of the payload matching an unhandled error including:
|
69
|
+
# The unhandled flag
|
70
|
+
# Any attached session information
|
71
|
+
# Severity
|
72
|
+
#
|
73
|
+
# @param event [Integer] The event to verify
|
74
|
+
Then('event {int} is handled') do |event|
|
75
|
+
test_unhandled_state(event, false)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Verifies that an event is correct for an handled error
|
79
|
+
# This checks various elements of the payload matching an unhandled error including:
|
80
|
+
# The unhandled flag
|
81
|
+
# Any attached session information
|
82
|
+
# Severity
|
83
|
+
#
|
84
|
+
# @param event [Integer] The event to verify
|
85
|
+
# @param severity [String] An expected severity different to the default "error"
|
86
|
+
Then('event {int} is handled with the severity {string}') do |event, severity|
|
87
|
+
test_unhandled_state(event, false, severity)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Checks the payloadVersion is set correctly.
|
91
|
+
# For Javascript this should be in the events.
|
92
|
+
# For all other notifiers this should be a top-level key.
|
93
|
+
#
|
94
|
+
# @step_input payload_version [String] The payload version expected
|
95
|
+
Then('the error payload contains the payloadVersion {string}') do |payload_version|
|
96
|
+
body_version = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], 'payloadVersion')
|
97
|
+
body_set = payload_version == body_version
|
98
|
+
event_version = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], 'events.0.payloadVersion')
|
99
|
+
event_set = payload_version == event_version
|
100
|
+
Maze.check.true(
|
101
|
+
body_set || event_set,
|
102
|
+
"The payloadVersion was not the expected value of #{payload_version}. " \
|
103
|
+
"#{body_version} found in body, #{event_version} found in event"
|
104
|
+
)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Tests whether a value in the first event entry matches a literal.
|
108
|
+
#
|
109
|
+
# @step_input field [String] The relative location of the value to test
|
110
|
+
# @step_input literal [Enum] The literal to test against, one of: true, false, null, not null
|
111
|
+
Then(/^the event "(.+)" is (true|false|null|not null)$/) do |field, literal|
|
112
|
+
step "the error payload field \"events.0.#{field}\" is #{literal}"
|
113
|
+
end
|
114
|
+
|
115
|
+
# Tests whether a value in the first event entry matches a string.
|
116
|
+
#
|
117
|
+
# @step_input field [String] The relative location of the value to test
|
118
|
+
# @step_input string_value [String] The string to match against
|
119
|
+
Then('the event {string} equals {string}') do |field, string_value|
|
120
|
+
step "the error payload field \"events.0.#{field}\" equals \"#{string_value}\""
|
121
|
+
end
|
122
|
+
|
123
|
+
# Tests whether a value in the first event entry matches a floating point value.
|
124
|
+
#
|
125
|
+
# @step_input field [String] The relative location of the value to test
|
126
|
+
# @step_input string_value [String] The string to match against
|
127
|
+
Then('the event {string} equals {float}') do |field, float_value|
|
128
|
+
step "the error payload field \"events.0.#{field}\" equals #{float_value}"
|
129
|
+
end
|
130
|
+
|
131
|
+
# Tests whether a value in the first event entry matches a floating point value, to a given number of decimal places.
|
132
|
+
#
|
133
|
+
# @step_input field [String] The relative location of the value to test
|
134
|
+
# @step_input string_value [String] The string to match against
|
135
|
+
Then('the event {string} equals {float} to {int} decimal place(s)') do |field, float_value, places|
|
136
|
+
step "the error payload field \"events.0.#{field}\" equals #{float_value} to #{places} decimal places"
|
137
|
+
end
|
138
|
+
|
139
|
+
# Tests whether a value in the first event entry equals an integer.
|
140
|
+
#
|
141
|
+
# @step_input field [String] The relative location of the value to test
|
142
|
+
# @step_input value [Integer] The integer to test against
|
143
|
+
Then('the event {string} equals {int}') do |field, value|
|
144
|
+
step "the error payload field \"events.0.#{field}\" equals #{value}"
|
145
|
+
end
|
146
|
+
|
147
|
+
# Tests whether a value in the first event entry starts with a string.
|
148
|
+
#
|
149
|
+
# @step_input field [String] The relative location of the value to test
|
150
|
+
# @step_input string_value [String] The string to match against
|
151
|
+
Then('the event {string} starts with {string}') do |field, string_value|
|
152
|
+
step "the error payload field \"events.0.#{field}\" starts with \"#{string_value}\""
|
153
|
+
end
|
154
|
+
|
155
|
+
# Tests whether a value in the first event entry ends with a string.
|
156
|
+
#
|
157
|
+
# @step_input field [String] The relative location of the value to test
|
158
|
+
# @step_input string_value [String] The string to match against
|
159
|
+
Then('the event {string} ends with {string}') do |field, string_value|
|
160
|
+
step "the error payload field \"events.0.#{field}\" ends with \"#{string_value}\""
|
161
|
+
end
|
162
|
+
|
163
|
+
# Tests whether a value in the first event entry matches a regex.
|
164
|
+
#
|
165
|
+
# @step_input field [String] The relative location of the value to test
|
166
|
+
# @step_input pattern [String] The regex to match against
|
167
|
+
Then('the event {string} matches {string}') do |field, pattern|
|
168
|
+
step "the error payload field \"events.0.#{field}\" matches the regex \"#{pattern}\""
|
169
|
+
end
|
170
|
+
|
171
|
+
# Tests whether a value in the first event entry is a timestamp.
|
172
|
+
#
|
173
|
+
# @step_input field [String] The relative location of the value to test
|
174
|
+
Then('the event {string} is a timestamp') do |field|
|
175
|
+
step "the error payload field \"events.0.#{field}\" matches the regex \"#{TIMESTAMP_REGEX}\""
|
176
|
+
end
|
177
|
+
|
178
|
+
# Tests whether a value in the first event entry is a numeric and parsable timestamp.
|
179
|
+
#
|
180
|
+
# @step_input field [String] The relative location of the value to test
|
181
|
+
Then('the event {string} is a parsable timestamp in seconds') do |field|
|
182
|
+
step "the error payload field \"events.0.#{field}\" is a parsable timestamp in seconds"
|
183
|
+
end
|
184
|
+
|
185
|
+
# Tests the Event field value against an environment variable.
|
186
|
+
#
|
187
|
+
# @step_input field [String] The payload element to check
|
188
|
+
# @step_input env_var [String] The environment variable to test against
|
189
|
+
Then('the event {string} equals the environment variable {string}') do |field, env_var|
|
190
|
+
step "the error payload field \"events.0.#{field}\" equals the environment variable \"#{env_var}\""
|
191
|
+
end
|
192
|
+
|
193
|
+
# Tests whether a value in the first event entry matches a JSON fixture.
|
194
|
+
#
|
195
|
+
# @step_input field [String] The relative location of the value to test
|
196
|
+
# @step_input fixture_path [String] The fixture to match against
|
197
|
+
Then('the event {string} matches the JSON fixture in {string}') do |field, fixture_path|
|
198
|
+
step "the error payload field \"events.0.#{field}\" matches the JSON fixture in \"#{fixture_path}\""
|
199
|
+
end
|
200
|
+
|
201
|
+
Then('the event {string} string is empty') do |keypath|
|
202
|
+
value = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], keypath)
|
203
|
+
Maze.check.true(value.nil? || value.empty?, "The #{keypath} is not empty: '#{value}'")
|
204
|
+
end
|
205
|
+
|
206
|
+
Then('the event {string} is greater than {int}') do |keypath, int|
|
207
|
+
value = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], "events.0.#{keypath}")
|
208
|
+
Maze.check.false(value.nil?, "The event #{keypath} is nil")
|
209
|
+
Maze.check.operator(value, :>, int)
|
210
|
+
end
|
211
|
+
|
212
|
+
# Tests whether a value in the first exception of the first event entry starts with a string.
|
213
|
+
#
|
214
|
+
# @step_input field [String] The relative location of the value to test
|
215
|
+
# @step_input string_value [String] The string to match against
|
216
|
+
Then('the exception {string} starts with {string}') do |field, string_value|
|
217
|
+
step "the error payload field \"events.0.exceptions.0.#{field}\" starts with \"#{string_value}\""
|
218
|
+
end
|
219
|
+
|
220
|
+
# Tests whether a value in the first exception of the first event entry ends with a string.
|
221
|
+
#
|
222
|
+
# @step_input field [String] The relative location of the value to test
|
223
|
+
# @step_input string_value [String] The string to match against
|
224
|
+
Then('the exception {string} ends with {string}') do |field, string_value|
|
225
|
+
step "the error payload field \"events.0.exceptions.0.#{field}\" ends with \"#{string_value}\""
|
226
|
+
end
|
227
|
+
|
228
|
+
# Tests whether a value in the first exception of the first event entry equals a string.
|
229
|
+
#
|
230
|
+
# @step_input field [String] The relative location of the value to test
|
231
|
+
# @step_input string_value [String] The string to match against
|
232
|
+
Then('the exception {string} equals {string}') do |field, string_value|
|
233
|
+
step "the error payload field \"events.0.exceptions.0.#{field}\" equals \"#{string_value}\""
|
234
|
+
end
|
235
|
+
|
236
|
+
# Tests whether a value in the first exception of the first event entry matches a regex.
|
237
|
+
#
|
238
|
+
# @step_input field [String] The relative location of the value to test
|
239
|
+
# @step_input pattern [String] The regex to match against
|
240
|
+
Then('the exception {string} matches {string}') do |field, pattern|
|
241
|
+
step "the error payload field \"events.0.exceptions.0.#{field}\" matches the regex \"#{pattern}\""
|
242
|
+
end
|
243
|
+
|
244
|
+
# Tests whether a element of a stack frame in the first exception of the first event equals an integer.
|
245
|
+
#
|
246
|
+
# @step_input key [String] The element of the stack frame to test
|
247
|
+
# @step_input num [Integer] The stack frame where the element is present
|
248
|
+
# @step_input value [Integer] The value to test against
|
249
|
+
Then('the {string} of stack frame {int} equals {int}') do |key, num, value|
|
250
|
+
field = "events.0.exceptions.0.stacktrace.#{num}.#{key}"
|
251
|
+
step "the error payload field \"#{field}\" equals #{value}"
|
252
|
+
end
|
253
|
+
|
254
|
+
# Tests whether an element of a stack frame in the first exception of the first event matches a regex pattern.
|
255
|
+
#
|
256
|
+
# @step_input key [String] The element of the stack frame to test
|
257
|
+
# @step_input num [Integer] The stack frame where the element is present
|
258
|
+
# @step_input pattern [String] The regex to match against
|
259
|
+
Then('the {string} of stack frame {int} matches {string}') do |key, num, pattern|
|
260
|
+
field = "events.0.exceptions.0.stacktrace.#{num}.#{key}"
|
261
|
+
step "the error payload field \"#{field}\" matches the regex \"#{pattern}\""
|
262
|
+
end
|
263
|
+
|
264
|
+
# Tests whether an element of a stack frame in the first exception of the first event equals a string.
|
265
|
+
#
|
266
|
+
# @step_input key [String] The element of the stack frame to test
|
267
|
+
# @step_input num [Integer] The stack frame where the element is present
|
268
|
+
# @step_input value [String] The value to test against
|
269
|
+
Then('the {string} of stack frame {int} equals {string}') do |key, num, value|
|
270
|
+
field = "events.0.exceptions.0.stacktrace.#{num}.#{key}"
|
271
|
+
step "the error payload field \"#{field}\" equals \"#{value}\""
|
272
|
+
end
|
273
|
+
|
274
|
+
# Tests whether an element of a stack frame in the first exception of the first event starts with a string.
|
275
|
+
#
|
276
|
+
# @step_input key [String] The element of the stack frame to test
|
277
|
+
# @step_input num [Integer] The stack frame where the element is present
|
278
|
+
# @step_input value [String] The value to test against
|
279
|
+
Then('the {string} of stack frame {int} starts with {string}') do |key, num, value|
|
280
|
+
field = "events.0.exceptions.0.stacktrace.#{num}.#{key}"
|
281
|
+
step "the error payload field \"#{field}\" starts with \"#{value}\""
|
282
|
+
end
|
283
|
+
|
284
|
+
# Tests whether an element of a stack frame in the first exception of the first event ends with a string.
|
285
|
+
#
|
286
|
+
# @step_input key [String] The element of the stack frame to test
|
287
|
+
# @step_input num [Integer] The stack frame where the element is present
|
288
|
+
# @step_input value [String] The value to test against
|
289
|
+
Then('the {string} of stack frame {int} ends with {string}') do |key, num, value|
|
290
|
+
field = "events.0.exceptions.0.stacktrace.#{num}.#{key}"
|
291
|
+
step "the error payload field \"#{field}\" ends with \"#{value}\""
|
292
|
+
end
|
293
|
+
|
294
|
+
# Tests whether an element of a stack frame in the first exception of the first event matches a literal.
|
295
|
+
#
|
296
|
+
# @step_input key [String] The element of the stack frame to test
|
297
|
+
# @step_input num [Integer] The stack frame where the element is present
|
298
|
+
# @step_input literal [Enum] The literal to test against, one of: true, false, null, not null
|
299
|
+
Then(/^the "(.*)" of stack frame (\d*) is (true|false|null|not null)$/) do |key, num, literal|
|
300
|
+
field = "events.0.exceptions.0.stacktrace.#{num}.#{key}"
|
301
|
+
step "the error payload field \"#{field}\" is #{literal}"
|
302
|
+
end
|
303
|
+
|
304
|
+
# Tests whether a thread from the first event, identified by name, is the error reporting thread.
|
305
|
+
#
|
306
|
+
# @step_input thread_name [String] The name of the thread to test
|
307
|
+
Then('the thread with name {string} contains the error reporting flag') do |thread_name|
|
308
|
+
validate_error_reporting_thread('name', thread_name)
|
309
|
+
end
|
310
|
+
|
311
|
+
# Tests whether a thread from the first event, identified by an id, is the error reporting thread.
|
312
|
+
#
|
313
|
+
# @step_input thread_id [String] The id of the thread to test
|
314
|
+
Then('the thread with id {string} contains the error reporting flag') do |thread_id|
|
315
|
+
validate_error_reporting_thread('id', thread_id)
|
316
|
+
end
|
317
|
+
|
318
|
+
# Tests that a thread from the first event, identified by a particular key-value pair, is the error reporting thread.
|
319
|
+
#
|
320
|
+
# @param payload_key [String] The thread identifier key
|
321
|
+
# @param payload_value [Any] The thread identifier value
|
322
|
+
def validate_error_reporting_thread(payload_key, payload_value)
|
323
|
+
threads = Maze::Server.errors.current[:body]['events'].first['threads']
|
324
|
+
Maze.check.kind_of Array, threads
|
325
|
+
count = 0
|
326
|
+
|
327
|
+
threads.each do |thread|
|
328
|
+
count += 1 if thread[payload_key].to_s == payload_value && thread['errorReportingThread'] == true
|
329
|
+
end
|
330
|
+
Maze.check.equal(1, count)
|
331
|
+
end
|
332
|
+
|
333
|
+
# Tests whether an event has the correct attributes we'd expect for un/handled events
|
334
|
+
#
|
335
|
+
# @param event [Integer] The index of the event
|
336
|
+
# @param unhandled [Boolean] Whether the event is unhandled or handled
|
337
|
+
# @param severity [String] Optional. An overwritten severity to look for
|
338
|
+
def test_unhandled_state(event, unhandled, severity = nil)
|
339
|
+
expected_unhandled_state = unhandled ? 'true' : 'false'
|
340
|
+
expected_severity = if severity
|
341
|
+
severity
|
342
|
+
elsif unhandled
|
343
|
+
'error'
|
344
|
+
else
|
345
|
+
'warning'
|
346
|
+
end
|
347
|
+
steps %(
|
348
|
+
Then the error payload field "events.#{event}.unhandled" is #{expected_unhandled_state}
|
349
|
+
And the error payload field "events.#{event}.severity" equals "#{expected_severity}"
|
350
|
+
)
|
351
|
+
|
352
|
+
return if Maze::Helper.read_key_path(Maze::Server.errors.current[:body], "events.#{event}.session").nil?
|
353
|
+
|
354
|
+
session_field = unhandled ? 'unhandled' : 'handled'
|
355
|
+
steps %(
|
356
|
+
And the error payload field "events.#{event}.session.events.#{session_field}" is greater than 0
|
357
|
+
)
|
358
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @!group Feature flag steps
|
4
|
+
|
5
|
+
# Verifies that there are no feature flags present in a given event
|
6
|
+
#
|
7
|
+
# @step_input event_id [Integer] The id of the event in the payloads array
|
8
|
+
Then('event {int} has no feature flags') do |event_id|
|
9
|
+
event = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], "events.#{event_id}")
|
10
|
+
Maze.check.false(has_feature_flags?(event), "Feature flags expected to be absent or empty, was #{event['featureFlags']}")
|
11
|
+
end
|
12
|
+
|
13
|
+
# Verifies that the are no feature flags present
|
14
|
+
Then('the event has no feature flags') do
|
15
|
+
steps %(
|
16
|
+
Then event 0 has no feature flags
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Verifies a feature flag with a specific variant is uniquely present in a given event
|
21
|
+
#
|
22
|
+
# @step_input event_id [Integer] The id of the event in the payloads array
|
23
|
+
# @step_input flag_name [String] The featureFlag value expected
|
24
|
+
# @step_input variant [String] The variant value expected
|
25
|
+
Then('event {int} contains the feature flag {string} with variant {string}') do |event_id, flag_name, variant|
|
26
|
+
event = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], "events.#{event_id}")
|
27
|
+
Maze.check.true(has_feature_flags?(event), "Expected feature flags were not present in event #{event_id}: #{event}")
|
28
|
+
feature_flags = event['featureFlags']
|
29
|
+
# Test for flag name uniqueness
|
30
|
+
Maze.check.true(
|
31
|
+
feature_flags.one? { |flag| flag['featureFlag'].eql?(flag_name) },
|
32
|
+
"Expected single flag with 'featureFlag' value: #{flag_name}. Present flags: #{feature_flags}"
|
33
|
+
)
|
34
|
+
|
35
|
+
flag = feature_flags.find { |flag| flag['featureFlag'].eql?(flag_name) }
|
36
|
+
# Test the variant value
|
37
|
+
Maze.check.true(
|
38
|
+
flag.has_key?('variant') && flag['variant'].eql?(variant),
|
39
|
+
"Feature flag: #{flag} did not have variant: #{variant}. All flags: #{feature_flags}"
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Verifies a feature flag with a specific variant is uniquely present
|
44
|
+
#
|
45
|
+
# @step_input flag_name [String] The featureFlag value expected
|
46
|
+
# @step_input variant [String] The variant value expected
|
47
|
+
Then('the event contains the feature flag {string} with variant {string}') do |flag_name, variant|
|
48
|
+
steps %(
|
49
|
+
Then event 0 contains the feature flag "#{flag_name}" with variant "#{variant}"
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Verifies a feature flag with no variant (either null or missing) is uniquely present in a given event
|
54
|
+
#
|
55
|
+
# @step_input event_id [Integer] The id of the event in the payloads array
|
56
|
+
# @step_input flag_name [String] The featureFlag value expected
|
57
|
+
Then('event {int} contains the feature flag {string} with no variant') do |event_id, flag_name|
|
58
|
+
event = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], "events.#{event_id}")
|
59
|
+
Maze.check.true(has_feature_flags?(event),
|
60
|
+
"Expected feature flags were not present in event #{event_id}: #{event}")
|
61
|
+
feature_flags = event['featureFlags']
|
62
|
+
# Test for flag name uniqueness
|
63
|
+
Maze.check.true(
|
64
|
+
feature_flags.one? { |flag| flag['featureFlag'].eql?(flag_name) },
|
65
|
+
"Expected single flag with 'featureFlag' value: #{flag_name}. All flags: #{feature_flags}"
|
66
|
+
)
|
67
|
+
|
68
|
+
flag = feature_flags.find { |flag| flag['featureFlag'].eql?(flag_name) }
|
69
|
+
# Test the variant value
|
70
|
+
Maze.check.false(
|
71
|
+
flag.has_key?('variant'),
|
72
|
+
"Feature flag: #{flag} expected to have no variant. All flags: #{feature_flags}"
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Verifies a feature flag with no variant (either null or missing) is uniquely present
|
77
|
+
#
|
78
|
+
# @step_input flag_name [String] The featureFlag value expected
|
79
|
+
Then('the event contains the feature flag {string} with no variant') do |flag_name|
|
80
|
+
steps %(
|
81
|
+
Then event 0 contains the feature flag "#{flag_name}" with no variant
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Verifies that a number of feature flags outlined in a table are all present and unique in the given event
|
86
|
+
#
|
87
|
+
# The DataTable used for this step should have `featureFlag` and `variant` columns, containing the appropriate
|
88
|
+
# values. For flags with a variant leave the `variant` column blank.
|
89
|
+
#
|
90
|
+
# Example:
|
91
|
+
# | featureFlag | variant |
|
92
|
+
# | my_flag_1 | var_1 |
|
93
|
+
# | my_flag_2 | var_2 |
|
94
|
+
# | my_flag_3 | | # Should not have a variant present
|
95
|
+
#
|
96
|
+
# @step_input event_id [Integer] The id of the event in the payloads array
|
97
|
+
# @step_input table [Cucumber::MultilineArgument::DataTable] Table of expected values
|
98
|
+
Then('event {int} contains the following feature flags:') do |event_id, table|
|
99
|
+
event = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], "events.#{event_id}")
|
100
|
+
verify_feature_flags_with_table(event, table)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Verifies that a number of feature flags outlined in a table are all present and unique
|
104
|
+
#
|
105
|
+
# See above for data table details
|
106
|
+
#
|
107
|
+
# @step_input table [Cucumber::MultilineArgument::DataTable] Table of expected values
|
108
|
+
Then('the event contains the following feature flags:') do |table|
|
109
|
+
event = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], 'events.0')
|
110
|
+
verify_feature_flags_with_table(event, table)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Verifies a feature flag a specific name is not present, regardless of variant, for a given event
|
114
|
+
#
|
115
|
+
# @step_input event_id [Integer] The id of the event in the payloads array
|
116
|
+
# @step_input flag_name [String] The featureFlag value not expected
|
117
|
+
Then('event {int} does not contain the feature flag {string}') do |event_id, flag_name|
|
118
|
+
event = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], "events.#{event_id}")
|
119
|
+
Maze.check.true(
|
120
|
+
has_feature_flags?(event),
|
121
|
+
"Expected feature flags were not present in event #{event_id}: #{event}"
|
122
|
+
)
|
123
|
+
feature_flags = event['featureFlags']
|
124
|
+
Maze.check.true(
|
125
|
+
feature_flags.none? { |flag| flag['featureFlag'].eql?(flag_name) },
|
126
|
+
"Expected to not find feature flag #{flag_name}. All flags: #{feature_flags}"
|
127
|
+
)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Verifies a feature flag a specific name is not present, regardless of variant
|
131
|
+
#
|
132
|
+
# @step_input flag_name [String] The featureFlag value not expected
|
133
|
+
Then('the event does not contain the feature flag {string}') do |flag_name|
|
134
|
+
steps %(
|
135
|
+
Then event 0 does not contain the feature flag "#{flag_name}"
|
136
|
+
)
|
137
|
+
end
|
138
|
+
|
139
|
+
def verify_feature_flags_with_table(event, table)
|
140
|
+
Maze.check.true(
|
141
|
+
has_feature_flags?(event),
|
142
|
+
"Expected feature flags were not present in event: #{event}"
|
143
|
+
)
|
144
|
+
feature_flags = event['featureFlags']
|
145
|
+
|
146
|
+
expected_features = table.hashes
|
147
|
+
Maze.check.true(
|
148
|
+
feature_flags.size == expected_features.size,
|
149
|
+
"Expected #{expected_features.size} features,
|
150
|
+
found #{feature_flags}"
|
151
|
+
)
|
152
|
+
expected_features.each do |expected|
|
153
|
+
flag_name = expected['featureFlag']
|
154
|
+
variant = expected['variant']
|
155
|
+
# Test for flag name uniqueness
|
156
|
+
Maze.check.true(
|
157
|
+
feature_flags.one? { |flag| flag['featureFlag'].eql?(flag_name) },
|
158
|
+
"Expected single flag with 'featureFlag' value: #{flag_name}. Present flags: #{feature_flags}"
|
159
|
+
)
|
160
|
+
flag = feature_flags.find { |flag| flag['featureFlag'].eql?(flag_name) }
|
161
|
+
# Test the variant value
|
162
|
+
if variant.nil? || expected['variant'].empty?
|
163
|
+
Maze.check.false(
|
164
|
+
flag.has_key?('variant'),
|
165
|
+
"Feature flag: #{flag} expected to have no variant. All flags: #{feature_flags}"
|
166
|
+
)
|
167
|
+
else
|
168
|
+
Maze.check.true(
|
169
|
+
flag.has_key?('variant') && flag['variant'].eql?(variant),
|
170
|
+
"Feature flag: #{flag} did not have variant: #{variant}. All flags: #{feature_flags}"
|
171
|
+
)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def has_feature_flags?(event)
|
177
|
+
if event.has_key?('featureFlags')
|
178
|
+
Maze.check.false(
|
179
|
+
event['featureFlags'].nil?,
|
180
|
+
'The feature flags key was present, but null'
|
181
|
+
)
|
182
|
+
Maze.check.true(
|
183
|
+
event['featureFlags'].is_a?(Array),
|
184
|
+
"The feature flags key was present, but the value: #{event['featureFlags']} must be an array"
|
185
|
+
)
|
186
|
+
!event['featureFlags'].empty?
|
187
|
+
else
|
188
|
+
false
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @!group Header steps
|
4
|
+
|
5
|
+
# Tests that a request header is not null
|
6
|
+
#
|
7
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
8
|
+
# @step_input header_name [String] The header to test
|
9
|
+
Then('the {word} {string} header is not null') do |request_type, header_name|
|
10
|
+
Maze.check.not_nil(Maze::Server.list_for(request_type).current[:request][header_name],
|
11
|
+
"The #{request_type} '#{header_name}' header should not be null")
|
12
|
+
end
|
13
|
+
|
14
|
+
# Tests that a request header is null
|
15
|
+
#
|
16
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
17
|
+
# @step_input header_name [String] The header to test
|
18
|
+
Then('the {word} {string} header is null') do |request_type, header_name|
|
19
|
+
request = Maze::Server.list_for(request_type).current[:request]
|
20
|
+
|
21
|
+
Maze.check.nil(request[header_name],
|
22
|
+
"The #{request_type} '#{header_name}' header should be null")
|
23
|
+
end
|
24
|
+
|
25
|
+
# Tests that request header equals a string
|
26
|
+
#
|
27
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
28
|
+
# @step_input header_name [String] The header to test
|
29
|
+
# @step_input header_value [String] The string it should match
|
30
|
+
Then('the {word} {string} header equals {string}') do |request_type, header_name, header_value|
|
31
|
+
Maze.check.not_nil(Maze::Server.list_for(request_type).current[:request][header_name],
|
32
|
+
"The #{request_type} '#{header_name}' header wasn't present in the request")
|
33
|
+
Maze.check.equal(header_value, Maze::Server.list_for(request_type).current[:request][header_name])
|
34
|
+
end
|
35
|
+
|
36
|
+
# Tests that a request header matches a regex
|
37
|
+
#
|
38
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
39
|
+
# @step_input header_name [String] The header to test
|
40
|
+
# @step_input regex_string [String] The regex to match with
|
41
|
+
Then('the {word} {string} header matches the regex {string}') do |request_type, header_name, regex_string|
|
42
|
+
regex = Regexp.new(regex_string)
|
43
|
+
value = Maze::Server.list_for(request_type).current[:request][header_name]
|
44
|
+
Maze.check.match(regex, value)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Tests that a request header matches one of a list of strings
|
48
|
+
#
|
49
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
50
|
+
# @step_input header_name [String] The header to test
|
51
|
+
# @step_input header_values [DataTable] A parsed data table
|
52
|
+
Then('the {word} {string} header equals one of:') do |request_type, header_name, header_values|
|
53
|
+
Maze.check.include(header_values.raw.flatten, Maze::Server.list_for(request_type).current[:request][header_name])
|
54
|
+
end
|
55
|
+
|
56
|
+
# Tests that a request header is a timestamp.
|
57
|
+
#
|
58
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
59
|
+
# @step_input header_name [String] The header to test
|
60
|
+
Then('the {word} {string} header is a timestamp') do |request_type, header_name|
|
61
|
+
header = Maze::Server.list_for(request_type).current[:request][header_name]
|
62
|
+
Maze.check.match(TIMESTAMP_REGEX, header)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Checks that the Bugsnag-Integrity header is a SHA1 or simple digest
|
66
|
+
#
|
67
|
+
# @step_input request_type [String] The type of request (error, session, build, etc)
|
68
|
+
When('the {word} Bugsnag-Integrity header is valid') do |request_type|
|
69
|
+
Maze.check.true(Maze::Helper.valid_bugsnag_integrity_header(Maze::Server.list_for(request_type).current),
|
70
|
+
'Invalid Bugsnag-Integrity header detected')
|
71
|
+
end
|
72
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @!group Log assertion steps
|
4
|
+
|
5
|
+
# Tests that a log request matches a given level and message.
|
6
|
+
#
|
7
|
+
# @step_input log_level [String] The expected log level
|
8
|
+
# @step_input message [String] The expected message
|
9
|
+
Then('the {string} level log message equals {string}') do |log_level, message|
|
10
|
+
request = Maze::Server.logs.current
|
11
|
+
Maze.check.not_nil(request, 'No log message to check')
|
12
|
+
log = request[:body]
|
13
|
+
Maze.check.equal(log_level, Maze::Helper.read_key_path(log, 'level'))
|
14
|
+
Maze.check.equal(message, Maze::Helper.read_key_path(log, 'message'))
|
15
|
+
end
|
16
|
+
|
17
|
+
# Tests that a log request matches a given level and message regex.
|
18
|
+
#
|
19
|
+
# @step_input log_level [String] The expected log level
|
20
|
+
# @step_input message_regex [String] Regex for the expected message
|
21
|
+
Then('the {string} level log message matches the regex {string}') do |log_level, message_regex|
|
22
|
+
request = Maze::Server.logs.current
|
23
|
+
Maze.check.not_nil(request, 'No log message to check')
|
24
|
+
|
25
|
+
log = request[:body]
|
26
|
+
Maze.check.equal(log_level, Maze::Helper.read_key_path(log, 'level'))
|
27
|
+
regex = Regexp.new(message_regex)
|
28
|
+
Maze.check.match(regex, Maze::Helper.read_key_path(log, 'message'))
|
29
|
+
end
|