kareem-h
11/11/2014 - 9:28 PM

states_matcher.rb

# This custom matcher can be used to test state machine
#
# Examples
#
#   it { should have_event(:status, :event_name, [:state1, :state2] => [:state3, :state4]) }
#   it { should have_event(:status, :event_name, {
#                                                   :state1 => :state3,
#                                                   :state1 => :state4,
#                                                   :state3 => :state3,
#                                                   :state2 => :state4
#                                                 })
#   }
RSpec::Matchers.define :have_event do |state_field, event_name, status_flows|
  match do |model|
    @event_name = event_name
    all_transitions = []

    status_flows.each do |from, to|
      from_arr = *from
      to_arr = *to
      from_arr.product(to_arr).each do |from, to|
        all_transitions << OpenStruct({from: from, to: to})
      end
    end

    events = model.class.state_machines[state_field].events
    event = events[event_name]

    @unexpected_transitions = all_transitions.select do |transition|
      !events.valid_for(model, from: transition.from, to: transition.to).include?(event)
    end

    @unexpected_transitions.empty?
  end

  failure_message_for_should do
    unexpected_transitions_str = @unexpected_transitions.map do |t|
                                    "#{@event_name}: :#{t.from} => :#{t.to}"
                                  end.join(', ')
    "there are unexpected transitions in the expectation: [#{unexpected_transitions_str}]"
  end

  def OpenStruct(params)
    params.is_a?(OpenStruct) ? params : OpenStruct.new(params)
  end
end