bugsnag-maze-runner 6.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/bin/bugsnag-print-load-paths +6 -0
  3. data/bin/download-logs +76 -0
  4. data/bin/maze-runner +136 -0
  5. data/bin/upload-app +56 -0
  6. data/lib/features/scripts/await-android-emulator.sh +11 -0
  7. data/lib/features/scripts/clear-android-app-data.sh +8 -0
  8. data/lib/features/scripts/force-stop-android-app.sh +8 -0
  9. data/lib/features/scripts/install-android-app.sh +15 -0
  10. data/lib/features/scripts/launch-android-app.sh +38 -0
  11. data/lib/features/scripts/launch-android-emulator.sh +15 -0
  12. data/lib/features/steps/android_steps.rb +51 -0
  13. data/lib/features/steps/app_automator_steps.rb +228 -0
  14. data/lib/features/steps/aws_sam_steps.rb +212 -0
  15. data/lib/features/steps/breadcrumb_steps.rb +50 -0
  16. data/lib/features/steps/browser_steps.rb +93 -0
  17. data/lib/features/steps/build_api_steps.rb +25 -0
  18. data/lib/features/steps/document_server_steps.rb +7 -0
  19. data/lib/features/steps/error_reporting_steps.rb +342 -0
  20. data/lib/features/steps/feature_flag_steps.rb +190 -0
  21. data/lib/features/steps/header_steps.rb +72 -0
  22. data/lib/features/steps/log_steps.rb +29 -0
  23. data/lib/features/steps/multipart_request_steps.rb +142 -0
  24. data/lib/features/steps/network_steps.rb +75 -0
  25. data/lib/features/steps/payload_steps.rb +234 -0
  26. data/lib/features/steps/proxy_steps.rb +34 -0
  27. data/lib/features/steps/query_parameter_steps.rb +31 -0
  28. data/lib/features/steps/request_assertion_steps.rb +107 -0
  29. data/lib/features/steps/runner_steps.rb +406 -0
  30. data/lib/features/steps/session_tracking_steps.rb +116 -0
  31. data/lib/features/steps/value_steps.rb +119 -0
  32. data/lib/features/support/env.rb +7 -0
  33. data/lib/features/support/internal_hooks.rb +260 -0
  34. data/lib/maze/appium_server.rb +112 -0
  35. data/lib/maze/assertions/request_set_assertions.rb +97 -0
  36. data/lib/maze/aws/sam.rb +112 -0
  37. data/lib/maze/bitbar_devices.rb +84 -0
  38. data/lib/maze/bitbar_utils.rb +112 -0
  39. data/lib/maze/browser_stack_devices.rb +160 -0
  40. data/lib/maze/browser_stack_utils.rb +164 -0
  41. data/lib/maze/browsers_bs.yml +220 -0
  42. data/lib/maze/browsers_cbt.yml +100 -0
  43. data/lib/maze/bugsnag_config.rb +42 -0
  44. data/lib/maze/capabilities.rb +126 -0
  45. data/lib/maze/checks/assert_check.rb +91 -0
  46. data/lib/maze/checks/noop_check.rb +34 -0
  47. data/lib/maze/compare.rb +161 -0
  48. data/lib/maze/configuration.rb +174 -0
  49. data/lib/maze/docker.rb +108 -0
  50. data/lib/maze/document_server.rb +46 -0
  51. data/lib/maze/driver/appium.rb +217 -0
  52. data/lib/maze/driver/browser.rb +138 -0
  53. data/lib/maze/driver/resilient_appium.rb +51 -0
  54. data/lib/maze/errors.rb +20 -0
  55. data/lib/maze/helper.rb +118 -0
  56. data/lib/maze/hooks/appium_hooks.rb +216 -0
  57. data/lib/maze/hooks/browser_hooks.rb +68 -0
  58. data/lib/maze/hooks/command_hooks.rb +9 -0
  59. data/lib/maze/hooks/hooks.rb +61 -0
  60. data/lib/maze/interactive_cli.rb +173 -0
  61. data/lib/maze/logger.rb +73 -0
  62. data/lib/maze/macos_utils.rb +14 -0
  63. data/lib/maze/network.rb +49 -0
  64. data/lib/maze/option/parser.rb +245 -0
  65. data/lib/maze/option/processor.rb +143 -0
  66. data/lib/maze/option/validator.rb +184 -0
  67. data/lib/maze/option.rb +64 -0
  68. data/lib/maze/plugins/bugsnag_reporting_plugin.rb +49 -0
  69. data/lib/maze/plugins/cucumber_report_plugin.rb +101 -0
  70. data/lib/maze/plugins/global_retry_plugin.rb +38 -0
  71. data/lib/maze/proxy.rb +114 -0
  72. data/lib/maze/request_list.rb +82 -0
  73. data/lib/maze/retry_handler.rb +76 -0
  74. data/lib/maze/runner.rb +149 -0
  75. data/lib/maze/sauce_labs_utils.rb +96 -0
  76. data/lib/maze/server.rb +207 -0
  77. data/lib/maze/servlets/base_servlet.rb +22 -0
  78. data/lib/maze/servlets/command_servlet.rb +44 -0
  79. data/lib/maze/servlets/log_servlet.rb +64 -0
  80. data/lib/maze/servlets/reflective_servlet.rb +69 -0
  81. data/lib/maze/servlets/servlet.rb +160 -0
  82. data/lib/maze/smart_bear_utils.rb +71 -0
  83. data/lib/maze/store.rb +15 -0
  84. data/lib/maze/terminating_server.rb +129 -0
  85. data/lib/maze/timers.rb +51 -0
  86. data/lib/maze/wait.rb +35 -0
  87. data/lib/maze.rb +27 -0
  88. metadata +371 -0
@@ -0,0 +1,51 @@
1
+ require 'appium_lib'
2
+ require_relative '../logger'
3
+
4
+ module Maze
5
+ module Driver
6
+ # Handles Appium driver restarts and retries in the case of failure. BrowserStack's iOS 10 and 11 iOS devices in
7
+ # particular seemed prone to the underlying Appium connection failing.
8
+ #
9
+ # For methods available on this class, @see AppAutomateDriver.
10
+ class ResilientAppium
11
+ # Creates the Appium Driver
12
+ #
13
+ # @param server_url [String] URL of the Appium server
14
+ # @param capabilities [Hash] a hash of capabilities to be used in this test run
15
+ # @param locator [Symbol] the primary locator strategy Appium should use to find elements
16
+ def initialize(server_url, capabilities, locator = :id)
17
+ @driver = Appium.new server_url,
18
+ capabilities,
19
+ locator
20
+ end
21
+
22
+ def respond_to_missing?(method_name, include_private = false)
23
+ @driver.respond_to_missing? method_name, include_private
24
+ end
25
+
26
+ def method_missing(method, *args, &block)
27
+ return super unless @driver.respond_to?(method)
28
+
29
+ retries = 0
30
+ until retries >= 5
31
+ begin
32
+ return @driver.send(method, *args, &block)
33
+ rescue Selenium::WebDriver::Error::UnknownError, Selenium::WebDriver::Error::WebDriverError => error
34
+ retries += 1
35
+ $logger.warn 'Appium Error occurred - restarting driver:'
36
+ $logger.warn error
37
+ sleep 3
38
+ restart
39
+ end
40
+ end
41
+
42
+ # Re-raise the last error, although it might be better to re-raise the
43
+ # first error instead. Review based on whether we ever hit this.
44
+ return if error.nil?
45
+
46
+ $logger.error 'Maximum retries exceeded - raising the last error'
47
+ raise error
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,20 @@
1
+ module Maze
2
+ module Error
3
+ # An error raised when an appium element cannot be found
4
+ class AppiumElementNotFoundError < StandardError
5
+
6
+ # @# @!attribute [r] element
7
+ # @return [String] The named element that could not be found
8
+ attr_reader :element
9
+
10
+ # Creates the error
11
+ #
12
+ # @param message [String] The error to display
13
+ # @param element [String] The name of the element that could not be located
14
+ def initialize(message='Element not found', element='No element specified')
15
+ @element = element
16
+ super(message)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest/sha1'
4
+ require 'json'
5
+
6
+ # A collection of helper routines
7
+ module Maze
8
+ # Miscellaneous helper functions.
9
+ module Helper
10
+ class << self
11
+ # Parses a request's query string, because WEBrick doesn't in POST requests
12
+ #
13
+ # @param request [Hash] The received request
14
+ #
15
+ # @return [Hash] The parsed query string.
16
+ def parse_querystring(request)
17
+ CGI.parse(request[:request].query_string)
18
+ end
19
+
20
+ # Enables traversal of a hash using Mongo-style dot notation.
21
+ #
22
+ # @example hash["array"][0]["item"] becomes "hash.array.0.item"
23
+ #
24
+ # @param hash [Hash] The hash to traverse
25
+ # @param key_path [String] The dot notation path within the hash
26
+ #
27
+ # @return [Any] The value found by the key path
28
+ def read_key_path(hash, key_path)
29
+ value = hash
30
+ key_path.split('.').each do |key|
31
+ if key =~ /^(\d+)$/
32
+ key = key.to_i
33
+ if value.length > key
34
+ value = value[key]
35
+ else
36
+ return nil
37
+ end
38
+ else
39
+ if value.key? key
40
+ value = value[key]
41
+ else
42
+ return nil
43
+ end
44
+ end
45
+ end
46
+ value
47
+ end
48
+
49
+ # Determines if the Bugsnag-Integrity header is valid.
50
+ # Whether a missing header is deemed valid depends on @see Maze.config.enforce_bugsnag_integrity.
51
+ #
52
+ # @return [Boolean] True if the header is present and valid, or not present and not enforced. False otherwise.
53
+ def valid_bugsnag_integrity_header(request)
54
+ header = request[:request]['Bugsnag-Integrity']
55
+ return !Maze.config.enforce_bugsnag_integrity if header.nil?
56
+
57
+ digests = request[:digests]
58
+ if header.start_with?('sha1')
59
+ computed_digest = "sha1 #{digests[:sha1]}"
60
+ elsif header.start_with?('simple')
61
+ computed_digest = "simple #{digests[:simple]}"
62
+ else
63
+ return false
64
+ end
65
+ header == computed_digest
66
+ end
67
+
68
+ # Nil-safe version of File.expand_path
69
+ #
70
+ # @param path [String] Path to expand
71
+ #
72
+ # @return [String] Expanded path, or nil if path is nil.
73
+ def expand_path(path)
74
+ return nil unless path
75
+
76
+ File.expand_path path
77
+ end
78
+
79
+ # Helps interpret "@file" arguments. I.e. if the argument starts with an "@",
80
+ # read the contents of the filename given.
81
+ def read_at_arg_file(argument)
82
+ return nil if argument.nil?
83
+ return argument unless argument.start_with? '@'
84
+
85
+ file = argument[1..argument.size]
86
+ File.read file
87
+ end
88
+
89
+ # Returns the current platform all lower-case.
90
+ # @return Typically 'ios', 'android', 'mac' or 'browser'
91
+ def get_current_platform
92
+ if Maze.mode == :browser
93
+ os = 'browser'
94
+ else
95
+ os = case Maze.config.farm
96
+ when :bs
97
+ Maze.config.capabilities['os']
98
+ when :sl
99
+ Maze.driver.capabilities['platformName']
100
+ else
101
+ Maze.config.os
102
+ end
103
+ os = os&.downcase
104
+ end
105
+
106
+ raise('Unable to determine the current platform') if os.nil?
107
+
108
+ os
109
+ end
110
+
111
+ # Returns the name of the scenario to
112
+ # @param string [String] a string to convert to a file name
113
+ def to_friendly_filename(string)
114
+ string.gsub(/[:"& ]/, "_").gsub(/_+/, "_")
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,216 @@
1
+ # Contains logic for the Cucumber hooks when in Appium mode
2
+ module Maze
3
+ module Hooks
4
+ # Hooks for Appium mode use
5
+ class AppiumHooks < InternalHooks
6
+ def before_all
7
+ # Setup Appium capabilities. Note that the 'app' capability is
8
+ # set in a hook as it will change if uploaded to BrowserStack.
9
+
10
+ config = Maze.config
11
+ case config.farm
12
+ when :bs
13
+ tunnel_id = SecureRandom.uuid
14
+ config.app = Maze::BrowserStackUtils.upload_app config.username,
15
+ config.access_key,
16
+ config.app
17
+ Maze::BrowserStackUtils.start_local_tunnel config.bs_local,
18
+ tunnel_id,
19
+ config.access_key
20
+ when :sl
21
+ config.app = Maze::SauceLabsUtils.upload_app config.username,
22
+ config.access_key,
23
+ config.app
24
+ tunnel_id = SecureRandom.uuid
25
+ Maze::SauceLabsUtils.start_sauce_connect config.sl_local,
26
+ tunnel_id,
27
+ config.username,
28
+ config.access_key
29
+ when :bb
30
+ if ENV['BUILDKITE']
31
+ credentials = Maze::BitBarUtils.account_credentials config.tms_uri
32
+ config.username = credentials[:username]
33
+ config.access_key = credentials[:access_key]
34
+ end
35
+ config.app = Maze::BitBarUtils.upload_app config.access_key,
36
+ config.app
37
+ Maze::SmartBearUtils.start_local_tunnel config.sb_local,
38
+ config.username,
39
+ config.access_key
40
+ when :local
41
+ # Attempt to start the local appium server
42
+ appium_uri = URI(config.appium_server_url)
43
+ Maze::AppiumServer.start(address: appium_uri.host, port: appium_uri.port) if config.start_appium
44
+ end
45
+
46
+ start_driver(config, tunnel_id)
47
+
48
+ # Ensure the device is unlocked
49
+ Maze.driver.unlock
50
+
51
+ # Write links to device farm sessions, where applicable
52
+ write_session_link
53
+ end
54
+
55
+ def before
56
+ Maze.driver.start_driver if Maze.config.farm != :none && Maze.config.appium_session_isolation
57
+
58
+ # Launch the app on macOS, if Appium is being used
59
+ Maze.driver.get(Maze.config.app) if Maze.driver && Maze.config.os == 'macos'
60
+ end
61
+
62
+ def after
63
+ if Maze.config.appium_session_isolation
64
+ Maze.driver.driver_quit
65
+ elsif Maze.config.os == 'macos'
66
+ # Close the app - without the sleep, launching the app for the next scenario intermittently fails
67
+ system("killall -KILL #{Maze.config.app} && sleep 1")
68
+ elsif [:bb].include? Maze.config.farm
69
+ Maze.driver.launch_app
70
+ else
71
+ Maze.driver.reset
72
+ end
73
+ end
74
+
75
+ def at_exit
76
+ # Stop the Appium session and server
77
+ Maze.driver.driver_quit unless Maze.config.appium_session_isolation
78
+ Maze::AppiumServer.stop if Maze::AppiumServer.running
79
+
80
+ if Maze.config.farm == :local && Maze.config.os == 'macos'
81
+ # Acquire and output the logs for the current session
82
+ Maze::Runner.run_command("log show --predicate '(process == \"#{Maze.config.app}\")' --style syslog --start '#{Maze.start_time}' > #{Maze.config.app}.log")
83
+ elsif Maze.config.farm == :bs
84
+ Maze::BrowserStackUtils.stop_local_tunnel
85
+ elsif Maze.config.farm == :sl
86
+ $logger.info 'Stopping Sauce Connect'
87
+ Maze::SauceLabsUtils.stop_sauce_connect
88
+ elsif Maze.config.farm == :bb
89
+ Maze::SmartBearUtils.stop_local_tunnel
90
+ Maze::BitBarUtils.release_account(Maze.config.tms_uri) if ENV['BUILDKITE']
91
+ end
92
+ end
93
+
94
+ def write_session_link
95
+ config = Maze.config
96
+ if config.farm == :bs && (config.device || config.browser)
97
+ # Log a link to the BrowserStack session search dashboard
98
+ build = Maze.driver.capabilities[:build]
99
+ url = "https://app-automate.browserstack.com/dashboard/v2/search?query=#{build}&type=builds"
100
+ if ENV['BUILDKITE']
101
+ $logger.info Maze::LogUtil.linkify url, 'BrowserStack session(s)'
102
+ else
103
+ $logger.info "BrowserStack session(s): #{url}"
104
+ end
105
+ end
106
+ end
107
+
108
+ def device_capabilities(config, tunnel_id = nil)
109
+ case config.farm
110
+ when :bs
111
+ capabilities = Maze::Capabilities.for_browser_stack_device config.device,
112
+ tunnel_id,
113
+ config.appium_version,
114
+ config.capabilities_option
115
+ capabilities['app'] = config.app
116
+ when :sl
117
+ capabilities = Maze::Capabilities.for_sauce_labs_device config.device,
118
+ config.os,
119
+ config.os_version,
120
+ tunnel_id,
121
+ config.appium_version,
122
+ config.capabilities_option
123
+ capabilities['app'] = "storage:#{config.app}"
124
+ when :local
125
+ capabilities = Maze::Capabilities.for_local config.os,
126
+ config.capabilities_option,
127
+ config.apple_team_id,
128
+ config.device_id
129
+ capabilities['app'] = config.app
130
+ when :bb
131
+ capabilities = Maze::Capabilities.for_bitbar_device config.access_key,
132
+ config.device,
133
+ config.os,
134
+ config.os_version,
135
+ config.capabilities_option
136
+ capabilities['bitbar_app'] = config.app
137
+ capabilities['bundleId'] = config.app_bundle_id
138
+ end
139
+ capabilities
140
+ end
141
+
142
+ def create_driver(config)
143
+ if Maze.config.resilient
144
+ $logger.info 'Creating ResilientAppium driver instance'
145
+ Maze::Driver::ResilientAppium.new config.appium_server_url,
146
+ config.capabilities,
147
+ config.locator
148
+ else
149
+ $logger.info 'Creating Appium driver instance'
150
+ Maze::Driver::Appium.new config.appium_server_url,
151
+ config.capabilities,
152
+ config.locator
153
+ end
154
+ end
155
+
156
+ def start_driver(config, tunnel_id = nil)
157
+ retry_failure = config.device_list.nil? || config.device_list.empty?
158
+ until Maze.driver
159
+ begin
160
+ config.capabilities = device_capabilities(config, tunnel_id)
161
+ driver = create_driver(config)
162
+
163
+ start_driver_closure = Proc.new do
164
+ begin
165
+ driver.start_driver
166
+ true
167
+ rescue => start_error
168
+ raise start_error unless retry_failure
169
+ false
170
+ end
171
+ end
172
+
173
+ unless config.appium_session_isolation
174
+ if retry_failure
175
+ wait = Maze::Wait.new(interval: 10, timeout: 60)
176
+ success = wait.until(&start_driver_closure)
177
+
178
+ unless success
179
+ $logger.error 'Appium driver failed to start after 6 attempts in 60 seconds'
180
+ raise RuntimeError.new('Appium driver failed to start in 60 seconds')
181
+ end
182
+ else
183
+ start_driver_closure.call
184
+ end
185
+ end
186
+
187
+ # Infer OS version if necessary when running locally
188
+ if Maze.config.farm == :local && Maze.config.os_version.nil?
189
+ version = case Maze.config.os
190
+ when 'android'
191
+ driver.session_capabilities['platformVersion'].to_f
192
+ when 'ios'
193
+ driver.session_capabilities['sdkVersion'].to_f
194
+ end
195
+ $logger.info "Inferred OS version to be #{version}"
196
+ Maze.config.os_version = version
197
+ end
198
+
199
+ Maze.driver = driver
200
+ rescue Selenium::WebDriver::Error::UnknownError => original_exception
201
+ $logger.warn "Attempt to acquire #{config.device} device from farm #{config.farm} failed"
202
+ $logger.warn "Exception: #{original_exception.message}"
203
+ if config.device_list.empty?
204
+ $logger.error 'No further devices to try - raising original exception'
205
+ raise original_exception
206
+ else
207
+ config.device = config.device_list.first
208
+ config.device_list = config.device_list.drop(1)
209
+ $logger.warn "Retrying driver initialisation using next device: #{config.device}"
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,68 @@
1
+ # Contains logic for the Cucumber hooks when in Browser mode
2
+ module Maze
3
+ module Hooks
4
+ # Hooks for Browser mode use
5
+ class BrowserHooks < InternalHooks
6
+ def before_all
7
+ config = Maze.config
8
+ case config.farm
9
+ when :bs
10
+ # BrowserStack browser
11
+ tunnel_id = SecureRandom.uuid
12
+ config.capabilities = Maze::Capabilities.for_browser_stack_browser config.browser,
13
+ tunnel_id,
14
+ config.capabilities_option
15
+ Maze::BrowserStackUtils.start_local_tunnel config.bs_local,
16
+ tunnel_id,
17
+ config.access_key
18
+ when :cbt
19
+ # CrossBrowserTesting browser
20
+ tunnel_id = SecureRandom.uuid
21
+ config.capabilities = Maze::Capabilities.for_cbt_browser config.browser,
22
+ tunnel_id,
23
+ config.capabilities_option
24
+ Maze::SmartBearUtils.start_local_tunnel config.sb_local,
25
+ config.username,
26
+ config.access_key,
27
+ tunnel_id
28
+ end
29
+
30
+ # Create and start the relevant driver
31
+ case config.farm
32
+ when :cbt
33
+ selenium_url = "http://#{config.username}:#{config.access_key}@hub.crossbrowsertesting.com:80/wd/hub"
34
+ Maze.driver = Maze::Driver::Browser.new :remote, selenium_url, config.capabilities
35
+ when :bs
36
+ selenium_url = "http://#{config.username}:#{config.access_key}@hub.browserstack.com/wd/hub"
37
+ Maze.driver = Maze::Driver::Browser.new :remote, selenium_url, config.capabilities
38
+ when :local
39
+ Maze.driver = Maze::Driver::Browser.new Maze.config.browser.to_sym
40
+ end
41
+
42
+ # Write links to device farm sessions, where applicable
43
+ write_session_link
44
+ end
45
+
46
+ def at_exit
47
+ if Maze.config.farm == :bs
48
+ Maze::BrowserStackUtils.stop_local_tunnel
49
+ Maze.driver.driver_quit
50
+ end
51
+ end
52
+
53
+ def write_session_link
54
+ config = Maze.config
55
+ if config.farm == :bs
56
+ # Log a link to the BrowserStack session search dashboard
57
+ build = Maze.driver.capabilities[:build]
58
+ url = "https://automate.browserstack.com/dashboard/v2/search?query=#{build}&type=builds"
59
+ if ENV['BUILDKITE']
60
+ $logger.info Maze::LogUtil.linkify url, 'BrowserStack session(s)'
61
+ else
62
+ $logger.info "BrowserStack session(s): #{url}"
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,9 @@
1
+ # Contains logic for the Cucumber hooks when in Command mode
2
+ module Maze
3
+ module Hooks
4
+ # Hooks for Command mode use
5
+ class CommandHooks < InternalHooks
6
+ # Nothing required at present
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maze
4
+ module Hooks
5
+ # Provides the ability for callbacks to be provided as part of running Cucumber.
6
+ # These are akin to Cucumber's BeforeAll, Before, After and AfterAll hooks, but are invoked in such a way that
7
+ # Maze Runner's hooks do not interfere with callbacks registered by clients.
8
+ class Hooks
9
+ def initialize
10
+ @before_all = []
11
+ @before = []
12
+ @after = []
13
+ end
14
+
15
+ # Register blocks to be called from a Cucumber BeforeAll hook (after MazeRunner does everything it needs to)
16
+ def before_all(&block)
17
+ @before_all << block
18
+ end
19
+
20
+ # Register blocks to be called from a Cucumber Before hook (after MazeRunner does everything it needs to)
21
+ def before(&block)
22
+ @before << block
23
+ end
24
+
25
+ # Register blocks to be called from a Cucumber After hook (before MazeRunner does everything it needs to)
26
+ def after(&block)
27
+ @after << block
28
+ end
29
+
30
+ # For MazeRunner use only, call the registered BeforeAll blocks
31
+ def call_before_all
32
+ @before_all.each(&:call)
33
+ end
34
+
35
+ # For MazeRunner use only, call the registered Before blocks
36
+ # @param scenario The current Cucumber scenario
37
+ def call_before(scenario)
38
+ @before.each { |block| block.call(scenario) }
39
+ end
40
+
41
+ # For MazeRunner use only, call the registered After blocks
42
+ # @param scenario The current Cucumber scenario
43
+ def call_after(scenario)
44
+ @after.each { |block| block.call(scenario) }
45
+ end
46
+ end
47
+
48
+ # Base class for hooks internal to Maze Runner
49
+ class InternalHooks
50
+ def before_all; end
51
+
52
+ def before; end
53
+
54
+ def after; end
55
+
56
+ def after_all; end
57
+
58
+ def at_exit; end
59
+ end
60
+ end
61
+ end