Skip to content

Commit

Permalink
[Matcher] Enhance descriptions and failure messages (#223)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mth0158 committed Dec 12, 2023
1 parent aba146e commit 2a8eec7
Show file tree
Hide file tree
Showing 18 changed files with 307 additions and 74 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,6 +18,7 @@ class AspectRatioValidatorMatcher
include AllowBlankable
include Contextable
include Messageable
include Rspecable
include Validatable

def initialize(attribute_name)
Expand All @@ -25,7 +27,11 @@ def initialize(attribute_name)
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 +55,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,19 @@ class AttachedValidatorMatcher
include ActiveStorageable
include Contextable
include Messageable
include Rspecable
include Validatable

def initialize(attribute_name)
@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 +41,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
26 changes: 26 additions & 0 deletions lib/active_storage_validations/matchers/concerns/rspecable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
require "active_support/concern"

module ActiveStorageValidations
module Matchers
module Rspecable
extend ActiveSupport::Concern

def initialize(attribute_name)
super
@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,6 +21,7 @@ class ContentTypeValidatorMatcher
include AllowBlankable
include Contextable
include Messageable
include Rspecable
include Validatable

def initialize(attribute_name)
Expand All @@ -28,7 +30,33 @@ def initialize(attribute_name)
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 build_failure_message(message)
if @allowed_types_not_allowed.present?
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 << " 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
end

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

def allowing(*types)
Expand All @@ -52,22 +80,6 @@ def matches?(subject)
all_rejected_types_rejected?
end

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

if @allowed_types_not_allowed.present?
message << "Accept content types: #{@allowed_types.join(", ")}"
message << "#{@allowed_types_not_allowed.join(", ")} were rejected"
end

if @rejected_types_not_rejected.present?
message << "Reject content types: #{@rejected_types.join(", ")}"
message << "#{@rejected_types_not_rejected.join(", ")} were accepted"
end

message.join("\n")
end

protected

def all_allowed_types_allowed?
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,38 @@ class DimensionValidatorMatcher
include AllowBlankable
include Contextable
include Messageable
include Rspecable
include Validatable

def initialize(attribute_name)
@attribute_name = attribute_name
@width_min = @width_max = @height_min = @height_max = nil
@failure_message_artefacts = []
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 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 width(width)
@width_min = @width_max = width
self
end

def width_min(width)
Expand All @@ -38,8 +62,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 +82,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,14 +106,6 @@ 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 valid_width
Expand All @@ -106,52 +117,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 2a8eec7

Please sign in to comment.