bugsnag-maze-runner 6.27.0

Sign up to get free protection for your applications and to get access to all the features.
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