Skip to content
This repository was archived by the owner on Oct 12, 2023. It is now read-only.
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
64 changes: 32 additions & 32 deletions lib/validation_examples_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,49 @@
require 'rspec/expectations'

module ValidationExamplesMatcher
RSpec::Matchers.define :be_valid_on do |attr_name|
match do |model|
model.send("#{attr_name}=", @value)
model.valid?(@context)
end
def self.register(matchers = RSpec::Matchers)
matchers.define :be_valid_on do |attr_name|
match do |model|
model.send("#{attr_name}=", @value)
model.valid?(@context)
end

chain :with do |value|
@value = value
end
chain :with do |value|
@value = value
end

chain :on_context do |context|
@context = context
end
chain :on_context do |context|
@context = context
end

failure_message do |model|
if model.invalid?(@context)
failure_message do |model|
"expect #{model.class} to be valid, but it is INVALID."
else
"expect #{model.class} to have errors on #{attr_name}, but there is NO ERROR."
end
end
end

RSpec::Matchers.define :be_invalid_on do |attr_name|
match do |model|
model.send("#{attr_name}=", @value)
model.invalid?(@context) && model.errors[attr_name].size >= 1
end
matchers.define :be_invalid_on do |attr_name|
match do |model|
model.send("#{attr_name}=", @value)
model.invalid?(@context) && model.errors[attr_name].size >= 1
end

chain :with do |value|
@value = value
end
chain :with do |value|
@value = value
end

chain :on_context do |context|
@context = context
end
chain :on_context do |context|
@context = context
end

failure_message do |model|
if model.valid?(@context)
"expect #{model.class} to be invalid but it is VALID"
else
"expect #{model.class} to have errors on #{attr_name} but EMPTY"
failure_message do |model|
if model.valid?(@context)
"expect #{model.class} to be invalid, but it is VALID."
else
"expect #{model.class} to have errors on #{attr_name}, but EMPTY."
end
end
end
end

register
end
8 changes: 0 additions & 8 deletions spec/examples/validations_spec.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
require 'spec_helper'

class TestModel
include ActiveModel::Model

attr_accessor :attr
validates :attr, presence: true
validates :attr, length: { maximum: 4 }, on: :a_context
end

RSpec.describe TestModel do
subject { TestModel.new }

Expand Down
37 changes: 37 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,40 @@
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
require 'validation_examples_matcher'
require 'active_model'
require 'pry'

class TestModel
include ActiveModel::Model

attr_accessor :attr
validates :attr, presence: true
validates :attr, length: { maximum: 4 }, on: :a_context
end

class MatchersMock < BasicObject
attr_reader :defined_matchers, :match_blocks, :chain_blocks, :failure_message_blocks
attr_reader :value, :context

def define(name, &block)
@defined_matchers ||= []
@defined_matchers << name
@current = name
instance_exec(:attr, &block)
end

def match(&block)
@match_blocks ||= {}
@match_blocks[@current] = block
end

def chain(name, &block)
@chain_blocks ||= {}
@chain_blocks[@current] ||= {}
@chain_blocks[@current][name] = block
end

def failure_message(&block)
@failure_message_blocks ||= {}
@failure_message_blocks[@current] = block
end
end
85 changes: 85 additions & 0 deletions spec/unit_tests/be_invalid_on_matcher_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
require 'spec_helper'

RSpec.describe 'be_invalid_on matcher' do
let(:matchers) { MatchersMock.new }
before { ValidationExamplesMatcher.register(matchers) }
let(:model) { TestModel.new }

let(:match_block) { matchers.match_blocks[:be_invalid_on] }
let(:chain_blocks) { matchers.chain_blocks[:be_invalid_on] }
let(:with_chain_block) { chain_blocks[:with] }
let(:on_context_chain_block) { chain_blocks[:on_context] }
let(:failure_message_block) { matchers.failure_message_blocks[:be_invalid_on] }

it 'is registered with match block' do
expect(match_block).to be_kind_of(Proc)
end

describe 'match block' do
let(:model) { double('model') }
before { matchers.instance_exec(:a_value, &with_chain_block) }

context 'without on_context chain' do
it "calls model's setter and valid?" do
expect(model).to receive(:attr=).with(:a_value)
expect(model).to receive(:invalid?).with(nil)
matchers.instance_exec(model, &match_block)
end
end

context 'with on_context chain' do
before { matchers.instance_exec(:a_context, &on_context_chain_block) }
it "calls model's setter and valid?" do
expect(model).to receive(:attr=).with(:a_value)
expect(model).to receive(:invalid?).with(:a_context)
matchers.instance_exec(model, &match_block)
end
end
end

it 'is registered with :with chain block' do
expect(with_chain_block).to be_kind_of(Proc)
end

describe 'with chain block' do
it 'sets @value' do
matchers.instance_exec(:a_value, &with_chain_block)
expect(matchers.value).to eq :a_value
end
end

it 'is registered with :on_context chain block' do
expect(on_context_chain_block).to be_kind_of(Proc)
end

describe 'with on_context block' do
it 'sets @context' do
matchers.instance_exec(:a_context, &on_context_chain_block)
expect(matchers.context).to eq :a_context
end
end


it 'is registered with failure_message block' do
expect(failure_message_block).to be_kind_of(Proc)
end

describe 'failure_message block' do
context 'the model is valid' do
before { model.attr = 'ok' }
it 'returns message saying "it should be invalid"' do
expect(matchers.instance_exec(model, &failure_message_block))
.to eq 'expect TestModel to be invalid, but it is VALID.'
end
end

context 'the model is invalid but it has no errors' do
let(:model) { double('model') }
it 'returns message saying "it should have errors"' do
expect(model).to receive(:valid?) { false }
expect(matchers.instance_exec(model, &failure_message_block))
.to eq 'expect RSpec::Mocks::Double to have errors on attr, but EMPTY.'
end
end
end
end
75 changes: 75 additions & 0 deletions spec/unit_tests/be_valid_on_matcher_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
require 'spec_helper'

RSpec.describe 'be_valid_on matcher' do
let(:matchers) { MatchersMock.new }
before { ValidationExamplesMatcher.register(matchers) }
let(:model) { TestModel.new }

let(:match_block) { matchers.match_blocks[:be_valid_on] }
let(:chain_blocks) { matchers.chain_blocks[:be_valid_on] }
let(:with_chain_block) { chain_blocks[:with] }
let(:on_context_chain_block) { chain_blocks[:on_context] }
let(:failure_message_block) { matchers.failure_message_blocks[:be_valid_on] }

it 'is registered with match block' do
expect(match_block).to be_kind_of(Proc)
end

describe 'match block' do
let(:model) { double('model') }
before { matchers.instance_exec(:a_value, &with_chain_block) }

context 'without on_context chain' do
it "calls model's setter and valid?" do
expect(model).to receive(:attr=).with(:a_value)
expect(model).to receive(:valid?).with(nil)
matchers.instance_exec(model, &match_block)
end
end

context 'with on_context chain' do
before { matchers.instance_exec(:a_context, &on_context_chain_block) }
it "calls model's setter and valid?" do
expect(model).to receive(:attr=).with(:a_value)
expect(model).to receive(:valid?).with(:a_context)
matchers.instance_exec(model, &match_block)
end
end
end

it 'is registered with :with chain block' do
expect(with_chain_block).to be_kind_of(Proc)
end

describe 'with chain block' do
it 'sets @value' do
matchers.instance_exec(:a_value, &with_chain_block)
expect(matchers.value).to eq :a_value
end
end

it 'is registered with :on_context chain block' do
expect(on_context_chain_block).to be_kind_of(Proc)
end

describe 'with on_context block' do
it 'sets @context' do
matchers.instance_exec(:a_context, &on_context_chain_block)
expect(matchers.context).to eq :a_context
end
end


it 'is registered with failure_message block' do
expect(failure_message_block).to be_kind_of(Proc)
end

describe 'failure_message block' do
context 'the model is invalid' do
it 'returns message saying "it should be valid"' do
expect(matchers.instance_exec(model, &failure_message_block))
.to eq 'expect TestModel to be valid, but it is INVALID.'
end
end
end
end
11 changes: 11 additions & 0 deletions spec/validation_examples_matcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,15 @@
it 'has a version number' do
expect(ValidationExamplesMatcher::VERSION).not_to be nil
end

let(:matchers) { MatchersMock.new }
before { ValidationExamplesMatcher.register(matchers) }

it 'registers :be_valid_on matcher' do
expect(matchers.defined_matchers).to include(:be_valid_on)
end

it 'registers :be_invalid_on matcher' do
expect(matchers.defined_matchers).to include(:be_invalid_on)
end
end
1 change: 1 addition & 0 deletions validation_examples_matcher.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "rspec", "~> 3.0"
spec.add_development_dependency "activemodel", "~> 4.2.5"
spec.add_development_dependency "pry-byebug", "~> 3.3.0"
end