Tests that a log request matches a given level and message.
9 10 11 12 13 14 15 |
Then('the {string} level log message equals {string}') do |log_level, | request = Maze::Server.logs.current Maze.check.not_nil(request, 'No log message to check') log = request[:body] Maze.check.equal(log_level, Maze::Helper.read_key_path(log, 'level')) Maze.check.equal(, Maze::Helper.read_key_path(log, 'message')) end |
Tests that a log request matches a given level and message regex.
21 22 23 24 25 26 27 28 29 |
Then('the {string} level log message matches the regex {string}') do |log_level, | request = Maze::Server.logs.current Maze.check.not_nil(request, 'No log message to check') log = request[:body] Maze.check.equal(log_level, Maze::Helper.read_key_path(log, 'level')) regex = Regexp.new() Maze.check.match(regex, Maze::Helper.read_key_path(log, 'message')) end |
Starts an HTTP proxy server.
5 6 7 |
Then('I start an http proxy') do Maze::Proxy.instance.start :Http end |
Starts an authenticated HTTP proxy server.
11 12 13 |
Then('I start an authenticated http proxy') do Maze::Proxy.instance.start :Http, true end |
Starts an HTTPS proxy server.
17 18 19 |
Then('I start an https proxy') do Maze::Proxy.instance.start :Https end |
Starts an authenticated HTTPS proxy server.
23 24 25 |
Then('I start an authenticated https proxy') do Maze::Proxy.instance.start :Https, true end |
Test the proxy server handled a request for a host.
30 31 32 |
Then('the proxy handled a request for {string}') do |host| Maze.check.true(Maze::Proxy.instance.handled_host?(host), "The proxy did not handle a request for #{host}") end |
Disable timestamp validation for spans
5 6 7 |
When('I disable timestamp validation for spans') do Maze.config. = false end |
Waits for a given number of spans to be received, which may be spread across one or more trace requests.
12 13 14 |
Then('I wait to receive {int} span(s)') do |span_count| assert_received_span_count Maze::Server.list_for('traces'), span_count end |
Waits for a minimum number of spans to be received, which may be spread across one or more trace requests. If more spans than requested are received, this step will still pass.
20 21 22 |
Then('I wait to receive at least {int} span(s)') do |span_min| assert_received_minimum_span_count Maze::Server.list_for('traces'), span_min end |
Waits for a minimum number of spans to be received, which may be spread across one or more trace requests. If more spans than the maximum requested number of spans are received, this step will fail.
29 30 31 |
Then('I wait to receive between {int} and {int} span(s)') do |span_min, span_max| assert_received_ranged_span_count Maze::Server.list_for('traces'), span_min, span_max end |
33 34 35 36 |
Then('I should have received no spans') do sleep Maze.config.receive_no_requests_wait Maze.check.equal spans_from_request_list(Maze::Server.list_for('traces')).size, 0 end |
38 39 40 |
Then('I enter unmanaged traces mode') do Maze.config.unmanaged_traces_mode = true end |
42 43 44 |
Then('the trace payload field {string} bool attribute {string} is true') do |field, attribute| check_attribute_equal field, attribute, 'boolValue', true end |
46 47 48 |
Then('the trace payload field {string} bool attribute {string} is false') do |field, attribute| check_attribute_equal field, attribute, 'boolValue', false end |
50 51 52 |
Then('the trace payload field {string} integer attribute {string} equals {int}') do |field, attribute, expected| check_attribute_equal field, attribute, 'intValue', expected end |
54 55 56 57 58 |
Then('the trace payload field {string} integer attribute {string} is greater than {int}') do |field, attribute, expected| value = get_attribute_value field, attribute, 'intValue' Maze.check.operator value, :>, expected, "The payload field '#{field}' attribute '#{attribute}' (#{value}) is not greater than '#{expected}'" end |
60 61 62 |
Then('the trace payload field {string} string attribute {string} equals {string}') do |field, attribute, expected| check_attribute_equal field, attribute, 'stringValue', expected end |
64 65 66 67 68 69 |
Then('the trace payload field {string} string attribute {string} equals the stored value {string}') do |field, attribute, stored_key| value = get_attribute_value field, attribute, 'stringValue' stored = Maze::Store.values[stored_key] result = Maze::Compare.value value, stored Maze.check.true result.equal?, "Payload value: #{value} does not equal stored value: #{stored}" end |
71 72 73 74 75 |
Then('the trace payload field {string} string attribute {string} matches the regex {string}') do |field, attribute, pattern| value = get_attribute_value field, attribute, 'stringValue' regex = Regexp.new pattern Maze.check.match regex, value end |
77 78 79 80 81 82 83 84 |
Then('the trace payload field {string} integer attribute {string} matches the regex {string}') do |field, attribute, pattern| regex = Regexp.new(pattern) list = Maze::Server.traces attributes = Maze::Helper.read_key_path(list.current[:body], "#{field}.attributes") attribute = attributes.find { |a| a['key'] == attribute } value = attribute["value"]["intValue"] Maze.check.match(regex, value) end |
86 87 88 89 |
Then('the trace payload field {string} string attribute {string} exists') do |field, attribute| value = get_attribute_value field, attribute, 'stringValue' Maze.check.not_nil value end |
91 92 93 94 95 96 97 98 99 |
Then('the trace payload field {string} string attribute {string} is one of:') do |field, key, possible_values| list = Maze::Server.traces attributes = Maze::Helper.read_key_path(list.current[:body], "#{field}.attributes") attribute = attributes.find { |a| a['key'] == key } possible_attributes = possible_values.raw.flatten.map { |v| { 'key' => key, 'value' => { 'stringValue' => v } } } Maze.check.not_nil(attribute, "The attribute #{key} is nil") Maze.check.include(possible_attributes, attribute) end |
101 102 103 |
Then('the trace payload field {string} boolean attribute {string} is true') do |field, key| assert_attribute field, key, { 'boolValue' => true } end |
105 106 107 |
Then('the trace payload field {string} boolean attribute {string} is false') do |field, key| assert_attribute field, key, { 'boolValue' => false } end |
109 110 111 |
Then('the trace payload field {string} double attribute {string} equals {float}') do |field, attribute, expected| check_attribute_equal field, attribute, 'doubleValue', expected end |
114 115 116 117 118 |
Then('a span {word} equals {string}') do |attribute, expected| spans = spans_from_request_list(Maze::Server.list_for('traces')) selected_attributes = spans.map { |span| span[attribute] } Maze.check.includes selected_attributes, expected end |
120 121 122 123 124 |
Then('every span field {string} equals {string}') do |key, expected| spans = spans_from_request_list(Maze::Server.list_for('traces')) selected_keys = spans.map { |span| span[key] == expected } Maze.check.not_includes selected_keys, false end |
126 127 128 129 |
Then('every span field {string} matches the regex {string}') do |key, pattern| spans = spans_from_request_list(Maze::Server.list_for('traces')) spans.map { |span| Maze.check.match pattern, span[key] } end |
131 132 133 134 |
Then('every span string attribute {string} exists') do |attribute| spans = spans_from_request_list(Maze::Server.list_for('traces')) spans.map { |span| Maze.check.not_nil span['attributes'].find { |a| a['key'] == attribute }['value']['stringValue'] } end |
136 137 138 139 |
Then('every span string attribute {string} equals {string}') do |attribute, expected| spans = spans_from_request_list(Maze::Server.list_for('traces')) spans.map { |span| Maze.check.equal expected, span['attributes'].find { |a| a['key'] == attribute }['value']['stringValue'] } end |
141 142 143 144 |
Then('every span string attribute {string} matches the regex {string}') do |attribute, pattern| spans = spans_from_request_list(Maze::Server.list_for('traces')) spans.map { |span| Maze.check.match pattern, span['attributes'].find { |a| a['key'] == attribute }['value']['stringValue'] } end |
146 147 148 149 |
Then('every span integer attribute {string} is greater than {int}') do |attribute, expected| spans = spans_from_request_list(Maze::Server.list_for('traces')) spans.map { |span| Maze::check.true span['attributes'].find { |a| a['key'] == attribute }['value']['intValue'].to_i > expected } end |
151 152 153 154 |
Then('every span bool attribute {string} is true') do |attribute| spans = spans_from_request_list(Maze::Server.list_for('traces')) spans.map { |span| Maze::check.true span['attributes'].find { |a| a['key'] == attribute }['value']['boolValue'] } end |
156 157 158 159 160 |
Then('a span string attribute {string} exists') do |attribute| spans = spans_from_request_list(Maze::Server.list_for('traces')) selected_attributes = spans.map { |span| span['attributes'].find { |a| a['key'].eql?(attribute) && a['value'].has_key?('stringValue') } }.compact Maze.check.false(selected_attributes.empty?) end |
162 163 164 165 166 167 |
Then('a span string attribute {string} equals {string}') do |attribute, expected| spans = spans_from_request_list(Maze::Server.list_for('traces')) selected_attributes = spans.map { |span| span['attributes'].find { |a| a['key'].eql?(attribute) && a['value'].has_key?('stringValue') } }.compact attribute_values = selected_attributes.map { |a| a['value']['stringValue'] } Maze.check.includes attribute_values, expected end |
169 170 171 172 173 |
Then('a span field {string} equals {string}') do |key, expected| spans = spans_from_request_list(Maze::Server.list_for('traces')) selected_keys = spans.map { |span| span[key] } Maze.check.includes selected_keys, expected end |
175 176 177 178 179 |
Then('a span field {string} equals {int}') do |key, expected| spans = spans_from_request_list(Maze::Server.list_for('traces')) selected_keys = spans.map { |span| span[key] } Maze.check.includes selected_keys, expected end |
181 182 183 184 185 186 187 |
Then('a span field {string} matches the regex {string}') do |attribute, pattern| regex = Regexp.new pattern spans = spans_from_request_list(Maze::Server.list_for('traces')) selected_attributes = spans.select { |span| regex.match? span[attribute] } Maze.check.false(selected_attributes.empty?) end |
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
Then('a span named {string} contains the attributes:') do |span_name, table| spans = spans_from_request_list(Maze::Server.list_for('traces')) named_spans = spans.find_all { |span| span['name'].eql?(span_name) } raise Test::Unit::AssertionFailedError.new "No spans were found with the name #{span_name}" if named_spans.empty? expected_attributes = table.hashes match = false named_spans.each do |span| matches = expected_attributes.map do |expected_attribute| span['attributes'].find_all { |attribute| attribute['key'].eql?(expected_attribute['attribute']) } .any? { |attribute| attribute_value_matches?(attribute['value'], expected_attribute['type'], expected_attribute['value']) } end if matches.all? && !matches.empty? match = true break end end unless match raise Test::Unit::AssertionFailedError.new "No spans were found containing all of the given attributes" end end |
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
Then('a span named {string} has a parent named {string}') do |child_name, parent_name| spans = spans_from_request_list(Maze::Server.list_for('traces')) child_spans = spans.find_all { |span| span['name'].eql?(child_name) } raise Test::Unit::AssertionFailedError.new "No spans were found with the name #{child_name}" if child_spans.empty? parent_spans = spans.find_all { |span| span['name'].eql?(parent_name) } raise Test::Unit::AssertionFailedError.new "No spans were found with the name #{parent_name}" if parent_spans.empty? expected_parent_ids = child_spans.map { |span| span['parentSpanId'] } parent_ids = parent_spans.map { |span| span['spanId'] } match = expected_parent_ids.any? { |expected_id| parent_ids.include?(expected_id) } unless match raise Test::Unit::AssertionFailedError.new "No child span named #{child_name} was found with a parent named #{parent_name}" end end |
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
Then('a span named {string} has the following properties:') do |span_name, table| spans = spans_from_request_list(Maze::Server.list_for('traces')) found_spans = spans.find_all { |span| span['name'].eql?(span_name) } raise Test::Unit::AssertionFailedError.new "No spans were found with the name #{span_name}" if found_spans.empty? expected_properties = table.hashes match = false found_spans.each do |span| matches = expected_properties.map do |expected_property| property = Maze::Helper.read_key_path(span, expected_property['property']) expected_property['value'].eql?(property.to_s) end if matches.all? && !matches.empty? match = true break end end unless match raise Test::Unit::AssertionFailedError.new "No spans were found containing all of the given properties" end end |
365 366 367 368 |
Then('the trace payload field {string} string array attribute {string} equals the array:') do |field, attribute, expected_values| expected_values_list = expected_values.raw.flatten.map { |v| { 'stringValue' => v } } check_array_attribute_equal field, attribute, expected_values_list end |
370 371 372 373 |
Then('the trace payload field {string} integer array attribute {string} equals the array:') do |field, attribute, expected_values| expected_values_list = expected_values.raw.flatten.map { |v| { 'intValue' => v.to_i } } check_array_attribute_equal(field, attribute, expected_values_list) end |
375 376 377 378 |
Then('the trace payload field {string} double array attribute {string} equals the array:') do |field, attribute, expected_values| expected_values_list = expected_values.raw.flatten.map { |v| { 'doubleValue' => v.to_f } } check_array_attribute_equal field, attribute, expected_values_list end |
380 381 382 383 |
Then('the trace payload field {string} boolean array attribute {string} equals the array:') do |field, attribute, expected_values| expected_values_list = expected_values.raw.flatten.map { |v| { 'boolValue' => v == 'true' } } check_array_attribute_equal field, attribute, expected_values_list end |
Stores a payload value against a key for cross-request comparisons.
10 11 12 13 14 |
Then('the {request_type} payload field {string} is stored as the value {string}') do |request_type, field, key| list = Maze::Server.list_for request_type value = Maze::Helper.read_key_path(list.current[:body], field) Maze::Store.values[key] = value.dup end |
Tests whether a payload field matches a previously stored payload value
21 22 23 24 25 26 27 |
Then('the {request_type} payload field {string} equals the stored value {string}') do |request_type, field, key| list = Maze::Server.list_for request_type payload_value = Maze::Helper.read_key_path(list.current[:body], field) stored_value = Maze::Store.values[key] result = Maze::Compare.value(payload_value, stored_value) Maze.check.true(result.equal?, "Payload value: #{payload_value} does not equal stored value: #{stored_value}") end |
Tests whether a payload field is distinct from a previously stored payload value
34 35 36 37 38 39 40 |
Then('the {request_type} payload field {string} does not equal the stored value {string}') do |request_type, field, key| list = Maze::Server.list_for request_type payload_value = Maze::Helper.read_key_path(list.current[:body], field) stored_value = Maze::Store.values[key] result = Maze::Compare.value(payload_value, stored_value) Maze.check.false(result.equal?, "Payload value: #{payload_value} equals stored value: #{stored_value}") end |
Tests whether a payload field matches a previously stored payload value, ignoring case
47 48 49 50 51 52 53 54 55 |
Then('the {request_type} payload field {string} equals the stored value {string} ignoring case') do |request_type, field, key| list = Maze::Server.list_for request_type payload_value = Maze::Helper.read_key_path(list.current[:body], field) stored_value = Maze::Store.values[key] payload_value.downcase! stored_value.downcase! result = Maze::Compare.value(payload_value, stored_value) Maze.check.true(result.equal?, "Payload value: #{payload_value} does not equal stored value: #{stored_value}") end |
Tests whether a payload field is distinct from a previously stored payload value, ignoring case
62 63 64 65 66 67 68 69 70 |
Then('the {request_type} payload field {string} does not equal the stored value {string} ignoring case') do |request_type, field, key| list = Maze::Server.list_for request_type payload_value = Maze::Helper.read_key_path(list.current[:body], field) stored_value = Maze::Store.values[key] payload_value.downcase! stored_value.downcase! result = Maze::Compare.value(payload_value, stored_value) Maze.check.false(result.equal?, "Payload value: #{payload_value} equals stored value: #{stored_value}") end |
Tests whether a payload field is a number (Numeric according to Ruby)
76 77 78 79 80 |
Then('the {request_type} payload field {string} is a number') do |request_type, field| list = Maze::Server.list_for request_type value = Maze::Helper.read_key_path(list.current[:body], field) Maze.check.kind_of Numeric, value end |
Tests whether a payload field is an integer (Integer according to Ruby)
86 87 88 89 90 |
Then('the {request_type} payload field {string} is an integer') do |request_type, field| list = Maze::Server.list_for request_type value = Maze::Helper.read_key_path(list.current[:body], field) Maze.check.kind_of Integer, value end |
Tests whether a payload field is a date (parseable as a Date, according to Ruby)
96 97 98 99 100 101 102 103 104 105 |
Then('the {request_type} payload field {string} is a date') do |request_type, field| list = Maze::Server.list_for request_type value = Maze::Helper.read_key_path(list.current[:body], field) date = begin Date.parse(value) rescue StandardError nil end Maze.check.kind_of Date, date end |
Tests whether a payload field (loosely) matches a UUID regex (/[a-fA-F0-9-]{36}/)
111 112 113 114 115 116 117 |
Then('the {request_type} payload field {string} is a UUID') do |request_type, field| list = Maze::Server.list_for request_type value = Maze::Helper.read_key_path(list.current[:body], field) Maze.check.not_nil(value, "Expected UUID, got nil for #{field}") match = /[a-fA-F0-9-]{36}/.match(value).size > 0 Maze.check.true(match, "Field #{field} is not a UUID, received #{value}") end |
Tests that a request header is present
9 10 11 12 13 14 15 16 |
Then('the {request_type} {string} header is present') do |request_type, header_name| Maze.check.not_nil(Maze::Server.list_for(request_type).current[:request][header_name], "The #{request_type} '#{header_name}' header should not be null") request = Maze::Server.list_for(request_type).current[:request] Maze.check.true(request.header.key?(header_name.downcase), "The #{request_type} '#{header_name}' header should be present") end |
Tests that a request header is not present
22 23 24 25 26 27 |
Then('the {request_type} {string} header is not present') do |request_type, header_name| request = Maze::Server.list_for(request_type).current[:request] Maze.check.false(request.header.key?(header_name.downcase), "The #{request_type} '#{header_name}' header should not be present") end |
Tests that request header equals a string
34 35 36 37 38 |
Then('the {request_type} {string} header equals {string}') do |request_type, header_name, header_value| Maze.check.not_nil(Maze::Server.list_for(request_type).current[:request][header_name], "The #{request_type} '#{header_name}' header wasn't present in the request") Maze.check.equal(header_value, Maze::Server.list_for(request_type).current[:request][header_name]) end |
Tests that a request header matches a regex
45 46 47 48 49 |
Then('the {request_type} {string} header matches the regex {string}') do |request_type, header_name, regex_string| regex = Regexp.new(regex_string) value = Maze::Server.list_for(request_type).current[:request][header_name] Maze.check.match(regex, value) end |
Tests that a request header matches one of a list of strings
56 57 58 |
Then('the {request_type} {string} header equals one of:') do |request_type, header_name, header_values| Maze.check.include(header_values.raw.flatten, Maze::Server.list_for(request_type).current[:request][header_name]) end |
Tests that a request header is a timestamp.
64 65 66 67 |
Then('the {request_type} {string} header is a timestamp') do |request_type, header_name| header = Maze::Server.list_for(request_type).current[:request][header_name] Maze.check.match(TIMESTAMP_REGEX, header) end |
Checks that the Bugsnag-Integrity header is a SHA1 or simple digest
72 73 74 75 |
When('the {request_type} Bugsnag-Integrity header is valid') do |request_type| Maze.check.true(Maze::Helper.valid_bugsnag_integrity_header(Maze::Server.list_for(request_type).current), 'Invalid Bugsnag-Integrity header detected') end |
Sets an environment variable for subsequent scripts or commands.
10 11 12 |
When('I set environment variable {string} to {string}') do |key, value| Maze::Runner.environment[key] = value end |
Sets an environment variable to a given endpoint.
18 19 20 21 22 |
When('I store the {word} endpoint in the environment variable {string}') do |endpoint, name| steps %( When I set environment variable "#{name}" to "http://maze-runner:#{Maze.config.port}/#{endpoint}" ) end |
Sets an environment variable to the currently set API key.
27 28 29 30 31 |
When('I store the api key in the environment variable {string}') do |name| steps %Q{ When I set environment variable "#{name}" to "#{$api_key}" } end |
Runs a script, blocking until it returns.
36 37 38 |
When('I run the script {string} synchronously') do |script_path| Maze::Runner.run_script(script_path, blocking: true) end |
Runs a script with a given interpreter, blocking until it returns.
44 45 46 |
When('I run the script {string} using {word} synchronously') do |script_path, command| Maze::Runner.run_script(script_path, blocking: true, command: command) end |
Runs a script.
51 52 53 |
When('I run the script {string}') do |script_path| Maze::Runner.run_script script_path end |
Starts a docker-compose service.
58 59 60 |
When('I start the service {string}') do |service| Maze::Docker.start_service service end |
Runs a docker-compose service using a specific command.
66 67 68 |
When('I run the service {string} with the command {string}') do |service, command| Maze::Docker.start_service(service, command: command) end |
Runs a docker-compose service in an interactive CLI.
73 74 75 76 77 78 |
When('I run the service {string} interactively') do |service| # Stop the old session if one exists step('I stop the current shell') if Maze::Runner.interactive_session? Maze::Docker.start_service(service, interactive: true) end |
Runs a docker-compose service using a specific command in an interactive CLI.
84 85 86 87 88 89 |
When('I run the service {string} with the command {string} interactively') do |service, command| # Stop the old session if one exists step('I stop the current shell') if Maze::Runner.interactive_session? Maze::Docker.start_service(service, command: command, interactive: true) end |
Runs a docker-compose service using a specific command provided as a Gherkin multi-line string.
95 96 97 98 |
When('I run the service {string} with the command') do |service, command| one_line_cmd = command.gsub("\n", ' ').gsub(/ +/, ' ') Maze::Docker.start_service(service, command: one_line_cmd) end |
Executes a command in the given docker compose service.
The service must already be running for this to succeed as it uses 'docker compose exec'.
107 108 109 |
When('I execute the command {string} in the service {string}') do |command, service| Maze::Docker.exec(service, command) end |
Executes a command in the given docker compose service in the background.
The service must already be running for this to succeed as it uses 'docker compose exec --detach'.
118 119 120 |
When('I execute the command {string} in the service {string} in the background') do |command, service| Maze::Docker.exec(service, command, detach: true) end |
Allows validation of the last exit code of the last run docker-compose command. Will fail if no commands have been run.
126 127 128 129 130 131 132 |
Then('the exit code of the last docker command was {int}') do |expected_code| wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait) success = wait.until { !Maze::Docker.last_exit_code.nil? } Maze.check.true(success, 'No docker exit code available to verify') Maze.check.equal(Maze::Docker.last_exit_code, expected_code) end |
A shortcut for the above assuming 0 as a successful exit code Will fail if no commands have been run
136 137 138 |
Then('the last run docker command exited successfully') do step('the exit code of the last docker command was 0') end |
Allows testing that the last exit code was not 0 Will fail if no commands have been run
142 143 144 145 146 147 148 |
Then('the last run docker command did not exit successfully') do wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait) success = wait.until { !Maze::Docker.last_exit_code.nil? } Maze.check.true(success, 'No docker exit code available to verify') Maze.check.not_equal(Maze::Docker.last_exit_code, 0) end |
Allows testing a docker command output a specific string Will fail if no commands have been run
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
Then('the last run docker command output {string}') do |expected_string| wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait) success = wait.until { !Maze::Docker.last_command_logs.nil? } Maze.check.true(success, 'No docker logs available to verify') docker_output = Maze::Docker.last_command_logs output_included = docker_output.any? { |line| line.include?(expected_string) } Maze.check.true(output_included, %( No line of output included '#{expected_string}'. Full output: #{docker_output.join('\n')} )) end |
Waits for a number of seconds, performing no actions.
173 174 175 176 |
When('I wait for {int} second(s)') do |seconds| $logger.warn 'Sleep was used! Please avoid using sleep in tests!' sleep(seconds) end |
Starts an interactive shell
179 180 181 182 183 184 |
When('I start a new shell') do # Stop the old session if one exists step('I stop the current shell') if Maze::Runner.interactive_session? Maze::Runner.start_interactive_session end |
Stops currently running interactive shell
187 188 189 190 191 192 193 |
When('I stop the current shell') do shell = Maze::Runner.interactive_session result = Maze::Runner.stop_interactive_session Maze.check.true(result, 'The shell is still running when it should have exited') Maze.check.false(shell.running?, 'The shell is still running when it should have exited') end |
Run a command on the shell
198 199 200 201 202 |
When('I input {string} interactively') do |command| current_shell = Maze::Runner.interactive_session success = current_shell.run_command(command) Maze.check.true(success, 'The terminal had already closed') end |
Send a return or enter to the interactive session
205 206 207 |
When('I input a return interactively') do step('I input "" interactively') end |
Assert the current stdout line in the shell exactly matches the given string
212 213 214 215 |
Then('the current stdout line is {string}') do |expected| current_shell = Maze::Runner.interactive_session Maze.check.equal(expected, current_shell.current_buffer) end |
Assert the current stdout line in the shell includes the given string
220 221 222 223 |
Then('the current stdout line contains {string}') do |expected| current_shell = Maze::Runner.interactive_session Maze.check.include(current_shell.current_buffer, expected) end |
Waits for a line matching a regex to be present in the current stdout Times out after Maze.config.receive_requests_wait seconds.
229 230 231 232 233 234 235 236 |
Then('I wait for the current stdout line to match the regex {string}') do |regex| wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait) shell = Maze::Runner.interactive_session success = wait.until { shell.current_buffer.match?(regex) } Maze.check.true(success, "The current output line \"#{shell.current_buffer}\" did not match \"#{regex}\"") end |
Waits for a specific shell prompt to be present in the buffered stdout line, timing out after Maze.config.receive_requests_wait seconds.
242 243 244 245 246 247 248 249 |
Then('I wait for the shell prompt {string}') do |expected_prompt| wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait) shell = Maze::Runner.interactive_session success = wait.until { expected_prompt == sanitized(shell.current_buffer) } Maze.check.true(success, "The current output line \"#{shell.current_buffer}\" did not match \"#{expected_prompt}\"") end |
Verify a string appears in the stdout logs
254 255 256 257 258 |
Then('the shell has output {string} to stdout') do |expected_line| current_shell = Maze::Runner.interactive_session match = current_shell.stdout_lines.any? { |line| line == expected_line } Maze.check.true(match, "No output lines from #{current_shell.stdout_lines} matched #{expected_line}") end |
Wait for a string to appear in the stdout logs, timing out after Maze.config.receive_requests_wait seconds.
263 264 265 266 267 268 269 270 271 272 273 274 275 |
Then('I wait for the shell to output {string} to stdout') do |expected_line| wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait) current_shell = Maze::Runner.interactive_session success = wait.until do current_shell.stdout_lines.any? do |line| # Remove inconsequential escape codes expected_line == sanitized(line) end end Maze.check.true(success, "No output lines from #{current_shell.stdout_lines} matched #{expected_line}") end |
Verify a string using a regex in the stdout logs
284 285 286 287 288 |
Then('the shell has output a match for the regex {string} to stdout') do |regex_matcher| current_shell = Maze::Runner.interactive_session match = current_shell.stdout_lines.any? { |line| line.match?(regex_matcher) } Maze.check.true(match, "No output lines from #{current_shell.stdout_lines} matched #{regex_matcher}") end |
Wait for a string matching a regex in the stdout logs, timing out after Maze.config.receive_requests_wait seconds.
293 294 295 296 297 298 299 300 301 302 |
Then('I wait for the shell to output a match for the regex {string} to stdout') do |regex_matcher| wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait) current_shell = Maze::Runner.interactive_session success = wait.until do current_shell.stdout_lines.any? { |line| line.match?(regex_matcher) } end Maze.check.true(success, "No output lines from #{current_shell.stdout_lines} matched #{regex_matcher}") end |
Wait for the shell to output a number of strings in STDOUT, as defined by a table. This step will time out after Maze.config.receive_requests_wait seconds.
308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
Then('I wait for the interactive shell to output the following lines in stdout') do |expected_lines| wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait) current_shell = Maze::Runner.interactive_session success = wait.until do current_stdout = current_shell.stdout_lines.join("\n") current_stdout.include?(expected_lines) end Maze.check.true( success, "Lines present in stdout: #{current_shell.stdout_lines} did not include all of: #{expected_lines}" ) end |
Verify a string appears in the stderr logs
326 327 328 329 330 |
Then('the shell has output {string} to stderr') do |expected_err| current_shell = Maze::Runner.interactive_session match = current_shell.stderr_lines.any? { |line| line == expected_err } Maze.check.true(match, "No output lines from #{current_shell.stderr_lines} matched #{expected_err}") end |
Wait for a string to appear in the stderr logs
335 336 337 338 339 340 341 342 343 344 |
Then('I wait for the shell to output {string} to stderr') do |expected_line| wait = Maze::Wait.new(timeout: Maze.config.receive_requests_wait) current_shell = Maze::Runner.interactive_session success = wait.until do current_shell.stderr_lines.any? { |line| line == expected_line } end Maze.check.true(success, "No output lines from #{current_shell.stderr_lines} matched #{expected_line}") end |
Verify the last interactive command exited successfully (assuming a 0 is a success)
347 348 349 350 351 352 353 354 355 356 357 358 359 |
Then('the last interactive command exited successfully') do Maze.check.true( Maze::Runner.interactive_session?, 'No interactive session is running so the exit code cannot be checked' ) uuid = SecureRandom.uuid steps %Q{ When I input "[ $? = 0 ] && echo '#{uuid} exited with 0' || echo '#{uuid} exited with error'" interactively Then I wait for the shell to output a match for the regex "#{uuid} exited with 0" to stdout } end |
Verify the exit code of the last interactive command
364 365 366 367 368 369 370 371 372 373 374 375 376 |
Then('the last interactive command exit code is {int}') do |exit_code| Maze.check.true( Maze::Runner.interactive_session?, 'No interactive session is running so the exit code cannot be checked' ) uuid = SecureRandom.uuid steps %Q{ When I input "echo #{uuid} $?" interactively Then I wait for the shell to output a match for the regex "#{uuid} #{exit_code}" to stdout } end |
Assert that the last interactive command exited with an error code (assuming non-0 is an error)
379 380 381 382 383 384 385 386 387 388 389 390 391 |
Then('the last interactive command exited with an error code') do Maze.check.true( Maze::Runner.interactive_session?, 'No interactive session is running so the exit code cannot be checked' ) uuid = SecureRandom.uuid steps %Q{ When I input "[ $? = 0 ] && echo '#{uuid} exited with 0' || echo '#{uuid} exited with error'" interactively Then I wait for the shell to output a match for the regex "#{uuid} exited with error" to stdout } end |
Assert that an expected_line is present in a file located relative to the interactive terminal's CWD
397 398 399 400 401 402 403 |
Then('the interactive file {string} contains {string}') do |filename, expected_line| steps %( When I input "fgrep '#{expected_line.gsub(/"/, '\"')}' #{filename}" interactively And I wait for the current stdout line to match the regex "[#>$]\\s?" Then the last interactive command exited successfully ) end |
Assert that a line is not present in a file located relative to the interactive terminal's CWD
409 410 411 412 413 414 415 |
Then('the interactive file {string} does not contain {string}') do |filename, excluded_line| steps %( When I input "fgrep '#{excluded_line.gsub(/"/, '\"')}' #{filename}" interactively And I wait for the current stdout line to match the regex "[#>$]\\s?" Then the last interactive command exited with an error code ) end |
Assert that a file located relative to the CWD of the interactive terminal contains all of the expected lines
421 422 423 424 425 |
Then('the interactive file {string} contains:') do |filename, expected_lines| expected_lines.each_line do |line| step("the interactive file '#{filename}' contains '#{line.chomp}'") end end |
Assert that a file located relative to the CWD of the interactive terminal does not contain any of the excluded lines
431 432 433 434 435 |
Then('the interactive file {string} does not contain:') do |filename, excluded_lines| excluded_lines.each_line do |line| step("the interactive file '#{filename}' does not contain '#{line.chomp}'") end end |
Starts an Android emulator available within the local environment
6 7 8 9 10 11 12 |
When("I start Android emulator {string}") do |emulator| steps %Q{ When I set environment variable "ANDROID_EMULATOR" to "#{emulator}" And I run the script "launch-android-emulator.sh" And I run the script "await-android-emulator.sh" synchronously } end |
Synchronously clears the data for the test application Requires an ADB connection
16 17 18 |
When("I clear the Android app data") do step('I run the script "clear-android-app-data.sh" synchronously') end |
Force stops the test application Requires an ADB connection
22 23 24 |
When("I force stop the Android app") do step('I run the script "force-stop-android-app.sh" synchronously') end |
Installs a given bundle from an APK onto a device Requires an ADB connection
31 32 33 34 35 36 37 |
When("I install the {string} Android app from {string}") do |bundle, filepath| steps %Q{ When I set environment variable "APP_BUNDLE" to "#{bundle}" And I set environment variable "APK_PATH" to "#{filepath}" And I run the script "install-android-app.sh" synchronously } end |
Starts a specific activity for a given app Requires an ADB connection
44 45 46 47 48 49 50 51 |
When("I start the {string} Android app using the {string} activity") do |app, activity| steps %Q{ When I set environment variable "APP_BUNDLE" to "#{app}" When I set environment variable "APP_ACTIVITY" to "#{activity}" And I run the script "launch-android-app.sh" synchronously And I wait for 4 seconds } end |
Invoke a lambda directly with 'sam invoke'
12 13 14 |
Given('I invoke the {string} lambda in {string}') do |lambda_name, directory| Maze::Aws::Sam.invoke(directory, lambda_name) end |
Invoke a lambda directly with 'sam invoke' and the given event
21 22 23 |
Given('I invoke the {string} lambda in {string} with the {string} event') do |lambda_name, directory, event_file| Maze::Aws::Sam.invoke(directory, lambda_name, event_file) end |
Test the exit code of the SAM CLI process.
28 29 30 |
Then('the SAM exit code equals {int}') do |expected| Maze.check.equal(expected, Maze::Aws::Sam.last_exit_code) end |
Test the Lambda response is empty but not-null. This indicates the Lambda did not respond but did run successfully
34 35 36 37 38 |
Then('the lambda response is empty') do Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!') Maze.check.equal({}, Maze::Aws::Sam.last_response) end |
Test a Lambda response field equals the given string.
44 45 46 47 48 49 50 |
Then('the lambda response {string} equals {string}') do |key_path, expected| Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!') actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path) Maze.check.equal(expected, actual) end |
Test a Lambda response field contains the given string.
56 57 58 59 60 61 62 |
Then('the lambda response {string} contains {string}') do |key_path, expected| Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!') actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path) Maze.check.include(actual, expected) end |
Test a Lambda response field equals the given integer.
68 69 70 71 72 73 74 |
Then('the lambda response {string} equals {int}') do |key_path, expected| Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!') actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path) Maze.check.equal(expected, actual) end |
Test a Lambda response field is true.
79 80 81 82 83 84 85 |
Then('the lambda response {string} is true') do |key_path| Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!') actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path) Maze.check.true(actual) end |
Test a Lambda response field is false.
90 91 92 93 94 95 96 |
Then('the lambda response {string} is false') do |key_path| Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!') actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path) Maze.check.false(actual) end |
Test a Lambda response field is null.
101 102 103 104 105 106 107 |
Then('the lambda response {string} is null') do |key_path| Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!') actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path) Maze.check.nil(actual) end |
Test a Lambda response field is not null.
112 113 114 115 116 117 118 |
Then('the lambda response {string} is not null') do |key_path| Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!') actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path) Maze.check.not_nil(actual) end |
Test a Lambda response field is greater than the given integer.
124 125 126 127 128 129 130 |
Then('the lambda response {string} is greater than {int}') do |key_path, expected| Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!') actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path) Maze.check.operator(actual, :>, expected) end |
Test a Lambda response field is less than the given integer.
136 137 138 139 140 141 142 |
Then('the lambda response {string} is less than {int}') do |key_path, expected| Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!') actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path) Maze.check.operator(actual, :<, expected) end |
Test a Lambda response field starts with the given string.
148 149 150 151 152 153 154 155 156 157 158 |
Then('the lambda response {string} starts with {string}') do |key_path, expected| Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!') actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path) Maze.check.kind_of(String, actual) Maze.check.true( actual.start_with?(expected), "Field '#{key_path}' value ('#{actual}') does not start with '#{expected}'" ) end |
Test a Lambda response field ends with the given string.
164 165 166 167 168 169 170 171 172 173 174 |
Then('the lambda response {string} ends with {string}') do |key_path, expected| Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!') actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path) Maze.check.kind_of(String, actual) Maze.check.true( actual.end_with?(expected), "Field '#{key_path}' value ('#{actual}') does not start with '#{expected}'" ) end |
Test a Lambda response field is an array with a specific number of elements.
180 181 182 183 184 185 186 187 |
Then('the lambda response {string} is an array with {int} element(s)') do |key_path, expected| Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!') actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path) Maze.check.kind_of(Array, actual) Maze.check.equal(expected, actual.length) end |
Test a Lambda response field is an array with at least 1 element.
192 193 194 195 196 197 198 199 |
Then('the lambda response {string} is a non-empty array') do |key_path| Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!') actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path) Maze.check.kind_of(Array, actual) Maze.check.false(actual.empty?) end |
Test a Lambda response field matches the given regex.
205 206 207 208 209 210 211 212 |
Then('the lambda response {string} matches the regex {string}') do |key_path, regex| expected = Regexp.new(regex) Maze.check.not_nil(Maze::Aws::Sam.last_response, 'No lambda response!') actual = Maze::Helper.read_key_path(Maze::Aws::Sam.last_response, key_path) Maze.check.match(expected, actual) end |
3 4 5 6 7 8 9 10 11 12 13 14 |
When('I navigate to the URL {string}') do |path| begin $logger.debug "Navigating to: #{path}" Maze.driver.navigate.to path rescue => exception $logger.error("#{exception.class} occurred during navigation attempt with message: #{exception.}") $logger.error("Restarting driver and retrying navigation to: #{path}") Maze.driver.restart_driver Maze.driver.navigate.to path # If a further error occurs it will get thrown as normal end end |
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
Then(/^the error is a valid browser payload for the error reporting API$/) do if !/^ie_(8|9|10)$/.match(Maze.config.browser) steps %( Then the error "Bugsnag-API-Key" header is present And the error "Content-Type" header equals one of: | application/json | | application/json; charset=UTF-8 | And the error "Bugsnag-Payload-Version" header equals "4" And the error "Bugsnag-Sent-At" header is a timestamp ) else steps %( Then the error "apiKey" query parameter is not null And the error "payloadVersion" query parameter equals "4" And the error "sentAt" query parameter is a timestamp ) end steps %( And the error payload field "notifier.name" is not null And the error payload field "notifier.url" is not null And the error payload field "notifier.version" is not null And the error payload field "events" is a non-empty array And each element in error payload field "events" has "severity" And each element in error payload field "events" has "severityReason.type" And each element in error payload field "events" has "unhandled" And each element in error payload field "events" has "exceptions" And the exception "type" equals "browserjs" ) end |
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
Then('the session is a valid browser payload for the session tracking API') do if !/^ie_(8|9|10)$/.match(Maze.config.browser) steps %( Then the session "Bugsnag-API-Key" header is present And the session "Content-Type" header equals one of: | application/json | | application/json; charset=UTF-8 | And the session "Bugsnag-Payload-Version" header equals "1" And the session "Bugsnag-Sent-At" header is a timestamp ) else steps %( Then the session "apiKey" query parameter is not null And the session "payloadVersion" query parameter equals "1" And the session "sentAt" query parameter is a timestamp ) end steps %( And the session payload field "app" is not null And the session payload field "device" is not null And the session payload field "notifier.name" is not null And the session payload field "notifier.url" is not null And the session payload field "notifier.version" is not null And the session payload has a valid sessions array ) end |
75 76 77 78 79 80 81 82 |
Then('the event device ID is valid') do if Maze.driver.local_storage? step('the event "device.id" matches "^c[a-z0-9]{20,32}$"') else $logger.info('Local storage is not supported in this browser, assuming device ID is null') step('the event "device.id" is null') end end |
84 85 86 87 88 89 90 91 |
Then('the event device ID is {string}') do |expected_id| if Maze.driver.local_storage? step("the event \"device.id\" equals \"#{expected_id}\"") else $logger.info('Local storage is not supported in this browser, assuming device ID is null') step('the event "device.id" is null') end end |
Checks that a port on a given host is open and ready for connections.
7 8 9 |
When('I wait for the host {string} to open port {string}') do |host, port| Maze::Network.wait_for_port(host, port) end |
Sets the HTTP status code to be used for all subsequent requests for a given connection type
15 16 17 18 |
When('I set the HTTP status code for {string} requests to {int}') do |http_verb, status_code| raise("Invalid HTTP verb: #{http_verb}") unless Maze::Server::ALLOWED_HTTP_VERBS.include?(http_verb) Maze::Server.set_status_code_generator(Maze::Generator.new([status_code].cycle), http_verb) end |
Sets the HTTP status code to be used for the next set of requests for a given connection type
24 25 26 27 |
When('I set the HTTP status code for the next {string} request(s) to {int_array}') do |http_verb, status_codes| raise("Invalid HTTP verb: #{http_verb}") unless Maze::Server::ALLOWED_HTTP_VERBS.include?(http_verb) Maze::Server.set_status_code_generator(create_defaulting_generator(status_codes, Maze::Server::DEFAULT_STATUS_CODE), http_verb) end |
Sets the HTTP status code to be used for all subsequent POST requests
32 33 34 |
When('I set the HTTP status code to {int}') do |status_code| step %{I set the HTTP status code for "POST" requests to #{status_code}} end |
Sets the HTTP status code to be used for the next set of POST requests
39 40 41 |
When('I set the HTTP status code for the next request(s) to {int_array}') do |status_codes| step %{I set the HTTP status code for the next "POST" requests to #{status_codes.join ','}} end |
Sets the sampling probability to be used for all subsequent trace responses
46 47 48 |
When('I set the sampling probability to {string}') do |sampling_probability| Maze::Server.set_sampling_probability_generator(Maze::Generator.new [sampling_probability].cycle) end |
Sets the sampling probability to be used for the next trace responses
53 54 55 |
When('I set the sampling probability for the next trace to {string}') do |sampling_probability| Maze::Server.set_sampling_probability_generator(create_defaulting_generator([sampling_probability], Maze::Server::DEFAULT_SAMPLING_PROBABILITY)) end |
Sets the sampling probability to be used for the next set of trace requests
60 61 62 63 |
When('I set the sampling probability for the next traces to {string}') do |status_codes| codes = status_codes.split(',').map(&:strip) Maze::Server.set_sampling_probability_generator(create_defaulting_generator(codes, Maze::Server::DEFAULT_SAMPLING_PROBABILITY)) end |
Sets the response delay to be used for all subsequent requests
68 69 70 |
When('I set the response delay to {int} milliseconds') do |response_delay_ms| Maze::Server.set_response_delay_generator(Maze::Generator.new [response_delay_ms].cycle) end |
Sets the response delay to be used for the next request
75 76 77 |
When('I set the response delay for the next request to {int} milliseconds') do |delay| Maze::Server.set_response_delay_generator(create_defaulting_generator([delay], Maze::Server::DEFAULT_RESPONSE_DELAY)) end |
Attempts to open a URL.
96 97 98 99 100 101 102 |
When('I open the URL {string}') do |url| begin URI(url).open(&:read) rescue OpenURI::HTTPError $logger.debug $!.inspect end end |
Starts the terminating server to cancel requests received.
105 106 107 |
When('I start the terminating server') do Maze::TerminatingServer.start end |
Sets the response message on the terminating server
110 111 112 |
When('I set the terminated response message to {string}') do || Maze::TerminatingServer.response = end |
Sets the maximum allowable amount of data received to the terminating server
117 118 119 |
When('I set the terminating server data threshold to {int} bytes') do |max_length| Maze::TerminatingServer.max_received_size = max_length end |
Check if a certain number of connections have been received by the terminating server
124 125 126 127 |
Then('the terminating server has received {int} requests') do |request_count| Maze.check.equal(request_count, Maze::TerminatingServer.received_request_count, "#{request_count} terminated requests expected, #{Maze::TerminatingServer.received_request_count} received") end |
Tests the payload body does not match a JSON fixture.
9 10 11 12 13 14 |
Then('the {request_type} payload body does not match the JSON fixture in {string}') do |request_type, fixture_path| payload_value = Maze::Server.list_for(request_type).current[:body] expected_value = JSON.parse(File.open(fixture_path, &:read)) result = Maze::Compare.value(expected_value, payload_value) Maze.check.false(result.equal?, "Payload:\n#{payload_value}\nExpected:#{expected_value}") end |
Test the payload body matches a JSON fixture.
20 21 22 23 24 25 26 |
Then('the {request_type} payload body matches the JSON fixture in {string}') do |request_type, fixture_path| payload_value = Maze::Server.list_for(request_type).current[:body] expected_value = JSON.parse(File.open(fixture_path, &:read)) result = Maze::Compare.value(expected_value, payload_value) Maze.check.true(result.equal?, "The payload field '#{result.keypath}' does not match the fixture:\n #{result.reasons.join('\n')}") end |
Test that a payload element matches a JSON fixture.
33 34 35 36 37 38 39 40 41 |
Then('the {request_type}(|a b) payload field {string} matches the JSON fixture in {string}') \ do |request_type, field_path, fixture_path| list = Maze::Server.list_for(request_type) payload_value = Maze::Helper.read_key_path(list.current[:body], field_path) expected_value = JSON.parse(File.open(fixture_path, &:read)) result = Maze::Compare.value(expected_value, payload_value) Maze.check.true(result.equal?, "The payload field '#{result.keypath}' does not match the fixture:\n #{result.reasons.join('\n')}") end |
Tests that a request element is true.
47 48 49 50 |
Then('the {request_type} payload field {string} is true') do |request_type, field_path| list = Maze::Server.list_for(request_type) Maze.check.true(Maze::Helper.read_key_path(list.current[:body], field_path)) end |
Tests that a request element is false.
56 57 58 59 |
Then('the {request_type} payload field {string} is false') do |request_type, field_path| list = Maze::Server.list_for(request_type) Maze.check.false(Maze::Helper.read_key_path(list.current[:body], field_path)) end |
Tests that a request element is null.
65 66 67 68 |
Then('the {request_type} payload field {string} is null') do |request_type, field_path| list = Maze::Server.list_for(request_type) Maze.check.nil(Maze::Helper.read_key_path(list.current[:body], field_path)) end |
Tests that a request element is not null.
74 75 76 77 |
Then('the {request_type} payload field {string} is not null') do |request_type, field_path| list = Maze::Server.list_for(request_type) Maze.check.not_nil(Maze::Helper.read_key_path(list.current[:body], field_path)) end |
Tests that a payload element equals an integer.
84 85 86 87 |
Then('the {request_type} payload field {string} equals {int}') do |request_type, field_path, int_value| Maze.check.equal(int_value, Maze::Helper.read_key_path(Maze::Server.list_for(request_type).current[:body], field_path)) end |
Tests that a payload element equals a float.
94 95 96 97 |
Then('the {request_type} payload field {string} equals {float}') do |request_type, field_path, float_value| Maze.check.equal(float_value, Maze::Helper.read_key_path(Maze::Server.list_for(request_type).current[:body], field_path)) end |
Tests that a payload element equals a float, to a given number of decimal places.
105 106 107 108 109 110 |
Then('the {request_type} payload field {string} equals {float} to {int} decimal place(s)') do |request_type, field_path, float_value, places| body = Maze::Server.list_for(request_type).current[:body] rounded_value = Maze::Helper.read_key_path(body, field_path).round places Maze.check.equal(float_value, rounded_value) end |
Tests the payload field value against an environment variable.
117 118 119 120 121 122 123 124 125 |
Then('the {request_type} payload field {string} equals the environment variable {string}') \ do |request_type, field_path, env_var| environment_value = ENV[env_var] Maze.check.false(environment_value.nil?, "The environment variable #{env_var} must not be nil") list = Maze::Server.list_for(request_type) value = Maze::Helper.read_key_path(list.current[:body], field_path) Maze.check.equal(environment_value, value) end |
Tests a payload field contains a number larger than a value.
132 133 134 135 136 137 |
Then('the {request_type} payload field {string} is greater than {int}') do |request_type, field_path, int_value| list = Maze::Server.list_for(request_type) value = Maze::Helper.read_key_path(list.current[:body], field_path) Maze.check.kind_of Integer, value Maze.check.operator(value, :>, int_value, "The payload field '#{field_path}' (#{value}) is not greater than '#{int_value}'") end |
Tests a payload field contains a number smaller than a value.
144 145 146 147 148 149 150 |
Then('the {request_type} payload field {string} is less than {int}') do |request_type, field_path, int_value| list = Maze::Server.list_for(request_type) value = Maze::Helper.read_key_path(list.current[:body], field_path) Maze.check.kind_of Integer, value = "The #{request_type} payload field '#{field_path}' (#{value}) is not less than '#{int_value}'" Maze.check.operator(value, :<, int_value, ) end |
Tests a payload field equals a string.
157 158 159 160 |
Then('the {request_type} payload field {string} equals {string}') do |request_type, field_path, string_value| list = Maze::Server.list_for(request_type) Maze.check.equal(string_value, Maze::Helper.read_key_path(list.current[:body], field_path)) end |
Tests a payload field starts with a string.
167 168 169 170 171 172 173 174 175 |
Then('the {request_type} payload field {string} starts with {string}') do |request_type, field_path, string_value| list = Maze::Server.list_for(request_type) value = Maze::Helper.read_key_path(list.current[:body], field_path) Maze.check.kind_of String, value Maze.check.true( value.start_with?(string_value), "Field '#{field_path}' value ('#{value}') does not start with '#{string_value}'" ) end |
Tests a payload field ends with a string.
182 183 184 185 186 187 188 189 190 |
Then('the {request_type} payload field {string} ends with {string}') do |request_type, field_path, string_value| list = Maze::Server.list_for(request_type) value = Maze::Helper.read_key_path(list.current[:body], field_path) Maze.check.kind_of String, value Maze.check.true( value.end_with?(string_value), "Field '#{field_path}' value ('#{value}') does not end with '#{string_value}'" ) end |
Tests a payload field is an array with a specific element count.
197 198 199 200 201 202 |
Then('the {request_type} payload field {string} is an array with {int} elements') do |request_type, field, count| list = Maze::Server.list_for(request_type) value = Maze::Helper.read_key_path(list.current[:body], field) Maze.check.kind_of Array, value Maze.check.equal(count, value.length) end |
Tests a payload field is an array with at least one element.
208 209 210 211 212 213 |
Then('the {request_type} payload field {string} is a non-empty array') do |request_type, field| list = Maze::Server.list_for(request_type) value = Maze::Helper.read_key_path(list.current[:body], field) Maze.check.kind_of Array, value Maze.check.true(value.length.positive?, "the field '#{field}' must be a non-empty array") end |
Tests a payload field matches a regex.
220 221 222 223 224 225 |
Then('the {request_type} payload field {string} matches the regex {string}') do |request_type, field, regex_string| regex = Regexp.new(regex_string) list = Maze::Server.list_for(request_type) value = Maze::Helper.read_key_path(list.current[:body], field) Maze.check.match(regex, value) end |
Tests a payload field is a numeric timestamp.
231 232 233 234 235 236 237 238 239 240 241 |
Then('the {request_type} payload field {string} is a parsable timestamp in seconds') do |request_type, field| list = Maze::Server.list_for(request_type) value = Maze::Helper.read_key_path(list.current[:body], field) begin int = value.to_i parsed_time = Time.at(int) rescue StandardError parsed_time = nil end Maze.check.not_nil(parsed_time) end |
Tests that every element in an array contains a specified key-value pair.
248 249 250 251 252 253 254 255 256 |
Then('each element in {request_type} payload field {string} has {string}') do |request_type, key_path, element_key_path| list = Maze::Server.list_for(request_type) value = Maze::Helper.read_key_path(list.current[:body], key_path) Maze.check.kind_of Array, value value.each do |element| Maze.check.not_nil(Maze::Helper.read_key_path(element, element_key_path), "Each element in '#{key_path}' must have '#{element_key_path}'") end end |
Tests whether the top-most payload is valid for the Bugsnag build API APIKey fields and headers are tested against the '$api_key' global variable
7 8 9 10 11 12 |
Then('the build is valid for the Build API') do steps %( And the build payload field "apiKey" equals "#{$api_key}" And the build payload field "appVersion" is not null ) end |
Tests whether the top-most payload is valid for the Android mapping API APIKey fields and headers are tested against the '$api_key' global variable
16 17 18 19 20 21 22 23 24 25 |
Then('the build is valid for the Android Mapping API') do steps %( And the build payload field "apiKey" equals "#{$api_key}" And the build payload field "proguard" is not null And the build payload field "appId" is not null And the build payload field "versionCode" is not null And the build payload field "buildUUID" is not null And the build payload field "versionName" is not null ) end |
Tests whether the first event entry contains the specified number of breadcrumbs.
6 7 8 9 10 11 12 13 14 |
Then("the event has {int} breadcrumb(s)") do |expected| = Maze::Server.errors.current[:body]['events'].first['breadcrumbs'] Maze.check.equal( expected, &.length || 0, "Expected event to have '#{expected}' breadcrumbs, but got: #{}" ) end |
Tests whether the first event entry contains no breadcrumbs.
17 18 19 20 21 22 23 24 25 26 |
Then("the event has no breadcrumbs") do = Maze::Server.errors.current[:body]['events'].first['breadcrumbs'] Maze.check.true( # some notifiers may omit breadcrumbs entirely when empty, otherwise it should # be an empty array .nil? || .empty?, "Expected event not to have breadcrumbs, but got: #{}" ) end |
Tests whether the first event entry contains a specific breadcrumb with a type and name.
32 33 34 35 36 37 38 39 |
Then('the event has a {string} breadcrumb named {string}') do |type, name| = Maze::Server.errors.current[:body]['events'].first['breadcrumbs'] Maze.check.true( .any? { |crumb| crumb['type'] == type && crumb['name'] == name }, "Expected event to have a breadcrumb with type '#{type}' and name '#{name}', but got: #{}" ) end |
Tests whether the first event entry contains a specific breadcrumb with a type and message.
45 46 47 48 49 |
Then('the event has a {string} breadcrumb with message {string}') do |type, | value = Maze::Server.errors.current[:body]['events'].first['breadcrumbs'] found = value.any? { |crumb| crumb['type'] == type && crumb['metaData'] && crumb['metaData']['message'] == } raise("No breadcrumb matched: #{value}") unless found end |
Tests whether the first event entry does not contain a breadcrumb with a specific type. Used for confirming filtering of breadcrumbs
55 56 57 58 59 |
Then('the event does not have a {string} breadcrumb') do |type| value = Maze::Server.errors.current[:body]['events'].first['breadcrumbs'] found = value.any? { |crumb| crumb['type'] == type } raise("Breadcrumb with type: #{type} matched") if found end |
Test whether the first event entry does not contain a breadcrumb with a specific type and message. Used for confirming filtering of breadcrumbs
66 67 68 69 70 |
Then('the event does not have a {string} breadcrumb with message {string}') do |type, | value = Maze::Server.errors.current[:body]['events'].first['breadcrumbs'] found = value.any? { |crumb| crumb['type'] == type && crumb['metaData'] && crumb['metaData']['message'] == } raise("Breadcrumb with type: #{type} and message: #{} matched") if found end |
Tests whether any breadcrumb matches a given JSON fixture. This follows all the usual rules for JSON fixture matching.
75 76 77 78 79 80 |
Then('the event contains a breadcrumb matching the JSON fixture in {string}') do |json_fixture| = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], 'events.0.breadcrumbs') expected = JSON.parse(File.open(json_fixture, &:read)) match = .any? { || Maze::Compare.value(expected, ).equal? } Maze.check.true(match, 'No breadcrumbs in the event matched the given breadcrumb') end |
Waits for a given number of spans to be received, which may be spread across one or more trace requests.
6 7 8 |
When('I wait for {int} span(s)') do |span_count| assert_received_spans Maze::Server.list_for('traces'), span_count end |
Verifies that there are no feature flags present in a given event
8 9 10 11 |
Then('event {int} has no feature flags') do |event_id| event = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], "events.#{event_id}") Maze.check.false(has_feature_flags?(event), "Feature flags expected to be absent or empty, was #{event['featureFlags']}") end |
Verifies that the are no feature flags present
14 15 16 17 18 |
Then('the event has no feature flags') do steps %( Then event 0 has no feature flags ) end |
Verifies a feature flag with a specific variant is uniquely present in a given event
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
Then('event {int} contains the feature flag {string} with variant {string}') do |event_id, flag_name, variant| event = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], "events.#{event_id}") Maze.check.true(has_feature_flags?(event), "Expected feature flags were not present in event #{event_id}: #{event}") feature_flags = event['featureFlags'] # Test for flag name uniqueness Maze.check.true( feature_flags.one? { |flag| flag['featureFlag'].eql?(flag_name) }, "Expected single flag with 'featureFlag' value: #{flag_name}. Present flags: #{feature_flags}" ) flag = feature_flags.find { |flag| flag['featureFlag'].eql?(flag_name) } # Test the variant value Maze.check.true( flag.has_key?('variant') && flag['variant'].eql?(variant), "Feature flag: #{flag} did not have variant: #{variant}. All flags: #{feature_flags}" ) end |
Verifies a feature flag with a specific variant is uniquely present
47 48 49 50 51 |
Then('the event contains the feature flag {string} with variant {string}') do |flag_name, variant| steps %( Then event 0 contains the feature flag "#{flag_name}" with variant "#{variant}" ) end |
Verifies a feature flag with no variant (either null or missing) is uniquely present in a given event
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
Then('event {int} contains the feature flag {string} with no variant') do |event_id, flag_name| event = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], "events.#{event_id}") Maze.check.true(has_feature_flags?(event), "Expected feature flags were not present in event #{event_id}: #{event}") feature_flags = event['featureFlags'] # Test for flag name uniqueness Maze.check.true( feature_flags.one? { |flag| flag['featureFlag'].eql?(flag_name) }, "Expected single flag with 'featureFlag' value: #{flag_name}. All flags: #{feature_flags}" ) flag = feature_flags.find { |flag| flag['featureFlag'].eql?(flag_name) } # Test the variant value Maze.check.false( flag.has_key?('variant'), "Feature flag: #{flag} expected to have no variant. All flags: #{feature_flags}" ) end |
Verifies a feature flag with no variant (either null or missing) is uniquely present
79 80 81 82 83 |
Then('the event contains the feature flag {string} with no variant') do |flag_name| steps %( Then event 0 contains the feature flag "#{flag_name}" with no variant ) end |
Verifies that a number of feature flags outlined in a table are all present and unique in the given event
The DataTable used for this step should have featureFlag
and variant
columns, containing the appropriate
values. For flags with a variant leave the variant
column blank.
Example: | featureFlag | variant | | my_flag_1 | var_1 | | my_flag_2 | var_2 | | my_flag_3 | | # Should not have a variant present
98 99 100 101 |
Then('event {int} contains the following feature flags:') do |event_id, table| event = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], "events.#{event_id}") verify_feature_flags_with_table(event, table) end |
Verifies that a number of feature flags outlined in a table are all present and unique
See above for data table details
108 109 110 111 |
Then('the event contains the following feature flags:') do |table| event = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], 'events.0') verify_feature_flags_with_table(event, table) end |
Verifies a feature flag a specific name is not present, regardless of variant, for a given event
117 118 119 120 121 122 123 124 125 126 127 128 |
Then('event {int} does not contain the feature flag {string}') do |event_id, flag_name| event = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], "events.#{event_id}") Maze.check.true( has_feature_flags?(event), "Expected feature flags were not present in event #{event_id}: #{event}" ) feature_flags = event['featureFlags'] Maze.check.true( feature_flags.none? { |flag| flag['featureFlag'].eql?(flag_name) }, "Expected to not find feature flag #{flag_name}. All flags: #{feature_flags}" ) end |
Verifies a feature flag a specific name is not present, regardless of variant
133 134 135 136 137 |
Then('the event does not contain the feature flag {string}') do |flag_name| steps %( Then event 0 does not contain the feature flag "#{flag_name}" ) end |
Checks a UI element is present Requires a running Appium driver
7 8 9 10 |
Given('the element {string} is present') do |element_id| present = Maze.driver.wait_for_element(element_id) raise Maze::Error::AppiumElementNotFoundError.new("The element #{element_id} could not be found", element_id) unless present end |
Checks a UI element is present within a specified number of seconds Requires a running Appium driver
17 18 19 20 |
Given('the element {string} is present within {int} seconds') do |element_id, timeout| present = Maze.driver.wait_for_element(element_id, timeout) raise Maze::Error::AppiumElementNotFoundError.new("The element #{element_id} could not be found", element_id) unless present end |
Clicks a given element Requires a running Appium driver
26 27 28 |
When('I click the element {string}') do |element_id| Maze.driver.click_element(element_id) end |
Sends the app to the background indefinitely Requires a running Appium driver
32 33 34 |
When('I send the app to the background') do Maze.driver.background_app(-1) end |
Sends the app to the background for a number of seconds Requires a running Appium driver
40 41 42 |
When('I send the app to the background for {int} second(s)') do |timeout| Maze.driver.background_app(timeout) end |
Clears a given element Requires a running Appium driver
48 49 50 |
When('I clear the element {string}') do |element_id| Maze.driver.clear_element(element_id) end |
Sends keys to a given element Requires a running Appium driver
57 58 59 |
When('I send the keys {string} to the element {string}') do |keys, element_id| Maze.driver.send_keys_to_element(element_id, keys) end |
Set the device orientation to either portrait or landscape Requires a running Appium driver
63 64 65 |
When('I set the device orientation to {orientation}') do |orientation| Maze.driver.set_rotation orientation end |
Sends keys to a given element, clearing it first Requires a running Appium driver
72 73 74 |
When('I clear and send the keys {string} to the element {string}') do |keys, element_id| Maze.driver.clear_and_send_keys_to_element(element_id, keys) end |
Starts the document server manually. It will be stopped automatically at the end of each scenario (if started in this way).
5 6 7 |
When('I start the document server') do Maze::DocumentServer.manual_start end |
Verifies that generic elements of an error payload are present. APIKey fields and headers are tested against the '$api_key' global variable.
10 11 12 13 |
Then('the error is valid for the error reporting API version {string} for the {string} notifier') do |version, name| step "the error is valid for the error reporting API version \"#{version}\"" \ " for the \"#{name}\" notifier with the apiKey \"#{$api_key}\"" end |
Verifies that generic elements of an error payload are present.
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
Then('the error is valid for the error reporting API version {string}' \ ' for the {string} notifier with the apiKey {string}') do |payload_version, notifier_name, api_key| steps %( Then the error "Bugsnag-Api-Key" header equals "#{api_key}" And the error payload field "apiKey" equals "#{api_key}" And the error "Bugsnag-Payload-Version" header equals "#{payload_version}" And the error payload contains the payloadVersion "#{payload_version}" And the error "Content-Type" header equals "application/json" And the error "Bugsnag-Sent-At" header is a timestamp And the error Bugsnag-Integrity header is valid And the error payload field "notifier.name" equals "#{notifier_name}" And the error payload field "notifier.url" is not null And the error payload field "notifier.version" is not null And the error payload field "events" is a non-empty array And each element in error payload field "events" has "severity" And each element in error payload field "events" has "severityReason.type" And each element in error payload field "events" has "unhandled" And each element in error payload field "events" has "exceptions" ) end |
Verifies that an event is correct for an unhandled error This checks various elements of the payload matching an unhandled error including: The unhandled flag Any attached session information Severity
51 52 53 |
Then('event {int} is unhandled') do |event| test_unhandled_state(event, true) end |
Verifies that an event is correct for an unhandled error This checks various elements of the payload matching an unhandled error including: The unhandled flag Any attached session information Severity
63 64 65 |
Then('event {int} is unhandled with the severity {string}') do |event, severity| test_unhandled_state(event, true, severity) end |
Verifies that an event is correct for an handled error This checks various elements of the payload matching an unhandled error including: The unhandled flag Any attached session information Severity
74 75 76 |
Then('event {int} is handled') do |event| test_unhandled_state(event, false) end |
Verifies that an event is correct for an handled error This checks various elements of the payload matching an unhandled error including: The unhandled flag Any attached session information Severity
86 87 88 |
Then('event {int} is handled with the severity {string}') do |event, severity| test_unhandled_state(event, false, severity) end |
Checks the payloadVersion is set correctly. For Javascript this should be in the events. For all other notifiers this should be a top-level key.
95 96 97 98 99 100 101 102 103 104 105 |
Then('the error payload contains the payloadVersion {string}') do |payload_version| body_version = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], 'payloadVersion') body_set = payload_version == body_version event_version = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], 'events.0.payloadVersion') event_set = payload_version == event_version Maze.check.true( body_set || event_set, "The payloadVersion was not the expected value of #{payload_version}. " \ "#{body_version} found in body, #{event_version} found in event" ) end |
Tests whether a value in the first event entry matches a literal.
111 112 113 |
Then(/^the event "(.+)" is (true|false|null|not null)$/) do |field, literal| step "the error payload field \"events.0.#{field}\" is #{literal}" end |
Tests whether a value in the first event entry matches a string.
119 120 121 |
Then('the event {string} equals {string}') do |field, string_value| step "the error payload field \"events.0.#{field}\" equals \"#{string_value}\"" end |
Tests whether a value in the first event entry matches a floating point value.
127 128 129 |
Then('the event {string} equals {float}') do |field, float_value| step "the error payload field \"events.0.#{field}\" equals #{float_value}" end |
Tests whether a value in the first event entry matches a floating point value, to a given number of decimal places.
135 136 137 |
Then('the event {string} equals {float} to {int} decimal place(s)') do |field, float_value, places| step "the error payload field \"events.0.#{field}\" equals #{float_value} to #{places} decimal places" end |
Tests whether a value in the first event entry equals an integer.
143 144 145 |
Then('the event {string} equals {int}') do |field, value| step "the error payload field \"events.0.#{field}\" equals #{value}" end |
Tests whether a value in the first event entry starts with a string.
151 152 153 |
Then('the event {string} starts with {string}') do |field, string_value| step "the error payload field \"events.0.#{field}\" starts with \"#{string_value}\"" end |
Tests whether a value in the first event entry ends with a string.
159 160 161 |
Then('the event {string} ends with {string}') do |field, string_value| step "the error payload field \"events.0.#{field}\" ends with \"#{string_value}\"" end |
Tests whether a value in the first event entry matches a regex.
167 168 169 |
Then('the event {string} matches {string}') do |field, pattern| step "the error payload field \"events.0.#{field}\" matches the regex \"#{pattern}\"" end |
Tests whether a value in the first event entry is a timestamp.
174 175 176 |
Then('the event {string} is a timestamp') do |field| step "the error payload field \"events.0.#{field}\" matches the regex \"#{TIMESTAMP_REGEX}\"" end |
Tests whether a value in the first event entry is a numeric and parsable timestamp.
181 182 183 |
Then('the event {string} is a parsable timestamp in seconds') do |field| step "the error payload field \"events.0.#{field}\" is a parsable timestamp in seconds" end |
Tests the Event field value against an environment variable.
189 190 191 |
Then('the event {string} equals the environment variable {string}') do |field, env_var| step "the error payload field \"events.0.#{field}\" equals the environment variable \"#{env_var}\"" end |
Tests whether a value in the first event entry matches a JSON fixture.
197 198 199 |
Then('the event {string} matches the JSON fixture in {string}') do |field, fixture_path| step "the error payload field \"events.0.#{field}\" matches the JSON fixture in \"#{fixture_path}\"" end |
201 202 203 204 |
Then('the event {string} string is empty') do |keypath| value = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], keypath) Maze.check.true(value.nil? || value.empty?, "The #{keypath} is not empty: '#{value}'") end |
206 207 208 209 210 |
Then('the event {string} is greater than {int}') do |keypath, int| value = Maze::Helper.read_key_path(Maze::Server.errors.current[:body], "events.0.#{keypath}") Maze.check.false(value.nil?, "The event #{keypath} is nil") Maze.check.operator(value, :>, int) end |
Tests whether a value in the first exception of the first event entry starts with a string.
216 217 218 |
Then('the exception {string} starts with {string}') do |field, string_value| step "the error payload field \"events.0.exceptions.0.#{field}\" starts with \"#{string_value}\"" end |
Tests whether a value in the first exception of the first event entry ends with a string.
224 225 226 |
Then('the exception {string} ends with {string}') do |field, string_value| step "the error payload field \"events.0.exceptions.0.#{field}\" ends with \"#{string_value}\"" end |
Tests whether a value in the first exception of the first event entry equals a string.
232 233 234 |
Then('the exception {string} equals {string}') do |field, string_value| step "the error payload field \"events.0.exceptions.0.#{field}\" equals \"#{string_value}\"" end |
Tests whether a value in the first exception of the first event entry matches a regex.
240 241 242 |
Then('the exception {string} matches {string}') do |field, pattern| step "the error payload field \"events.0.exceptions.0.#{field}\" matches the regex \"#{pattern}\"" end |
Tests whether a element of a stack frame in the first exception of the first event equals an integer.
249 250 251 252 |
Then('the {string} of stack frame {int} equals {int}') do |key, num, value| field = "events.0.exceptions.0.stacktrace.#{num}.#{key}" step "the error payload field \"#{field}\" equals #{value}" end |
Tests whether an element of a stack frame in the first exception of the first event matches a regex pattern.
259 260 261 262 |
Then('the {string} of stack frame {int} matches {string}') do |key, num, pattern| field = "events.0.exceptions.0.stacktrace.#{num}.#{key}" step "the error payload field \"#{field}\" matches the regex \"#{pattern}\"" end |
Tests whether an element of a stack frame in the first exception of the first event equals a string.
269 270 271 272 |
Then('the {string} of stack frame {int} equals {string}') do |key, num, value| field = "events.0.exceptions.0.stacktrace.#{num}.#{key}" step "the error payload field \"#{field}\" equals \"#{value}\"" end |
Tests whether an element of a stack frame in the first exception of the first event starts with a string.
279 280 281 282 |
Then('the {string} of stack frame {int} starts with {string}') do |key, num, value| field = "events.0.exceptions.0.stacktrace.#{num}.#{key}" step "the error payload field \"#{field}\" starts with \"#{value}\"" end |
Tests whether an element of a stack frame in the first exception of the first event ends with a string.
289 290 291 292 |
Then('the {string} of stack frame {int} ends with {string}') do |key, num, value| field = "events.0.exceptions.0.stacktrace.#{num}.#{key}" step "the error payload field \"#{field}\" ends with \"#{value}\"" end |
Tests whether an element of a stack frame in the first exception of the first event matches a literal.
299 300 301 302 |
Then(/^the "(.*)" of stack frame (\d*) is (true|false|null|not null)$/) do |key, num, literal| field = "events.0.exceptions.0.stacktrace.#{num}.#{key}" step "the error payload field \"#{field}\" is #{literal}" end |
Tests whether a thread from the first event, identified by name, is the error reporting thread.
307 308 309 |
Then('the thread with name {string} contains the error reporting flag') do |thread_name| validate_error_reporting_thread('name', thread_name) end |
Tests whether a thread from the first event, identified by an id, is the error reporting thread.
314 315 316 |
Then('the thread with id {string} contains the error reporting flag') do |thread_id| validate_error_reporting_thread('id', thread_id) end |
Tests that a query parameter matches a string.
10 11 12 13 |
Then('the {request_type} {string} query parameter equals {string}') do |request_type, parameter_name, parameter_value| Maze.check.equal(parameter_value, Maze::Helper.parse_querystring(Maze::Server.list_for(request_type).current)[parameter_name][0]) end |
Tests that a query parameter is present and not null.
19 20 21 22 |
Then('the {request_type} {string} query parameter is not null') do |request_type, parameter_name| Maze.check.not_nil(Maze::Helper.parse_querystring(Maze::Server.list_for(request_type).current)[parameter_name][0], "The '#{parameter_name}' query parameter should not be null") end |
Tests that a query parameter is a timestamp.
28 29 30 31 |
Then('the {request_type} {string} query parameter is a timestamp') do |request_type, parameter_name| param = Maze::Helper.parse_querystring(Maze::Server.list_for(request_type).current)[parameter_name][0] Maze.check.match(TIMESTAMP_REGEX, param) end |
Verifies that generic elements of a session payload are present. APIKey fields and headers are tested against the '$api_key' global variable.
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
Then('the session is valid for the session reporting API version {string} for the {string} notifier') \ do |payload_version, notifier_name| steps %( Then the session "bugsnag-api-key" header equals "#{$api_key}" And the session "bugsnag-payload-version" header equals "#{payload_version}" And the session "Content-Type" header equals "application/json" And the session "Bugsnag-Sent-At" header is a timestamp And the session payload field "notifier.name" equals "#{notifier_name}" And the session payload field "notifier.url" is not null And the session payload field "notifier.version" is not null And the session payload field "app" is not null And the session payload field "device" is not null ) end |
Verifies that generic elements of a session payload are present for the React Native notifier APIKey fields and headers are tested against the '$api_key' global variable.
TODO: I'm reluctant to risk changing the previous step implementation right now, but we should consider refactoring the two at some point to avoid duplication.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
Then('the session is valid for the session reporting API version {string} for the React Native notifier') do |payload_version| steps %{ Then the session "bugsnag-api-key" header equals "#{$api_key}" And the session "bugsnag-payload-version" header equals "#{payload_version}" And the session "Content-Type" header equals "application/json" And the session "Bugsnag-Sent-At" header is a timestamp And the session payload field "notifier.name" matches the regex "(Bugsnag React Native|(Android|iOS) Bugsnag Notifier)" And the session payload field "notifier.url" is not null And the session payload field "notifier.version" is not null And the session payload field "app" is not null And the session payload field "device" is not null } end |
Tests whether a value in the first session entry matches a literal.
52 53 54 |
Then(/^the session "(.+)" is (true|false|null|not null)$/) do |field, literal| step "the session payload field \"sessions.0.#{field}\" is #{literal}" end |
Tests whether a value in the first session entry matches a string.
60 61 62 |
Then('the session {string} equals {string}') do |field, string_value| step "the session payload field \"sessions.0.#{field}\" equals \"#{string_value}\"" end |
Tests whether a value in the first session entry is a timestamp.
67 68 69 |
Then('the session {string} is a timestamp') do |field| step "the session payload field \"sessions.0.#{field}\" matches the regex \"#{TIMESTAMP_REGEX}\"" end |
Tests whether a value in the first sessionCount entry matches a literal.
75 76 77 |
Then(/^the sessionCount "(.+)" is (true|false|null|not null)$/) do |field, literal| step "the session payload field \"sessionCounts.0.#{field}\" is #{literal}" end |
Tests whether a value in the first sessionCount entry matches a string.
83 84 85 |
Then('the sessionCount {string} equals {string}') do |field, string_value| step "the session payload field \"sessionCounts.0.#{field}\" equals \"#{string_value}\"" end |
Tests whether a value in the first sessionCount entry equals an integer.
91 92 93 |
Then('the sessionCount {string} equals {int}') do |field, int_value| step "the session payload field \"sessionCounts.0.#{field}\" equals #{int_value}" end |
Tests whether a value in the first sessionCount entry is a timestamp.
98 99 100 |
Then('the sessionCount {string} is a timestamp') do |field| step "the session payload field \"sessionCounts.0.#{field}\" matches the regex \"#{TIMESTAMP_REGEX}\"" end |
Tests that a payload has an appropriately structured session array
103 104 105 106 107 108 109 110 111 112 113 114 115 |
Then('the session payload has a valid sessions array') do if sessions = Maze::Server.sessions.current[:body]['sessions'] steps %( Then the session "id" is not null And the session "startedAt" is a timestamp ) else steps %( Then the sessionCount "sessionsStarted" is not null And the sessionCount "startedAt" is a timestamp ) end end |
Verifies that any type of request contains multipart form-data
25 26 27 28 |
Then('the {request_type} request is valid multipart form-data') do |request_type| list = Maze::Server.list_for request_type valid_multipart_form_data?(list.current) end |
Verifies all requests of a given type contain multipart form-data
33 34 35 36 |
Then('all {request_type} requests are valid multipart form-data') do |request_type| list = Maze::Server.list_for request_type list.all.all? { |request| valid_multipart_form_data?(request) } end |
Tests the number of fields a given type of multipart request contains.
42 43 44 45 46 |
Then('the {request_type} multipart request has {int} fields') do |request_type, part_count| list = Maze::Server.list_for request_type parts = list.current[:body] Maze.check.equal(part_count, parts.size) end |
Tests a given type of multipart request has at least one field.
51 52 53 54 55 |
Then('the {request_type} multipart request has a non-empty body') do |request_type| list = Maze::Server.list_for request_type parts = list.current[:body] Maze.check.true(parts.size.positive?, "Multipart request payload contained #{parts.size} fields") end |
Tests that a given type of multipart payload body does not match a JSON file. JSON formatted multipart fields will be parsed into hashes.
76 77 78 79 80 81 82 83 84 |
Then('the {request_type} multipart body does not match the JSON file in {string}') do |request_type, json_path| Maze.check.true(File.exist?(json_path), "'#{json_path}' does not exist") payload_list = Maze::Server.list_for request_type raw_payload_value = payload_list.current[:body] payload_value = parse_multipart_body(raw_payload_value) expected_value = JSON.parse(File.open(json_path, &:read)) result = Maze::Compare.value(expected_value, payload_value) Maze.check.false(result.equal?, "Payload:\n#{payload_value}\nExpected:#{expected_value}") end |
Tests that a given type of multipart payload body matches a JSON fixture. JSON formatted multipart fields will be parsed into hashes.
91 92 93 94 95 96 97 98 99 |
Then('the {request_type} multipart body matches the JSON file in {string}') do |request_type, json_path| Maze.check.true(File.exist?(json_path), "'#{json_path}' does not exist") payload_list = Maze::Server.list_for request_type raw_payload_value = payload_list.current[:body] payload_value = parse_multipart_body(raw_payload_value) expected_value = JSON.parse(File.open(json_path, &:read)) result = Maze::Compare.value(expected_value, payload_value) Maze.check.true(result.equal?, "The payload field '#{result.keypath}' does not match the fixture:\n #{result.reasons.join('\n')}") end |
Tests that a given type of multipart field matches a JSON fixture. The field will be parsed into a hash.
107 108 109 110 111 112 113 114 |
Then('the {request_type} multipart field {string} matches the JSON file in {string}') do |request_type, field_path, json_path| Maze.check.true(File.exist?(json_path), "'#{json_path}' does not exist") payload_list = Maze::Server.list_for request_type payload_value = JSON.parse(payload_list.current[:body][field_path].to_s) expected_value = JSON.parse(File.open(json_path, &:read)) result = Maze::Compare.value(expected_value, payload_value) Maze.check.true(result.equal?, "The multipart field '#{result.keypath}' does not match the fixture:\n #{result.reasons.join('\n')}") end |
Tests that a multipart request field exists and is not null.
120 121 122 123 |
Then('the field {string} for multipart {request_type} is not null') do |part_key, request_type| parts = Maze::Server.list_for(request_type).current[:body] Maze.check.not_nil(parts[part_key], "The field '#{part_key}' should not be null") end |
Tests that a multipart request field exists and is null.
129 130 131 132 |
Then('the field {string} for multipart {request_type} is null') do |part_key, request_type| parts = Maze::Server.list_for(request_type).current[:body] Maze.check.nil(parts[part_key], "The field '#{part_key}' should be null") end |
Tests that a multipart request field equals a string.
139 140 141 142 |
Then('the field {string} for multipart {request_type} equals {string}') do |part_key, request_type, expected_value| parts = Maze::Server.list_for(request_type).current[:body] Maze.check.equal(parts[part_key], expected_value) end |
Request assertions
Shortcut to waiting to receive a single request of the given type
58 59 60 |
Then('I wait to receive a(n) {request_type}') do |request_type| step "I wait to receive 1 #{request_type}" end |
Continually checks to see if the required amount of requests have been received, timing out according to @see Maze.config.receive_requests_wait. If all expected requests are received and have the Bugsnag-Sent-At header, they will be sorted by the header.
69 70 71 72 73 |
Then('I wait to receive {int} {request_type}') do |request_count, request_type| list = Maze::Server.list_for(request_type) assert_received_requests request_count, list, request_type list.sort_by_sent_at! request_count end |
Continually checks to see if at least the number requests given has been received, timing out according to @see Maze.config.receive_requests_wait.
This step can tolerate receiving more than the expected number of requests.
82 83 84 85 |
Then('I wait to receive at least {int} {request_type}') do |request_count, request_type| list = Maze::Server.list_for(request_type) assert_received_requests request_count, list, request_type, false end |
Sorts the remaining requests in a list by the field path given.
91 92 93 94 |
Then('I sort the {request_type} by the payload field {string}') do |request_type, field_path| list = Maze::Server.list_for(request_type) list.sort_by! field_path end |
Verify that at least a certain amount of requests have been received This step is only intended for use in stress tests
101 102 103 104 |
Then('I have received at least {int} {request_type}') do |min_received, request_type| list = Maze::Server.list_for(request_type) Maze.check.operator(list.size_remaining, :>=, min_received, "Actually received #{list.size_remaining} #{request_type} requests") end |
Verify that an amount of requests within a range have been received
111 112 113 114 |
Then('I wait to receive between {int} and {int} {request_type}') do |min_received, max_received, request_type| list = Maze::Server.list_for(request_type) assert_received_requests min_received, list, request_type, false, max_received end |
Assert that the test Server hasn't received any requests - of a specific, or any, type.
119 120 121 122 123 |
Then('I should receive no {request_type}') do |request_type| sleep Maze.config.receive_no_requests_wait list = Maze::Server.list_for(request_type) Maze.check.equal(0, list.size_remaining, "#{list.size_remaining} #{request_type} received") end |
Moves to the next request
128 129 130 131 132 |
Then('I discard the oldest {request_type}') do |request_type| raise "No #{request_type} to discard" if Maze::Server.list_for(request_type).current.nil? Maze::Server.list_for(request_type).next end |
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
Then('the received errors match:') do |table| # Checks that each request matches one of the event fields requests = Maze::Server.errors.remaining match_count = 0 # iterate through each row in the table. exactly 1 request should match each row. table.hashes.each do |row| requests.each do |request| # Skip if no body.events in this request next if (!request.key? :body) || (!request[:body].key? 'events') events = request[:body]['events'] Maze.check.equal(1, events.length, 'Expected exactly one event per request') match_count += 1 if Maze::Assertions::RequestSetAssertions.request_matches_row(events[0], row) end end Maze.check.equal(requests.size, match_count, 'Unexpected number of requests matched the received payloads') end |
Verifies that a request was sent via a given method. Currently only supported with the reflective servlet.
158 159 160 161 162 163 164 165 |
Then('the {request_type} request method equals {string}') do |request_type, method| list = Maze::Server.list_for(request_type) payload = list.current if payload[:method].nil? raise Test::Unit::AssertionFailedError.new("#{request_type} request had no receipt method listed") end Maze.check.equal(method, payload[:method], "Expected #{request_type} request method to be #{method}") end |
Tests that the given payload value is correct for the target BrowserStack platform. This step will assume the expected and payload values are strings. If the step is invoked when a remote BrowserStack device is not in use this step will fail.
The DataTable used for this step should have ios
and android
in the same row as their expected value:
| android | Java.lang.RuntimeException |
| ios | NSException |
If the expected value is set to "@skip", the check should be skipped If the expected value is set to "@null", the check will be for null If the expected value is set to "@not_null", the check will be for a non-null value
18 19 20 |
Then('the {request_type} payload field {string} equals the platform-dependent string:') do |request_type, field_path, platform_values| test_string_platform_values(request_type, field_path, platform_values) end |
See the error payload field {string} equals the platform-dependent string:
26 27 28 |
Then('the event {string} equals the platform-dependent string:') do |field_path, platform_values| test_string_platform_values('error', "events.0.#{field_path}", platform_values) end |
Tests that the given payload value is correct for the target BrowserStack platform. This step will assume the expected and payload values are numeric. If the step is invoked when a remote BrowserStack device is not in use this step will fail.
The DataTable used for this step should have ios
and android
in the same row as their expected value:
| android | 1 |
| ios | 5.5 |
If the expected value is set to "@skip", the check should be skipped If the expected value is set to "@null", the check will be for null If the expected value is set to "@not_null", the check will be for a non-null value
45 46 47 |
Then('the {request_type} payload field {string} equals the platform-dependent numeric:') do |request_type, field_path, platform_values| test_numeric_platform_values(request_type, field_path, platform_values) end |
See the payload field {string} equals the platform-dependent numeric:
53 54 55 |
Then('the event {string} equals the platform-dependent numeric:') do |field_path, platform_values| test_numeric_platform_values('error', "events.0.#{field_path}", platform_values) end |
Tests that the given payload value is correct for the target BrowserStack platform. This step will assume the expected and payload values are booleans. If the step is invoked when a remote BrowserStack device is not in use this step will fail.
The DataTable used for this step should have ios
and android
in the same row as their expected value:
| android | 1 |
| ios | 5 |
If the expected value is set to "@skip", the check should be skipped If the expected value is set to "@null", the check will be for null If the expected value is set to "@not_null", the check will be for a non-null value
72 73 74 |
Then('the {request_type} payload field {string} equals the platform-dependent boolean:') do |request_type, field_path, platform_values| test_boolean_platform_values(request_type, field_path, platform_values) end |
See the payload field {string} equals the platform-dependent boolean:
80 81 82 |
Then('the event {string} equals the platform-dependent boolean:') do |field_path, platform_values| test_boolean_platform_values('error', "events.0.#{field_path}", platform_values) end |
See the payload field {string} equals the platform-dependent string:
88 89 90 |
Then('the exception {string} equals the platform-dependent string:') do |field_path, platform_values| test_string_platform_values('error', "events.0.exceptions.0.#{field_path}", platform_values) end |
See the payload field {string} equals the platform-dependent string:
97 98 99 |
Then('the {string} of stack frame {int} equals the platform-dependent string:') do |field_path, num, platform_values| test_string_platform_values('error', "events.0.exceptions.0.stacktrace.#{num}.#{field_path}", platform_values) end |
169 170 171 |
Then('the {request_type} payload field {string} matches the platform-dependent regex:') do |request_type, field_path, platform_regexes| match_string_platform_regexes(request_type, field_path, platform_regexes) end |
See the error payload field {string} equals the platform-dependent string:
177 178 179 |
Then('the event {string} matches the platform-dependent regex:') do |field_path, platform_regexes| match_string_platform_regexes('error', "events.0.#{field_path}", platform_regexes) end |
183 184 185 |
Then('the exception {string} matches the platform-dependent regex:') do |field_path, platform_regexes| match_string_platform_regexes('error', "events.0.exceptions.0.#{field_path}", platform_regexes) end |
190 191 192 |
Then('the {string} of stack frame {int} matches the platform-dependent regex:') do |field_path, num, platform_regexes| match_string_platform_regexes('error', "events.0.exceptions.0.stacktrace.#{num}.#{field_path}", platform_regexes) end |
1 2 3 4 5 6 |
ParameterType( name: 'request_type', regexp: /errors?|sessions?|builds?|logs?|metrics?|sampling requests?|traces?|uploads?|sourcemaps?|reflects?|reflections?|invalid requests?/, type: String, transformer: ->(s) { s } ) |
8 9 10 11 12 13 |
ParameterType( name: 'orientation', regexp: /portrait|landscape/, type: String, transformer: ->(s) { s.to_sym } ) |
15 16 17 18 19 20 |
ParameterType( name: 'int_array', regexp: /\d+(?:, ?\d+)*/, type: String, transformer: ->(s) { s.split(',').map(&:strip).map(&:to_i) } ) |