albertosaurus/power_enum

View on GitHub
lib/testing/rspec.rb

Summary

Maintainability
A
45 mins
Test Coverage
if defined? RSpec
  require 'rspec/expectations'

  # This is used to test that a model acts as enumerated.  Example:
  #
  #     describe BookingStatus do
  #       it { should act_as_enumerated }
  #     end
  #
  # This also works:
  #
  #     describe BookingStatus do
  #       it "should act as enumerated" do
  #         BookingStatus.should act_as_enumerated
  #       end
  #     end
  #
  # You can use the `with_items` chained matcher to test that each enum is properly seeded:
  #
  #     describe BookingStatus do
  #       it {
  #         should act_as_enumerated.with_items(:confirmed, :received, :rejected)
  #       }
  #     end
  #
  # You can also pass in hashes if you want to be thorough and test out all the attributes of each enum.  If
  # you do this, you must pass in the `:name` attribute in each hash
  #
  #     describe BookingStatus do
  #       it {
  #         should act_as_enumerated.with_items({ :name => 'confirmed', :description => "Processed and confirmed" },
  #                                             { :name => 'received', :description => "Pending confirmation" },
  #                                             { :name => 'rejected', :description => "Rejected due to internal rules" })
  #       }
  #     end
  RSpec::Matchers.define :act_as_enumerated do

    chain :with_items do |*args|
      @items = args
    end

    match do |enum|
      enum_class = get_enum_class(enum)

      if enum_class.respond_to?(:acts_as_enumerated?) &&
          enum_class.acts_as_enumerated?

        if @items
          begin
            @items.all? { |item| validate_enum(enum_class, item) }
          rescue Exception
            false
          end
        else
          true
        end
      else
        false
      end
    end

    # Returns the class of <tt>enum</tt>, or enum if it's a class.
    def get_enum_class(enum)
      if enum.is_a?(Class)
         enum
       else
         enum.class
       end
    end

    # Validates the given enum.
    def validate_enum(enum_class, item)
      case item
      when String, Symbol, Fixnum
        enum_class[item].present?
      when Hash
        name = item[:name]
        if (e = enum_class[name]).present?
          item.all?{ |attribute, value|
            e.send(attribute) == value
          }
        else
          false
        end
      else
        false
      end
    end

    failure_message_for_should do
      message = "should act as enumerated"
      if @items
        message << " and have members #{@items.inspect}"
      end
      message
    end

    failure_message_for_should_not do
      message = "should not act as enumerated"
      if @items
        message << " with members #{@items.inspect}"
      end
      message
    end

    description do
      "act as enumerated"
    end
  end

  # This is used to test that a model has enumerated the given attribute:
  #
  #     describe Booking do
  #       it { should have_enumerated(:status) }
  #     end
  #
  # This is also valid:
  #
  #     describe Booking do
  #       it "Should have enumerated the status attribute" do
  #         Booking.should have_enumerated(:status)
  #       end
  #     end
  RSpec::Matchers.define :have_enumerated do |attribute|
    match do |model|
      model_class = if model.is_a?(Class)
                      model
                    else
                      model.class
                    end
      model_class.has_enumerated?(attribute)
    end

    failure_message_for_should do
      "expected #{attribute} to be an enumerated attribute"
    end

    failure_message_for_should_not do
      "expected #{attribute} to not be an enumerated attribute"
    end

    description do
      "have enumerated #{attribute}"
    end
  end

  # Tests if an enum instance matches the given value, which may be a symbol,
  # id, string, or enum instance:
  #
  #     describe Booking do
  #       it "status should be 'received' for a new booking" do
  #         Booking.new.status.should match_enum(:received)
  #       end
  #     end
  RSpec::Matchers.define :match_enum do |attribute|
    match do |item|
      if item.class.respond_to?(:acts_as_enumerated?) &&
          item.class.acts_as_enumerated?
        begin
          item.class[attribute] == item
        rescue Exception
          false
        end
      else
        false
      end
    end

    failure_message_for_should do
      "expected #{attribute} to match the enum"
    end

    failure_message_for_should_not do
      "expected #{attribute} to not match the enum"
    end

    description do
      "match enum value of #{attribute}"
    end
  end

end