Skip to content

Commit

Permalink
Merge pull request #233 from igorkasyanchuk/223-investigate-messages-…
Browse files Browse the repository at this point in the history
…available-to-the-dev-when-using-matchers

[Matcher] Enhance descriptions and failure messages (#223)
  • Loading branch information
Mth0158 authored Dec 13, 2023
2 parents aba146e + 96b1a4e commit a32bcfd
Show file tree
Hide file tree
Showing 21 changed files with 318 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require_relative 'concerns/allow_blankable.rb'
require_relative 'concerns/contextable.rb'
require_relative 'concerns/messageable.rb'
require_relative 'concerns/rspecable.rb'
require_relative 'concerns/validatable.rb'

module ActiveStorageValidations
Expand All @@ -17,15 +18,24 @@ class AspectRatioValidatorMatcher
include AllowBlankable
include Contextable
include Messageable
include Rspecable
include Validatable

def initialize(attribute_name)
initialize_allow_blankable
initialize_contextable
initialize_messageable
initialize_rspecable
@attribute_name = attribute_name
@allowed_aspect_ratios = @rejected_aspect_ratios = []
end

def description
"validate the aspect ratios allowed on attachment #{@attribute_name}."
"validate the aspect ratios allowed on :#{@attribute_name}."
end

def failure_message
"is expected to validate aspect ratio of :#{@attribute_name}"
end

def allowing(*aspect_ratios)
Expand All @@ -49,10 +59,6 @@ def matches?(subject)
all_rejected_aspect_ratios_rejected?
end

def failure_message
"is expected to validate aspect ratio of #{@attribute_name}"
end

protected

def all_allowed_aspect_ratios_allowed?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require_relative 'concerns/active_storageable.rb'
require_relative 'concerns/contextable.rb'
require_relative 'concerns/messageable.rb'
require_relative 'concerns/rspecable.rb'
require_relative 'concerns/validatable.rb'

module ActiveStorageValidations
Expand All @@ -15,14 +16,22 @@ class AttachedValidatorMatcher
include ActiveStorageable
include Contextable
include Messageable
include Rspecable
include Validatable

def initialize(attribute_name)
initialize_contextable
initialize_messageable
initialize_rspecable
@attribute_name = attribute_name
end

def description
"validate #{@attribute_name} must be attached"
"validate that :#{@attribute_name} must be attached"
end

def failure_message
"is expected to validate attachment of :#{@attribute_name}"
end

def matches?(subject)
Expand All @@ -35,14 +44,6 @@ def matches?(subject)
is_invalid_when_file_not_attached?
end

def failure_message
"is expected to validate attached of #{@attribute_name}"
end

def failure_message_when_negated
"is expected to not validate attached of #{@attribute_name}"
end

private

def is_valid_when_file_attached?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ module Matchers
module AllowBlankable
extend ActiveSupport::Concern

def initialize(attribute_name)
super
def initialize_allow_blankable
@allow_blank = nil
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ module Matchers
module Contextable
extend ActiveSupport::Concern

def initialize(attribute_name)
super
def initialize_contextable
@context = nil
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ module Matchers
module Messageable
extend ActiveSupport::Concern

def initialize(attribute_name)
super
def initialize_messageable
@custom_message = nil
end

Expand Down
25 changes: 25 additions & 0 deletions lib/active_storage_validations/matchers/concerns/rspecable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
require "active_support/concern"

module ActiveStorageValidations
module Matchers
module Rspecable
extend ActiveSupport::Concern

def initialize_rspecable
@failure_message_artefacts = []
end

def description
raise NotImplementedError, "#{self.class} did not define #{__method__}"
end

def failure_message
raise NotImplementedError, "#{self.class} did not define #{__method__}"
end

def failure_message_when_negated
failure_message.sub(/is expected to validate/, 'is expected not to validate')
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
require_relative 'concerns/allow_blankable.rb'
require_relative 'concerns/contextable.rb'
require_relative 'concerns/messageable.rb'
require_relative 'concerns/rspecable.rb'
require_relative 'concerns/validatable.rb'

module ActiveStorageValidations
Expand All @@ -20,15 +21,26 @@ class ContentTypeValidatorMatcher
include AllowBlankable
include Contextable
include Messageable
include Rspecable
include Validatable

def initialize(attribute_name)
initialize_allow_blankable
initialize_contextable
initialize_messageable
initialize_rspecable
@attribute_name = attribute_name
@allowed_types = @rejected_types = []
end

def description
"validate the content types allowed on attachment #{@attribute_name}"
"validate the content types allowed on :#{@attribute_name}"
end

def failure_message
message = ["is expected to validate the content types of :#{@attribute_name}"]
build_failure_message(message)
message.join("\n")
end

def allowing(*types)
Expand All @@ -52,23 +64,27 @@ def matches?(subject)
all_rejected_types_rejected?
end

def failure_message
message = ["Expected #{@attribute_name}"]
protected

def build_failure_message(message)
if @allowed_types_not_allowed.present?
message << "Accept content types: #{@allowed_types.join(", ")}"
message << "#{@allowed_types_not_allowed.join(", ")} were rejected"
message << " the following content type#{'s' if @allowed_types.count > 1} should be allowed: :#{@allowed_types.join(", :")}"
message << " but #{pluralize(@allowed_types_not_allowed)} rejected"
end

if @rejected_types_not_rejected.present?
message << "Reject content types: #{@rejected_types.join(", ")}"
message << "#{@rejected_types_not_rejected.join(", ")} were accepted"
message << " the following content type#{'s' if @rejected_types.count > 1} should be rejected: :#{@rejected_types.join(", :")}"
message << " but #{pluralize(@rejected_types_not_rejected)} accepted"
end

message.join("\n")
end

protected
def pluralize(types)
if types.count == 1
":#{types[0]} was"
else
":#{types.join(", :")} were"
end
end

def all_allowed_types_allowed?
@allowed_types_not_allowed ||= @allowed_types.reject { |type| type_allowed?(type) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require_relative 'concerns/allow_blankable.rb'
require_relative 'concerns/contextable.rb'
require_relative 'concerns/messageable.rb'
require_relative 'concerns/rspecable.rb'
require_relative 'concerns/validatable.rb'

module ActiveStorageValidations
Expand All @@ -17,15 +18,31 @@ class DimensionValidatorMatcher
include AllowBlankable
include Contextable
include Messageable
include Rspecable
include Validatable

def initialize(attribute_name)
initialize_allow_blankable
initialize_contextable
initialize_messageable
initialize_rspecable
@attribute_name = attribute_name
@width_min = @width_max = @height_min = @height_max = nil
end

def description
"validate image dimensions of #{@attribute_name}"
"validate the image dimensions of :#{@attribute_name}"
end

def failure_message
message = ["is expected to validate dimensions of :#{@attribute_name}"]
build_failure_message(message)
message.join("\n")
end

def width(width)
@width_min = @width_max = width
self
end

def width_min(width)
Expand All @@ -38,8 +55,13 @@ def width_max(width)
self
end

def width(width)
@width_min = @width_max = width
def width_between(range)
@width_min, @width_max = range.first, range.last
self
end

def height(height)
@height_min = @height_max = height
self
end

Expand All @@ -53,21 +75,11 @@ def height_max(height)
self
end

def width_between(range)
@width_min, @width_max = range.first, range.last
self
end

def height_between(range)
@height_min, @height_max = range.first, range.last
self
end

def height(height)
@height_min = @height_max = height
self
end

def matches?(subject)
@subject = subject.is_a?(Class) ? subject.new : subject

Expand All @@ -87,16 +99,18 @@ def matches?(subject)
height_equals?
end

def failure_message
<<~MESSAGE
is expected to validate dimensions of #{@attribute_name}
width between #{@width_min} and #{@width_max}
height between #{@height_min} and #{@height_max}
MESSAGE
end

protected

def build_failure_message(message)
return unless @failure_message_artefacts.present?

message << " but there seem to have issues with the matcher methods you used, since:"
@failure_message_artefacts.each do |error_case|
message << " validation failed when provided with a #{error_case[:width]}x#{error_case[:height]}px test image"
end
message << " whereas it should have passed"
end

def valid_width
((@width_min || 0) + (@width_max || 2000)) / 2
end
Expand All @@ -106,52 +120,57 @@ def valid_height
end

def width_not_smaller_than_min?
@width_min.nil? || !passes_validation_with_dimensions(@width_min - 1, valid_height, 'width')
@width_min.nil? || !passes_validation_with_dimensions(@width_min - 1, valid_height)
end

def width_larger_than_min?
@width_min.nil? || @width_min == @width_max || passes_validation_with_dimensions(@width_min + 1, valid_height, 'width')
@width_min.nil? || @width_min == @width_max || passes_validation_with_dimensions(@width_min + 1, valid_height)
end

def width_smaller_than_max?
@width_max.nil? || @width_min == @width_max || passes_validation_with_dimensions(@width_max - 1, valid_height, 'width')
@width_max.nil? || @width_min == @width_max || passes_validation_with_dimensions(@width_max - 1, valid_height)
end

def width_not_larger_than_max?
@width_max.nil? || !passes_validation_with_dimensions(@width_max + 1, valid_height, 'width')
@width_max.nil? || !passes_validation_with_dimensions(@width_max + 1, valid_height)
end

def width_equals?
@width_min.nil? || @width_min != @width_max || passes_validation_with_dimensions(@width_min, valid_height, 'width')
@width_min.nil? || @width_min != @width_max || passes_validation_with_dimensions(@width_min, valid_height)
end

def height_not_smaller_than_min?
@height_min.nil? || !passes_validation_with_dimensions(valid_width, @height_min - 1, 'height')
@height_min.nil? || !passes_validation_with_dimensions(valid_width, @height_min - 1)
end

def height_larger_than_min?
@height_min.nil? || @height_min == @height_max || passes_validation_with_dimensions(valid_width, @height_min + 1, 'height')
@height_min.nil? || @height_min == @height_max || passes_validation_with_dimensions(valid_width, @height_min + 1)
end

def height_smaller_than_max?
@height_max.nil? || @height_min == @height_max || passes_validation_with_dimensions(valid_width, @height_max - 1, 'height')
@height_max.nil? || @height_min == @height_max || passes_validation_with_dimensions(valid_width, @height_max - 1)
end

def height_not_larger_than_max?
@height_max.nil? || !passes_validation_with_dimensions(valid_width, @height_max + 1, 'height')
@height_max.nil? || !passes_validation_with_dimensions(valid_width, @height_max + 1)
end

def height_equals?
@height_min.nil? || @height_min != @height_max || passes_validation_with_dimensions(valid_width, @height_min, 'height')
@height_min.nil? || @height_min != @height_max || passes_validation_with_dimensions(valid_width, @height_min)
end

def passes_validation_with_dimensions(width, height, check)
def passes_validation_with_dimensions(width, height)
mock_dimensions_for(attach_file, width, height) do
validate
is_valid?
is_valid? || add_failure_message_artefact(width, height)
end
end

def add_failure_message_artefact(width, height)
@failure_message_artefacts << { width: width, height: height }
false
end

def is_custom_message_valid?
return true unless @custom_message

Expand Down
Loading

0 comments on commit a32bcfd

Please sign in to comment.