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

action_key(scenario) click to toggle source

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
clear_actions() click to toggle source

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
execute_action(world, action, scenario) click to toggle source

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_action(status, action = nil, options = nil, &block) click to toggle source

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
register_config_actions(world) click to toggle source

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
registered_actions(status, type) click to toggle source

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
resolve_action(namespace, world, method_name) click to toggle source

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
split_string_action(action) click to toggle source

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