bugsnag-maze-runner 7.22.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +7 -0
  2. data/bin/bugsnag-print-load-paths +6 -0
  3. data/bin/download-logs +74 -0
  4. data/bin/maze-runner +174 -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 +80 -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 +358 -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 +135 -0
  25. data/lib/features/steps/payload_steps.rb +257 -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 +186 -0
  29. data/lib/features/steps/runner_steps.rb +428 -0
  30. data/lib/features/steps/session_tracking_steps.rb +116 -0
  31. data/lib/features/steps/trace_steps.rb +206 -0
  32. data/lib/features/steps/value_steps.rb +119 -0
  33. data/lib/features/support/env.rb +7 -0
  34. data/lib/features/support/internal_hooks.rb +207 -0
  35. data/lib/maze/api/appium/file_manager.rb +29 -0
  36. data/lib/maze/appium_server.rb +112 -0
  37. data/lib/maze/assertions/request_set_assertions.rb +97 -0
  38. data/lib/maze/aws/sam.rb +112 -0
  39. data/lib/maze/aws_public_ip.rb +53 -0
  40. data/lib/maze/bugsnag_config.rb +42 -0
  41. data/lib/maze/checks/assert_check.rb +69 -0
  42. data/lib/maze/checks/noop_check.rb +34 -0
  43. data/lib/maze/client/appium/base_client.rb +131 -0
  44. data/lib/maze/client/appium/bb_client.rb +102 -0
  45. data/lib/maze/client/appium/bb_devices.rb +127 -0
  46. data/lib/maze/client/appium/bs_client.rb +91 -0
  47. data/lib/maze/client/appium/bs_devices.rb +141 -0
  48. data/lib/maze/client/appium/bs_legacy_client.rb +31 -0
  49. data/lib/maze/client/appium/local_client.rb +67 -0
  50. data/lib/maze/client/appium.rb +23 -0
  51. data/lib/maze/client/bb_api_client.rb +102 -0
  52. data/lib/maze/client/bb_client_utils.rb +181 -0
  53. data/lib/maze/client/bs_client_utils.rb +168 -0
  54. data/lib/maze/client/selenium/base_client.rb +15 -0
  55. data/lib/maze/client/selenium/bb_browsers.yml +188 -0
  56. data/lib/maze/client/selenium/bb_client.rb +38 -0
  57. data/lib/maze/client/selenium/bs_browsers.yml +257 -0
  58. data/lib/maze/client/selenium/bs_client.rb +89 -0
  59. data/lib/maze/client/selenium/local_client.rb +16 -0
  60. data/lib/maze/client/selenium.rb +16 -0
  61. data/lib/maze/compare.rb +161 -0
  62. data/lib/maze/configuration.rb +182 -0
  63. data/lib/maze/docker.rb +147 -0
  64. data/lib/maze/document_server.rb +46 -0
  65. data/lib/maze/driver/appium.rb +198 -0
  66. data/lib/maze/driver/browser.rb +124 -0
  67. data/lib/maze/errors.rb +52 -0
  68. data/lib/maze/generator.rb +55 -0
  69. data/lib/maze/helper.rb +122 -0
  70. data/lib/maze/hooks/appium_hooks.rb +55 -0
  71. data/lib/maze/hooks/browser_hooks.rb +15 -0
  72. data/lib/maze/hooks/command_hooks.rb +9 -0
  73. data/lib/maze/hooks/error_code_hook.rb +49 -0
  74. data/lib/maze/hooks/hooks.rb +61 -0
  75. data/lib/maze/http_request.rb +21 -0
  76. data/lib/maze/interactive_cli.rb +173 -0
  77. data/lib/maze/logger.rb +86 -0
  78. data/lib/maze/macos_utils.rb +14 -0
  79. data/lib/maze/maze_output.rb +88 -0
  80. data/lib/maze/network.rb +49 -0
  81. data/lib/maze/option/parser.rb +240 -0
  82. data/lib/maze/option/processor.rb +130 -0
  83. data/lib/maze/option/validator.rb +155 -0
  84. data/lib/maze/option.rb +62 -0
  85. data/lib/maze/plugins/bugsnag_reporting_plugin.rb +49 -0
  86. data/lib/maze/plugins/cucumber_report_plugin.rb +101 -0
  87. data/lib/maze/plugins/error_code_plugin.rb +21 -0
  88. data/lib/maze/plugins/global_retry_plugin.rb +38 -0
  89. data/lib/maze/proxy.rb +114 -0
  90. data/lib/maze/request_list.rb +87 -0
  91. data/lib/maze/request_repeater.rb +49 -0
  92. data/lib/maze/retry_handler.rb +67 -0
  93. data/lib/maze/runner.rb +149 -0
  94. data/lib/maze/schemas/OtelTraceSchema.json +390 -0
  95. data/lib/maze/schemas/trace_schema.rb +7 -0
  96. data/lib/maze/schemas/trace_validator.rb +98 -0
  97. data/lib/maze/server.rb +251 -0
  98. data/lib/maze/servlets/base_servlet.rb +27 -0
  99. data/lib/maze/servlets/command_servlet.rb +47 -0
  100. data/lib/maze/servlets/log_servlet.rb +64 -0
  101. data/lib/maze/servlets/reflective_servlet.rb +70 -0
  102. data/lib/maze/servlets/servlet.rb +199 -0
  103. data/lib/maze/servlets/temp.rb +0 -0
  104. data/lib/maze/servlets/trace_servlet.rb +13 -0
  105. data/lib/maze/store.rb +15 -0
  106. data/lib/maze/terminating_server.rb +129 -0
  107. data/lib/maze/timers.rb +51 -0
  108. data/lib/maze/wait.rb +35 -0
  109. data/lib/maze.rb +27 -0
  110. data/lib/utils/deep_merge.rb +17 -0
  111. data/lib/utils/selenium_money_patch.rb +17 -0
  112. metadata +451 -0
@@ -0,0 +1,228 @@
1
+ # @!group App Automator steps
2
+
3
+ # Checks a UI element is present
4
+ # Requires a running Appium driver
5
+ #
6
+ # @step_input element_id [String] The locator id
7
+ Given('the element {string} is present') do |element_id|
8
+ present = Maze.driver.wait_for_element(element_id)
9
+ raise Maze::Error::AppiumElementNotFoundError.new("The element #{element_id} could not be found", element_id) unless present
10
+ end
11
+
12
+ # Checks a UI element is present within a specified number of seconds
13
+ # Requires a running Appium driver
14
+ #
15
+ # @step_input element_id [String] The locator id
16
+ # @step_input timeout [Int] The number of seconds to wait before timing out
17
+ Given('the element {string} is present within {int} seconds') do |element_id, timeout|
18
+ present = Maze.driver.wait_for_element(element_id, timeout)
19
+ raise Maze::Error::AppiumElementNotFoundError.new("The element #{element_id} could not be found", element_id) unless present
20
+ end
21
+
22
+ # Clicks a given element
23
+ # Requires a running Appium driver
24
+ #
25
+ # @step_input element_id [String] The locator id
26
+ When('I click the element {string}') do |element_id|
27
+ Maze.driver.click_element(element_id)
28
+ end
29
+
30
+ # Sends the app to the background indefinitely
31
+ # Requires a running Appium driver
32
+ When('I send the app to the background') do
33
+ Maze.driver.background_app(-1)
34
+ end
35
+
36
+ # Sends the app to the background for a number of seconds
37
+ # Requires a running Appium driver
38
+ #
39
+ # @step_input timeout [Integer] The amount of time the app is in the background in seconds
40
+ When('I send the app to the background for {int} second(s)') do |timeout|
41
+ Maze.driver.background_app(timeout)
42
+ end
43
+
44
+ # Clears a given element
45
+ # Requires a running Appium driver
46
+ #
47
+ # @step_input element_id [String] The locator id
48
+ When('I clear the element {string}') do |element_id|
49
+ Maze.driver.clear_element(element_id)
50
+ end
51
+
52
+ # Sends keys to a given element
53
+ # Requires a running Appium driver
54
+ #
55
+ # @step_input keys [String] The keys to send to the element
56
+ # @step_input element_id [String] The locator id
57
+ When('I send the keys {string} to the element {string}') do |keys, element_id|
58
+ Maze.driver.send_keys_to_element(element_id, keys)
59
+ end
60
+
61
+ # Tests that the given payload value is correct for the target BrowserStack platform.
62
+ # This step will assume the expected and payload values are strings.
63
+ # If the step is invoked when a remote BrowserStack device is not in use this step will fail.
64
+ #
65
+ # The DataTable used for this step should have `ios` and `android` in the same row as their expected value:
66
+ # | android | Java.lang.RuntimeException |
67
+ # | ios | NSException |
68
+ #
69
+ # If the expected value is set to "@skip", the check should be skipped
70
+ # If the expected value is set to "@null", the check will be for null
71
+ # If the expected value is set to "@not_null", the check will be for a non-null value
72
+ #
73
+ # @step_input request_type [String] The type of request (error, session, build, etc)
74
+ # @step_input field_path [String] The field to test
75
+ # @step_input platform_values [DataTable] A table of acceptable values for each platform
76
+ Then('the {word} payload field {string} equals the platform-dependent string:') do |request_type, field_path, platform_values|
77
+ test_string_platform_values(request_type, field_path, platform_values)
78
+ end
79
+
80
+ # See `the error payload field {string} equals the platform-dependent string:`
81
+ #
82
+ # @step_input field_path [String] The field to test, prepended with "events.0"
83
+ # @step_input platform_values [DataTable] A table of acceptable values for each platform
84
+ Then('the event {string} equals the platform-dependent string:') do |field_path, platform_values|
85
+ test_string_platform_values('error', "events.0.#{field_path}", platform_values)
86
+ end
87
+
88
+ # Tests that the given payload value is correct for the target BrowserStack platform.
89
+ # This step will assume the expected and payload values are numeric.
90
+ # If the step is invoked when a remote BrowserStack device is not in use this step will fail.
91
+ #
92
+ # The DataTable used for this step should have `ios` and `android` in the same row as their expected value:
93
+ # | android | 1 |
94
+ # | ios | 5.5 |
95
+ #
96
+ # If the expected value is set to "@skip", the check should be skipped
97
+ # If the expected value is set to "@null", the check will be for null
98
+ # If the expected value is set to "@not_null", the check will be for a non-null value
99
+ #
100
+ # @step_input request_type [String] The type of request (error, session, build, etc)
101
+ # @step_input field_path [String] The field to test
102
+ # @step_input platform_values [DataTable] A table of acceptable values for each platform
103
+ Then('the {word} payload field {string} equals the platform-dependent numeric:') do |request_type, field_path, platform_values|
104
+ test_numeric_platform_values(request_type, field_path, platform_values)
105
+ end
106
+
107
+ # See `the payload field {string} equals the platform-dependent numeric:`
108
+ #
109
+ # @step_input field_path [String] The field to test, prepended with "events.0"
110
+ # @step_input platform_values [DataTable] A table of acceptable values for each platform
111
+ Then('the event {string} equals the platform-dependent numeric:') do |field_path, platform_values|
112
+ test_numeric_platform_values('error', "events.0.#{field_path}", platform_values)
113
+ end
114
+
115
+ # Tests that the given payload value is correct for the target BrowserStack platform.
116
+ # This step will assume the expected and payload values are booleans.
117
+ # If the step is invoked when a remote BrowserStack device is not in use this step will fail.
118
+ #
119
+ # The DataTable used for this step should have `ios` and `android` in the same row as their expected value:
120
+ # | android | 1 |
121
+ # | ios | 5 |
122
+ #
123
+ # If the expected value is set to "@skip", the check should be skipped
124
+ # If the expected value is set to "@null", the check will be for null
125
+ # If the expected value is set to "@not_null", the check will be for a non-null value
126
+ #
127
+ # @step_input request_type [String] The type of request (error, session, build, etc)
128
+ # @step_input field_path [String] The field to test
129
+ # @step_input platform_values [DataTable] A table of acceptable values for each platform
130
+ Then('the {word} payload field {string} equals the platform-dependent boolean:') do |request_type, field_path, platform_values|
131
+ test_boolean_platform_values(request_type, field_path, platform_values)
132
+ end
133
+
134
+ # See `the payload field {string} equals the platform-dependent boolean:`
135
+ #
136
+ # @step_input field_path [String] The field to test, prepended with "events.0"
137
+ # @step_input platform_values [DataTable] A table of acceptable values for each platform
138
+ Then('the event {string} equals the platform-dependent boolean:') do |field_path, platform_values|
139
+ test_boolean_platform_values('error', "events.0.#{field_path}", platform_values)
140
+ end
141
+
142
+ # See `the payload field {string} equals the platform-dependent string:`
143
+ #
144
+ # @step_input field_path [String] The field to test, prepended with "events.0.exceptions.0."
145
+ # @step_input platform_values [DataTable] A table of acceptable values for each platform
146
+ Then('the exception {string} equals the platform-dependent string:') do |field_path, platform_values|
147
+ test_string_platform_values('error', "events.0.exceptions.0.#{field_path}", platform_values)
148
+ end
149
+
150
+ # See `the payload field {string} equals the platform-dependent string:`
151
+ #
152
+ # @step_input field_path [String] The field to test, prepended with "events.0.exceptions.0.stacktrace.#!{num}"
153
+ # @step_input num [Integer] The index of the stack frame to test
154
+ # @step_input platform_values [DataTable] A table of acceptable values for each platform
155
+ Then('the {string} of stack frame {int} equals the platform-dependent string:') do |field_path, num, platform_values|
156
+ test_string_platform_values('error', "events.0.exceptions.0.stacktrace.#{num}.#{field_path}", platform_values)
157
+ end
158
+
159
+ # Sends keys to a given element, clearing it first
160
+ # Requires a running Appium driver
161
+ #
162
+ # @step_input keys [String] The keys to send to the element
163
+ # @step_input element_id [String] The locator id
164
+ When('I clear and send the keys {string} to the element {string}') do |keys, element_id|
165
+ Maze.driver.clear_and_send_keys_to_element(element_id, keys)
166
+ end
167
+
168
+ def get_expected_platform_value(platform_values)
169
+ os = Maze::Helper.get_current_platform
170
+ expected_value = Hash[platform_values.raw][os.downcase]
171
+ raise("There is no expected value for the current platform \"#{os}\"") if expected_value.nil?
172
+
173
+ expected_value
174
+ end
175
+
176
+ def should_skip_platform_check(expected_value)
177
+ expected_value.eql?('@skip')
178
+ end
179
+
180
+ def test_string_platform_values(request_type, field_path, platform_values)
181
+ expected_value = get_expected_platform_value(platform_values)
182
+ return if should_skip_platform_check(expected_value)
183
+
184
+ list = Maze::Server.list_for(request_type)
185
+ payload_value = Maze::Helper.read_key_path(list.current[:body], field_path)
186
+ assert_equal_with_nullability(expected_value, payload_value)
187
+ end
188
+
189
+ def test_boolean_platform_values(request_type, field_path, platform_values)
190
+ expected_value = get_expected_platform_value(platform_values)
191
+ return if should_skip_platform_check(expected_value)
192
+
193
+ expected_bool = case expected_value.downcase
194
+ when 'true'
195
+ true
196
+ when 'false'
197
+ false
198
+ else
199
+ expected_value
200
+ end
201
+ list = Maze::Server.list_for(request_type)
202
+ payload_value = Maze::Helper.read_key_path(list.current[:body], field_path)
203
+ assert_equal_with_nullability(expected_bool, payload_value)
204
+ end
205
+
206
+ def test_numeric_platform_values(request_type, field_path, platform_values)
207
+ expected_value = get_expected_platform_value(platform_values)
208
+ return if should_skip_platform_check(expected_value)
209
+
210
+ list = Maze::Server.list_for(request_type)
211
+ payload_value = Maze::Helper.read_key_path(list.current[:body], field_path)
212
+
213
+ # Need to do a little more processing here to allow floats
214
+ special_value = expected_value.eql?('@null') || expected_value.eql?('@not_null')
215
+ expectation = special_value ? expected_value : expected_value.to_f
216
+ assert_equal_with_nullability(expectation, payload_value)
217
+ end
218
+
219
+ def assert_equal_with_nullability(expected_value, payload_value)
220
+ case expected_value
221
+ when '@null'
222
+ Maze.check.nil(payload_value)
223
+ when '@not_null'
224
+ Maze.check.not_nil(payload_value)
225
+ else
226
+ Maze.check.equal(expected_value, payload_value)
227
+ end
228
+ end
@@ -0,0 +1,212 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../maze'
4
+ require_relative '../../maze/aws/sam'
5
+
6
+ # @!group AWS SAM steps
7
+
8
+ # Invoke a lambda directly with 'sam invoke'
9
+ #
10
+ # @step_input lambda_name [String] The name of the lambda to invoke
11
+ # @step_input directory [String] The directory to invoke the lambda in
12
+ Given('I invoke the {string} lambda in {string}') do |lambda_name, directory|
13
+ Maze::Aws::Sam.invoke(directory, lambda_name)
14
+ end
15
+
16
+ # Invoke a lambda directly with 'sam invoke' and the given event
17
+ #
18
+ # @step_input lambda_name [String] The name of the lambda to invoke
19
+ # @step_input directory [String] The directory to invoke the lambda in
20
+ # @step_input event_file [String] The event file to call the lambda with
21
+ Given('I invoke the {string} lambda in {string} with the {string} event') do |lambda_name, directory, event_file|
22
+ Maze::Aws::Sam.invoke(directory, lambda_name, event_file)
23
+ end
24
+
25
+ # Test the exit code of the SAM CLI process.
26
+ #
27
+ # @step_input expected [Integer] The expected exit code
28
+ Then('the SAM exit code equals {int}') do |expected|
29
+ Maze.check.equal(expected, Maze::Aws::Sam.last_exit_code)
30
+ end
31
+
32
+ # Test the Lambda response is empty but not-null. This indicates the Lambda did
33
+ # not respond but did run successfully
34
+ Then('the lambda response is empty') do
35
+ Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!')
36
+
37
+ Maze.check.equal({}, Maze::Aws::Sam.last_response)
38
+ end
39
+
40
+ # Test a Lambda response field equals the given string.
41
+ #
42
+ # @step_input key_path [String] The response element to test
43
+ # @step_input expected [String] The string to test against
44
+ Then('the lambda response {string} equals {string}') do |key_path, expected|
45
+ Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!')
46
+
47
+ actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path)
48
+
49
+ Maze.check.equal(expected, actual)
50
+ end
51
+
52
+ # Test a Lambda response field contains the given string.
53
+ #
54
+ # @step_input key_path [String] The response element to test
55
+ # @step_input expected [String] The string to test against
56
+ Then('the lambda response {string} contains {string}') do |key_path, expected|
57
+ Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!')
58
+
59
+ actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path)
60
+
61
+ Maze.check.include(actual, expected)
62
+ end
63
+
64
+ # Test a Lambda response field equals the given integer.
65
+ #
66
+ # @step_input key_path [String] The response element to test
67
+ # @step_input expected [Integer] The integer to test against
68
+ Then('the lambda response {string} equals {int}') do |key_path, expected|
69
+ Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!')
70
+
71
+ actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path)
72
+
73
+ Maze.check.equal(expected, actual)
74
+ end
75
+
76
+ # Test a Lambda response field is true.
77
+ #
78
+ # @step_input key_path [String] The response element to test
79
+ Then('the lambda response {string} is true') do |key_path|
80
+ Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!')
81
+
82
+ actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path)
83
+
84
+ Maze.check.true(actual)
85
+ end
86
+
87
+ # Test a Lambda response field is false.
88
+ #
89
+ # @step_input key_path [String] The response element to test
90
+ Then('the lambda response {string} is false') do |key_path|
91
+ Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!')
92
+
93
+ actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path)
94
+
95
+ Maze.check.false(actual)
96
+ end
97
+
98
+ # Test a Lambda response field is null.
99
+ #
100
+ # @step_input key_path [String] The response element to test
101
+ Then('the lambda response {string} is null') do |key_path|
102
+ Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!')
103
+
104
+ actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path)
105
+
106
+ Maze.check.nil(actual)
107
+ end
108
+
109
+ # Test a Lambda response field is not null.
110
+ #
111
+ # @step_input key_path [String] The response element to test
112
+ Then('the lambda response {string} is not null') do |key_path|
113
+ Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!')
114
+
115
+ actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path)
116
+
117
+ Maze.check.not_nil(actual)
118
+ end
119
+
120
+ # Test a Lambda response field is greater than the given integer.
121
+ #
122
+ # @step_input key_path [String] The response element to test
123
+ # @step_input expected [Integer] The integer to test against
124
+ Then('the lambda response {string} is greater than {int}') do |key_path, expected|
125
+ Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!')
126
+
127
+ actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path)
128
+
129
+ Maze.check.operator(actual, :>, expected)
130
+ end
131
+
132
+ # Test a Lambda response field is less than the given integer.
133
+ #
134
+ # @step_input key_path [String] The response element to test
135
+ # @step_input expected [Integer] The integer to test against
136
+ Then('the lambda response {string} is less than {int}') do |key_path, expected|
137
+ Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!')
138
+
139
+ actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path)
140
+
141
+ Maze.check.operator(actual, :<, expected)
142
+ end
143
+
144
+ # Test a Lambda response field starts with the given string.
145
+ #
146
+ # @step_input key_path [String] The response element to test
147
+ # @step_input expected [String] The string to test against
148
+ Then('the lambda response {string} starts with {string}') do |key_path, expected|
149
+ Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!')
150
+
151
+ actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path)
152
+
153
+ Maze.check.kind_of(String, actual)
154
+ Maze.check.true(
155
+ actual.start_with?(expected),
156
+ "Field '#{key_path}' value ('#{actual}') does not start with '#{expected}'"
157
+ )
158
+ end
159
+
160
+ # Test a Lambda response field ends with the given string.
161
+ #
162
+ # @step_input key_path [String] The response element to test
163
+ # @step_input expected [String] The string to test against
164
+ Then('the lambda response {string} ends with {string}') do |key_path, expected|
165
+ Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!')
166
+
167
+ actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path)
168
+
169
+ Maze.check.kind_of(String, actual)
170
+ Maze.check.true(
171
+ actual.end_with?(expected),
172
+ "Field '#{key_path}' value ('#{actual}') does not start with '#{expected}'"
173
+ )
174
+ end
175
+
176
+ # Test a Lambda response field is an array with a specific number of elements.
177
+ #
178
+ # @step_input key_path [String] The response element to test
179
+ # @step_input expected [Integer] The expected number of elements
180
+ Then('the lambda response {string} is an array with {int} element(s)') do |key_path, expected|
181
+ Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!')
182
+
183
+ actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path)
184
+
185
+ Maze.check.kind_of(Array, actual)
186
+ Maze.check.equal(expected, actual.length)
187
+ end
188
+
189
+ # Test a Lambda response field is an array with at least 1 element.
190
+ #
191
+ # @step_input key_path [String] The response element to test
192
+ Then('the lambda response {string} is a non-empty array') do |key_path|
193
+ Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!')
194
+
195
+ actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path)
196
+
197
+ Maze.check.kind_of(Array, actual)
198
+ Maze.check.false(actual.empty?)
199
+ end
200
+
201
+ # Test a Lambda response field matches the given regex.
202
+ #
203
+ # @step_input key_path [String] The response element to test
204
+ # @step_input expected [String] The regex to match against
205
+ Then('the lambda response {string} matches the regex {string}') do |key_path, regex|
206
+ expected = Regexp.new(regex)
207
+ Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!')
208
+
209
+ actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path)
210
+
211
+ Maze.check.match(expected, actual)
212
+ end
@@ -0,0 +1,80 @@
1
+ # @!group Breadcrumb steps
2
+
3
+ # Tests whether the first event entry contains the specified number of breadcrumbs.
4
+ #
5
+ # @step_input expected [Integer] The expected number of breadcrumbs
6
+ Then("the event has {int} breadcrumb(s)") do |expected|
7
+ breadcrumbs = Maze::Server.errors.current[:body]['events'].first['breadcrumbs']
8
+
9
+ Maze.check.equal(
10
+ expected,
11
+ breadcrumbs&.length || 0,
12
+ "Expected event to have '#{expected}' breadcrumbs, but got: #{breadcrumbs}"
13
+ )
14
+ end
15
+
16
+ # Tests whether the first event entry contains no breadcrumbs.
17
+ Then("the event has no breadcrumbs") do
18
+ breadcrumbs = Maze::Server.errors.current[:body]['events'].first['breadcrumbs']
19
+
20
+ Maze.check.true(
21
+ # some notifiers may omit breadcrumbs entirely when empty, otherwise it should
22
+ # be an empty array
23
+ breadcrumbs.nil? || breadcrumbs.empty?,
24
+ "Expected event not to have breadcrumbs, but got: #{breadcrumbs}"
25
+ )
26
+ end
27
+
28
+ # Tests whether the first event entry contains a specific breadcrumb with a type and name.
29
+ #
30
+ # @step_input type [String] The expected breadcrumb's type
31
+ # @step_input name [String] The expected breadcrumb's name
32
+ Then('the event has a {string} breadcrumb named {string}') do |type, name|
33
+ breadcrumbs = Maze::Server.errors.current[:body]['events'].first['breadcrumbs']
34
+
35
+ Maze.check.true(
36
+ breadcrumbs.any? { |crumb| crumb['type'] == type && crumb['name'] == name },
37
+ "Expected event to have a breadcrumb with type '#{type}' and name '#{name}', but got: #{breadcrumbs}"
38
+ )
39
+ end
40
+
41
+ # Tests whether the first event entry contains a specific breadcrumb with a type and message.
42
+ #
43
+ # @step_input type [String] The expected breadcrumb's type
44
+ # @step_input message [String] The expected breadcrumb's message
45
+ Then('the event has a {string} breadcrumb with message {string}') do |type, message|
46
+ value = Maze::Server.errors.current[:body]['events'].first['breadcrumbs']
47
+ found = value.any? { |crumb| crumb['type'] == type && crumb['metaData'] && crumb['metaData']['message'] == message }
48
+ raise("No breadcrumb matched: #{value}") unless found
49
+ end
50
+
51
+ # Tests whether the first event entry does not contain a breadcrumb with a specific type.
52
+ # Used for confirming filtering of breadcrumbs
53
+ #
54
+ # @step_input type [String] The type of breadcrumb expected to not be present
55
+ Then('the event does not have a {string} breadcrumb') do |type|
56
+ value = Maze::Server.errors.current[:body]['events'].first['breadcrumbs']
57
+ found = value.any? { |crumb| crumb['type'] == type }
58
+ raise("Breadcrumb with type: #{type} matched") if found
59
+ end
60
+
61
+ # Test whether the first event entry does not contain a breadcrumb with a specific type and message.
62
+ # Used for confirming filtering of breadcrumbs
63
+ #
64
+ # @step_input type [String] The type of the breadcrumb expected to be absent
65
+ # @step_input message [String] The message of the breadcrumb expected to be absent
66
+ Then('the event does not have a {string} breadcrumb with message {string}') do |type, message|
67
+ value = Maze::Server.errors.current[:body]['events'].first['breadcrumbs']
68
+ found = value.any? { |crumb| crumb['type'] == type && crumb['metaData'] && crumb['metaData']['message'] == message }
69
+ raise("Breadcrumb with type: #{type} and message: #{message} matched") if found
70
+ end
71
+
72
+ # Tests whether any breadcrumb matches a given JSON fixture. This follows all the usual rules for JSON fixture matching.
73
+ #
74
+ # @step_input json_fixture [String] A path to the JSON fixture to compare against
75
+ Then('the event contains a breadcrumb matching the JSON fixture in {string}') do |json_fixture|
76
+ breadcrumbs = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], 'events.0.breadcrumbs')
77
+ expected = JSON.parse(open(json_fixture, &:read))
78
+ match = breadcrumbs.any? { |breadcrumb| Maze::Compare.value(expected, breadcrumb).equal? }
79
+ Maze.check.true(match, 'No breadcrumbs in the event matched the given breadcrumb')
80
+ end
@@ -0,0 +1,93 @@
1
+ # @!group Browser steps
2
+
3
+ When('I navigate to the URL {string}') do |path|
4
+ begin
5
+ $logger.debug "Navigating to: #{path}"
6
+ Maze.driver.navigate.to path
7
+ rescue => exception
8
+ $logger.error("#{exception.class} occurred during navigation attempt with message: #{exception.message}")
9
+ $logger.error("Restarting driver and retrying navigation to: #{path}")
10
+ Maze.driver.restart_driver
11
+ Maze.driver.navigate.to path
12
+ # If a further error occurs it will get thrown as normal
13
+ end
14
+ end
15
+
16
+ Then(/^the error is a valid browser payload for the error reporting API$/) do
17
+ if !/^ie_(8|9|10)$/.match(Maze.config.browser)
18
+ steps %(
19
+ Then the error "Bugsnag-API-Key" header is not null
20
+ And the error "Content-Type" header equals one of:
21
+ | application/json |
22
+ | application/json; charset=UTF-8 |
23
+ And the error "Bugsnag-Payload-Version" header equals "4"
24
+ And the error "Bugsnag-Sent-At" header is a timestamp
25
+ )
26
+ else
27
+ steps %(
28
+ Then the error "apiKey" query parameter is not null
29
+ And the error "payloadVersion" query parameter equals "4"
30
+ And the error "sentAt" query parameter is a timestamp
31
+ )
32
+ end
33
+ steps %(
34
+ And the error payload field "notifier.name" is not null
35
+ And the error payload field "notifier.url" is not null
36
+ And the error payload field "notifier.version" is not null
37
+ And the error payload field "events" is a non-empty array
38
+
39
+ And each element in error payload field "events" has "severity"
40
+ And each element in error payload field "events" has "severityReason.type"
41
+ And each element in error payload field "events" has "unhandled"
42
+ And each element in error payload field "events" has "exceptions"
43
+
44
+ And the exception "type" equals "browserjs"
45
+ )
46
+ end
47
+
48
+ Then('the session is a valid browser payload for the session tracking API') do
49
+ if !/^ie_(8|9|10)$/.match(Maze.config.browser)
50
+ steps %(
51
+ Then the session "Bugsnag-API-Key" header is not null
52
+ And the session "Content-Type" header equals one of:
53
+ | application/json |
54
+ | application/json; charset=UTF-8 |
55
+ And the session "Bugsnag-Payload-Version" header equals "1"
56
+ And the session "Bugsnag-Sent-At" header is a timestamp
57
+ )
58
+ else
59
+ steps %(
60
+ Then the session "apiKey" query parameter is not null
61
+ And the session "payloadVersion" query parameter equals "1"
62
+ And the session "sentAt" query parameter is a timestamp
63
+ )
64
+ end
65
+ steps %(
66
+ And the session payload field "app" is not null
67
+ And the session payload field "device" is not null
68
+ And the session payload field "notifier.name" is not null
69
+ And the session payload field "notifier.url" is not null
70
+ And the session payload field "notifier.version" is not null
71
+ And the session payload has a valid sessions array
72
+ )
73
+ end
74
+
75
+ Then('the event device ID is valid') do
76
+ if Maze.driver.local_storage?
77
+ step('the event "device.id" matches "^c[a-z0-9]{20,32}$"')
78
+ else
79
+ $logger.info('Local storage is not supported in this browser, assuming device ID is null')
80
+ step('the event "device.id" is null')
81
+ end
82
+ end
83
+
84
+ Then('the event device ID is {string}') do |expected_id|
85
+ if Maze.driver.local_storage?
86
+ step("the event \"device.id\" equals \"#{expected_id}\"")
87
+ else
88
+ $logger.info('Local storage is not supported in this browser, assuming device ID is null')
89
+ step('the event "device.id" is null')
90
+ end
91
+ end
92
+
93
+ # @!endgroup
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @!group Build API Steps
4
+
5
+ # Tests whether the top-most payload is valid for the Bugsnag build API
6
+ # APIKey fields and headers are tested against the '$api_key' global variable
7
+ Then('the build is valid for the Build API') do
8
+ steps %(
9
+ And the build payload field "apiKey" equals "#{$api_key}"
10
+ And the build payload field "appVersion" is not null
11
+ )
12
+ end
13
+
14
+ # Tests whether the top-most payload is valid for the Android mapping API
15
+ # APIKey fields and headers are tested against the '$api_key' global variable
16
+ Then('the build is valid for the Android Mapping API') do
17
+ steps %(
18
+ And the build payload field "apiKey" equals "#{$api_key}"
19
+ And the build payload field "proguard" is not null
20
+ And the build payload field "appId" is not null
21
+ And the build payload field "versionCode" is not null
22
+ And the build payload field "buildUUID" is not null
23
+ And the build payload field "versionName" is not null
24
+ )
25
+ end
@@ -0,0 +1,7 @@
1
+ # @!group Document steps
2
+
3
+ # Starts the document server manually. It will be stopped automatically at the end of each scenario
4
+ # (if started in this way).
5
+ When('I start the document server') do
6
+ Maze::DocumentServer.manual_start
7
+ end