module Unobtainium::Cucumber::StatusActions
The StatusActions
module contains all functionality for registering actions to be run when a scenario or outline ended with a particular status.
Constants
- DEFAULTS
Default status actions
- RUNTIME_KEY
Key for storing actions in unobtainium's runtime instance.
Public Instance Methods
For a given scenario, return the matching key for the actions.
# File lib/unobtainium-cucumber/status_actions.rb, line 87 def action_key(scenario) return [ (scenario.passed? ? :passed? : :failed?), (scenario.outline? ? :outline : :scenario) ] end
Partially for testing purposes, clears the action registry.
# File lib/unobtainium-cucumber/status_actions.rb, line 195 def clear_actions if not ::Unobtainium::Runtime.instance.has?(RUNTIME_KEY) # :nocov: return # :nocov: end ::Unobtainium::Runtime.instance.delete(RUNTIME_KEY) end
Given an action and a scenario, execute the action. This includes late/lazy resolution of String or Symbol actions.
# File lib/unobtainium-cucumber/status_actions.rb, line 139 def execute_action(world, action, scenario) # Simplest case first: the action is already callable. if action.respond_to?(:call) return action.call(world, scenario) end # Symbols are almost as easy to handle: they must resolve to a # method, either globally or on the world object. if action.is_a? Symbol meth = resolve_action(Object, world, action) if meth.nil? raise NoMethodError, "Symbol :#{action} could not be resolved "\ "either globally or as part of the cucumber World object, "\ "aborting!" end return meth.call(world, scenario) end # At this point, the action better be a String. if not action.is_a? String raise "Action '#{action}' is not callable, and not a method name. "\ "Aborting!" end # Split module and method name module_name, method_name = split_string_action(action) # If we have no discernable module, use Object instead. the_module = Object if not module_name.nil? and not module_name.empty? the_module = Object.const_get(module_name) end # Try the module we found (i.e. possibly Object) and world for # resolving the method name. meth = resolve_action(the_module, world, method_name) if meth.nil? raise NoMethodError, "Action '#{action}' could not be resolved!" end return meth.call(world, scenario) end
Register a action for a :passed? or :failed? scenario. The :type parameter may either be :scenario or :outline. The action will be passed the matching scenario. If no explicit action is given, a block is also acceptable.
# File lib/unobtainium-cucumber/status_actions.rb, line 35 def register_action(status, action = nil, options = nil, &block) # If the action is a Hash, then the options should be nil. If that's # the case, we have no action and instead got passed options. if action.is_a? Hash if not options.nil? raise "Can't pass a Hash as an action!" end options = action action = nil end # Parameter checks! if not %i[passed? failed?].include?(status) raise "Status may be one of :passed? or :failed? only!" end options ||= {} type = options[:type] || :scenario if not %i[scenario outline].include?(type) raise "The :type option may be one of :scenario or :outline only!" end if action.nil? and block.nil? raise "Must provide either an action method or a block!" end if not action.nil? and not block.nil? raise "Cannot provide both an action method and a block!" end callback = action || block # The key to store callbacks under is comprised of the status # and the type. That way we can match precisely when actions are to # be executed. key = [status, type] # Retrieve existing actions actions = {} if ::Unobtainium::Runtime.instance.has?(RUNTIME_KEY) actions = ::Unobtainium::Runtime.instance.fetch(RUNTIME_KEY) end # Add the callback actions[key] ||= [] actions[key] |= [callback] # And store the callback actions again. ::Unobtainium::Runtime.instance.store(RUNTIME_KEY, actions) end
Automatically register all configured status actions. This is largely an internal function, run after the first scenario and before status actions are executed.
# File lib/unobtainium-cucumber/status_actions.rb, line 98 def register_config_actions(world) to_register = world.config['cucumber.status_actions'] || DEFAULTS %i[passed? failed?].each do |status| for_status = to_register[status] if for_status.nil? # :nocov: next # :nocov: end # If the entry for the status is an Array, it applies to scenarios # and outlines equally. Otherwise we'll have to find a Hash with # entries for either or both. actions = {} if for_status.is_a?(Array) actions[:scenario] = for_status actions[:outline] = for_status elsif for_status.is_a?(Hash) actions[:scenario] = for_status[:scenario] || [] actions[:outline] = for_status[:outline] || [] else # :nocov: raise "Cannot interpret status action configuration for status "\ "#{status}; it should be an Array or Hash, but instead it was"\ " this: #{for_status}" # :nocov: end # Now we have actions for the statuses, we can register them. %i[scenario outline].each do |type| actions[type].each do |action| register_action(status, action, type: type) end end end end
For a given status and type, return the registered actions.
# File lib/unobtainium-cucumber/status_actions.rb, line 184 def registered_actions(status, type) if not ::Unobtainium::Runtime.instance.has?(RUNTIME_KEY) return [] end actions = ::Unobtainium::Runtime.instance.fetch(RUNTIME_KEY) return actions[[status, type]] || [] end
Resolves the given symbol in either of the given namespace or the world object. Returns nil if neither contained the symbol.
# File lib/unobtainium-cucumber/status_actions.rb, line 208 def resolve_action(namespace, world, method_name) method_sym = method_name.to_sym [namespace, world].each do |receiver| begin return receiver.method(method_sym) rescue NameError next end end return nil end
Splits a string action into a module prefix and a method name
# File lib/unobtainium-cucumber/status_actions.rb, line 223 def split_string_action(action) # Try to see whether we have a fully qualified name that requires us # to query a particular module for the method. split_action = action.split(/::/) method_name = split_action.pop # If the method name contains a dot, it uses 'Module.method' notation. # rubocop:disable Lint/EmptyWhen split_method = method_name.split(/\./) case split_method.length when 1 # Do nothing; the method name did not contain a dot when 2 # We have a method name and a module part split_action << split_method[0] method_name = split_method[1] else raise "Too many dots in method name: #{method_name}" end # rubocop:enable Lint/EmptyWhen # Re-join the module name module_name = split_action.join('::') return module_name, method_name end