Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add #have_error spec expectation #1062

Merged
merged 3 commits into from
Aug 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions spec/avram/expectations/have_custom_error_expectation_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
require "../../spec_helper"

private class SaveEmailAddress < EmailAddress::SaveOperation
end

include Avram::Expectations

describe Avram::Expectations::HaveCustomErrorExpectation do
describe "#have_error" do
context "in positive assertions" do
it "passes if attribute is invalid" do
operation = SaveEmailAddress.new
operation.add_error(:providers, "is empty")

operation.should have_error
operation.should have_error("is empty")
operation.should have_error(/\sempty/)

operation.should have_error(:providers)
operation.should have_error(:providers, "is empty")
operation.should have_error(:providers, /\sempty/)
end

it "fails if attribute is valid" do
operation = SaveEmailAddress.new

expect_raises Spec::AssertionFailed, "have an error" do
operation.should have_error(:providers)
end

expect_raises Spec::AssertionFailed, "have the error " do
operation.should have_error(:providers, "is empty")
end

expect_raises Spec::AssertionFailed, "have the error " do
operation.should have_error(:providers, /\sempty/)
end
end

it "fails if attribute is invalid but without the given message" do
operation = SaveEmailAddress.new
operation.add_error(:providers, "is empty")

expect_raises Spec::AssertionFailed, "have the error " do
operation.should have_error("wrong message")
end

expect_raises Spec::AssertionFailed, "have the error " do
operation.should have_error(/\smessage/)
end

expect_raises Spec::AssertionFailed, "have the error " do
operation.should have_error(:providers, "wrong message")
end

expect_raises Spec::AssertionFailed, "have the error " do
operation.should have_error(:providers, /\smessage/)
end
end
end

context "in negative assertions" do
it "passes if attribute is valid" do
operation = SaveEmailAddress.new

operation.should_not have_error(:providers)
operation.should_not have_error(:providers, "is empty")
operation.should_not have_error(:providers, /\sempty/)
end

it "fails if attribute is invalid" do
operation = SaveEmailAddress.new
operation.add_error(:providers, "is empty")

expect_raises Spec::AssertionFailed, "not have an error" do
operation.should_not have_error
end

expect_raises Spec::AssertionFailed, "not have the error " do
operation.should_not have_error("is empty")
end

expect_raises Spec::AssertionFailed, "not have the error " do
operation.should_not have_error(/\sempty/)
end

expect_raises Spec::AssertionFailed, "not have an error" do
operation.should_not have_error(:providers)
end

expect_raises Spec::AssertionFailed, "not have the error " do
operation.should_not have_error(:providers, "is empty")
end

expect_raises Spec::AssertionFailed, "not have the error " do
operation.should_not have_error(:providers, /\sempty/)
end
end

it "passes if attribute is invalid but without the given message" do
operation = SaveEmailAddress.new
operation.add_error(:providers, "is required")

operation.should_not have_error("wrong message")
operation.should_not have_error(/\smessage/)

operation.should_not have_error(:providers, "wrong message")
operation.should_not have_error(:providers, /\smessage/)
end
end
end
end
128 changes: 128 additions & 0 deletions spec/avram/expectations/have_error_expectation_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
require "../../spec_helper"

private class SaveEmailAddress < EmailAddress::SaveOperation
end

include Avram::Expectations

describe Avram::Expectations::HaveErrorExpectation do
describe "#have_error" do
context "in positive assertions" do
it "passes if attribute is invalid" do
operation = SaveEmailAddress.new
operation.address.add_error("is required")

operation.should have_error
operation.should have_error("is required")
operation.should have_error(/\srequired/)

operation.address.should have_error
operation.address.should have_error("is required")
operation.address.should have_error(/\srequired/)
end

it "fails if attribute is valid" do
operation = SaveEmailAddress.new

expect_raises Spec::AssertionFailed, "have an error" do
operation.should have_error
end

expect_raises Spec::AssertionFailed, "have the error" do
operation.should have_error("is required")
end

expect_raises Spec::AssertionFailed, "have the error" do
operation.should have_error(/\srequired/)
end

expect_raises Spec::AssertionFailed, "have an error" do
operation.address.should have_error
end

expect_raises Spec::AssertionFailed, "have the error " do
operation.address.should have_error("is required")
end

expect_raises Spec::AssertionFailed, "have the error " do
operation.address.should have_error(/\srequired/)
end
end

it "fails if attribute is invalid but without the given message" do
operation = SaveEmailAddress.new
operation.address.add_error("is required")

expect_raises Spec::AssertionFailed, "have the error " do
operation.should have_error("wrong message")
end

expect_raises Spec::AssertionFailed, "have the error " do
operation.should have_error(/\smessage/)
end

expect_raises Spec::AssertionFailed, "have the error " do
operation.address.should have_error("wrong message")
end

expect_raises Spec::AssertionFailed, "have the error " do
operation.address.should have_error(/\smessage/)
end
end
end

context "in negative assertions" do
it "passes if attribute is valid" do
operation = SaveEmailAddress.new

operation.should_not have_error
operation.should_not have_error("is required")
operation.should_not have_error(/\srequired/)

operation.address.should_not have_error
operation.address.should_not have_error("is required")
operation.address.should_not have_error(/\srequired/)
end

it "fails if attribute is invalid" do
operation = SaveEmailAddress.new
operation.address.add_error("is required")

expect_raises Spec::AssertionFailed, "not have an error" do
operation.should_not have_error
end

expect_raises Spec::AssertionFailed, "not have the error " do
operation.should_not have_error("is required")
end

expect_raises Spec::AssertionFailed, "not have the error " do
operation.should_not have_error(/\srequired/)
end

expect_raises Spec::AssertionFailed, "not have an error" do
operation.address.should_not have_error
end

expect_raises Spec::AssertionFailed, "not have the error " do
operation.address.should_not have_error("is required")
end

expect_raises Spec::AssertionFailed, "not have the error " do
operation.address.should_not have_error(/\srequired/)
end
end

it "passes if attribute is invalid but without the given message" do
operation = SaveEmailAddress.new
operation.address.add_error("is required")

operation.should_not have_error("wrong message")
operation.should_not have_error(/\smessage/)

operation.address.should_not have_error("wrong message")
operation.address.should_not have_error(/\smessage/)
end
end
end
end
35 changes: 35 additions & 0 deletions src/avram/expectations.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module Avram::Expectations
# Tests that an operation or attribute has an error
#
# ```
# CreateReceipt.create(params) do |operation, receipt|
# receipt.should be_nil
#
# operation.should have_error
# operation.should have_error("is required")
# operation.should have_error(/\srequired/)
#
# operation.user_id.should have_error
# operation.user_id.should have_error("is required")
# operation.user_id.should have_error(/\srequired/)
# end
# ```
def have_error(message = nil)
HaveErrorExpectation.new(message)
end

# Tests that an operation has a custom error
#
# ```
# CreateUser.create(params) do |operation, user|
# user.should be_nil
#
# operation.should have_error(:roles)
# operation.should have_error(:roles, "is empty")
# operation.should have_error(:roles, /\sempty/)
# end
# ```
def have_error(name : Symbol, message = nil)
HaveCustomErrorExpectation.new(name, message)
end
end
35 changes: 35 additions & 0 deletions src/avram/expectations/have_custom_error_expectation.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module Avram::Expectations
struct HaveCustomErrorExpectation
def initialize(@name : Symbol, @message : Regex? = nil)
end

def self.new(name, message : String)
new(name, /#{message}/)
end

def match(operation : OperationErrors) : Bool
return false unless errors = operation.custom_errors[@name]?
@message.try { |message| return errors.any?(&.=~ message) }
!errors.empty?
end

def failure_message(operation : OperationErrors) : String
@message.try do |message|
return "Expected :#{@name} to have the error '#{message.source}'"
end

"Expected :#{@name} to have an error"
end

def negative_failure_message(operation : OperationErrors) : String
@message.try do |message|
return "Expected :#{@name} to not have the error '#{message.source}'"
end

<<-MSG
Expected :#{@name} to not have an error, got errors:
#{HaveErrorExpectation.list(operation.custom_errors[@name])}
MSG
end
end
end
81 changes: 81 additions & 0 deletions src/avram/expectations/have_error_expectation.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
module Avram::Expectations
struct HaveErrorExpectation
def initialize(@message : Regex? = nil)
end

def self.new(message : String)
new(/#{message}/)
end

def match(attribute : Attribute) : Bool
@message.try do |message|
return attribute.errors.any?(&.=~ message)
end

!attribute.errors.empty?
end

def match(operation : OperationErrors) : Bool
@message.try do |message|
return operation.errors.flat_map { |_, errors| errors }.any? do |error|
error =~ message
end
end

!operation.errors.empty?
end

def failure_message(attribute : Attribute) : String
@message.try do |message|
return "Expected :#{attribute.name} to have the error '#{message.source}'"
end

"Expected :#{attribute.name} to have an error"
end

def failure_message(operation : OperationErrors) : String
@message.try do |message|
return "Expected operation to have the error '#{message.source}'"
end

"Expected operation to have an error"
end

def negative_failure_message(attribute : Attribute) : String
@message.try do |message|
return "Expected :#{attribute.name} to not have the error '#{message.source}'"
end

<<-MSG
Expected :#{attribute.name} to not have an error, got errors:
#{self.class.list(attribute.errors)}
MSG
end

def negative_failure_message(operation : OperationErrors) : String
@message.try do |message|
return "Expected operation to not have the error '#{message.source}'"
end

<<-MSG
Expected operation to not have an error, got errors:
#{self.class.list(operation.errors)}
MSG
end

protected def self.list(errors : Hash)
errors.join do |name, _errors|
list _errors.map { |error| "#{name}: #{error}" }
end
end

protected def self.list(errors : Array)
errors.join do |error|
<<-ERROR
- #{error}

ERROR
end
end
end
end