frodsan/minitest-activemodel

View on GitHub
lib/matchers/validation_matcher.rb

Summary

Maintainability
A
45 mins
Test Coverage
module MiniTest
  module Matchers
    module ActiveModel
      class ValidationMatcher # :nodoc:
        include Helpers

        def initialize attr, type
          @attr = attr
          @type = type
          @expected_on = @expected_message = nil
        end

        # TODO: Add documentation.
        def on *contexts
          @expected_on = clean_contexts contexts
          self
        end

        # TODO: Add documentation.
        def with_message expected_message
          @expected_message = expected_message
          self
        end

        def matches? subject
          @klass     = subject.is_a?(Class) ? subject : subject.class
          @validator = @klass.validators_on(@attr).find { |v| v.kind == @type }
          @result    = true

          check_validator
          check_on        if @expected_on
          check_message   if @expected_message

          @result
        end

        def failure_message
          "Expected #{@klass} to #{description}; instead got #{@negative_message}"
        end

        def negative_failure_message
          "Expected #{@klass} to not #{description}; instead got #{@positive_message}"
        end

        def description
          "validate #{@type} of #{@attr}"
        end

        private

        def check_validator
          if @validator
            @positive_message = "#{@type} validator for #{@attr}"
            @negative_message = "#{@type} validator for #{@attr}"
          else
            @negative_message = "no #{@type} validator for #{@attr}"
            @result = false
          end
        end

        def check_on
          on = clean_contexts @validator.options[:on]

          if on.sort == @expected_on.sort
            @positive_message << " on #{on.empty? ? 'all actions' : to_sentence(on)}"
          else
            @negative_message << " on #{on.empty? ? 'all actions' : to_sentence(on)}"
            @result = false
          end
        end

        def check_message
          error_message = @validator.options[:message]

          if @expected_message == error_message
            @positive_message << " with message: #{error_message.inspect}"
          else
            @negative_message << " with message: #{error_message.inspect}"
            @result = false
          end
        end

        def clean_contexts contexts
          [contexts].flatten.compact.reject { |ctx| ctx == :save }
        end

        # TODO: Document this helper method.
        def validate_invalid_options! *options
          if options.all?(&:nil?)
            raise ArgumentError, 'You have to supply an option for this matcher'
          end
        end
      end
    end
  end
end