Skip to content

Commit

Permalink
Add have_rich_text_matcher
Browse files Browse the repository at this point in the history
The `have_rich_text` matcher tests usage of the `has_rich_text` macro that was added in Rails 6.
  • Loading branch information
vsppedro authored and mcmire committed Jan 23, 2020
1 parent 0b49b57 commit 0b23942
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Appraisals
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ if Gem::Requirement.new('>= 2.5.0').satisfied_by?(Gem::Version.new(RUBY_VERSION)
gem 'selenium-webdriver'
gem 'webdrivers'

# Other dependencies
gem 'actiontext', '~> 6.0.2.1'

# Database adapters
gem 'pg', '>= 0.18', '< 2.0'
gem 'sqlite3', '~> 1.4'
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,8 @@ about any of them, make sure to [consult the documentation][rubydocs]!
tests your `has_one` associations.
* **[have_readonly_attribute](lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb)**
tests usage of the `attr_readonly` macro.
* **[have_rich_text](lib/shoulda/matchers/active_record/have_rich_text_matcher.rb)**
tests your `has_rich_text` associations.
* **[serialize](lib/shoulda/matchers/active_record/serialize_matcher.rb)** tests
usage of the `serialize` macro.
* **[validate_uniqueness_of](lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb)**
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rails_6_0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ gem "listen", ">= 3.0.5", "< 3.2"
gem "spring-watcher-listen", "~> 2.0.0"
gem "selenium-webdriver"
gem "webdrivers"
gem "actiontext", "~> 6.0.2.1"
gem "pg", ">= 0.18", "< 2.0"
gem "sqlite3", "~> 1.4"
1 change: 1 addition & 0 deletions gemfiles/rails_6_0.gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ PLATFORMS
ruby

DEPENDENCIES
actiontext (~> 6.0.2.1)
appraisal (= 2.2.0)
bcrypt (~> 3.1.7)
bootsnap (>= 1.4.2)
Expand Down
1 change: 1 addition & 0 deletions lib/shoulda/matchers/active_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
require "shoulda/matchers/active_record/have_db_column_matcher"
require "shoulda/matchers/active_record/have_db_index_matcher"
require "shoulda/matchers/active_record/have_readonly_attribute_matcher"
require "shoulda/matchers/active_record/have_rich_text_matcher"
require "shoulda/matchers/active_record/have_secure_token_matcher"
require "shoulda/matchers/active_record/serialize_matcher"
require "shoulda/matchers/active_record/accept_nested_attributes_for_matcher"
Expand Down
79 changes: 79 additions & 0 deletions lib/shoulda/matchers/active_record/have_rich_text_matcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
module Shoulda
module Matchers
module ActiveRecord
# The `have_rich_text` matcher tests usage of the
# `has_rich_text` macro.
#
# #### Example
#
# class Post < ActiveRecord
# has_rich_text :content
# end
#
# # RSpec
# RSpec.describe Post, type: :model do
# it { is_expected.to have_rich_text(:content) }
# end
#
# # Minitest (Shoulda)
# class PostTest < ActiveSupport::TestCase
# should have_rich_text(:content)
# end
#
# @return [HaveRichText]
#
def have_rich_text(rich_text_attribute)
HaveRichText.new(rich_text_attribute)
end

# @private
class HaveRichText
def initialize(rich_text_attribute)
@rich_text_attribute = rich_text_attribute
end

def description
"have configured :#{rich_text_attribute} as a ActionText::RichText association"
end

def failure_message
"Expected #{subject.class} to #{error_description}"
end

def failure_message_when_negated
"Did not expect #{subject.class} to have ActionText::RichText :#{rich_text_attribute}"
end

def matches?(subject)
@subject = subject
@error = run_checks
@error.nil?
end

private

attr_reader :error, :rich_text_attribute, :subject

def run_checks
if !has_attribute?
":#{rich_text_attribute} does not exist"
elsif !has_expected_action_text?
:default
end
end

def has_attribute?
@subject.respond_to?(rich_text_attribute.to_s)
end

def has_expected_action_text?
@subject.send(rich_text_attribute).class.name == 'ActionText::RichText'
end

def error_description
error == :default ? description : "#{description} but #{error}"
end
end
end
end
end
11 changes: 11 additions & 0 deletions spec/support/unit/rails_application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ def create

def load
load_environment

if rails_version > 5 && bundle.includes?('actiontext')
add_action_text_migration
end

run_migrations
end

Expand Down Expand Up @@ -165,6 +170,12 @@ class DevelopmentRecord < ActiveRecord::Base
TEXT
end

def add_action_text_migration
fs.within_project do
run_command! 'bundle exec rake action_text:install:migrations'
end
end

def add_initializer_for_time_zone_aware_types
path = 'config/initializers/configure_time_zone_aware_types.rb'
fs.write(path, <<-TEXT)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
require 'unit_spec_helper'

describe Shoulda::Matchers::ActiveRecord::HaveRichText, type: :model do
def self.rich_text_is_defined?
defined?(ActionText::RichText)
end

context '#description' do
it 'returns the message including the name of the provided association' do
matcher = have_rich_text(:content)
expect(matcher.description).
to eq('have configured :content as a ActionText::RichText association')
end
end

if rich_text_is_defined?
context 'when the model has a RichText association' do
it 'matches when the subject configures has_rich_text' do
valid_record = new_post(is_rich_text_association: true)

expected_message = 'Did not expect Post to have ActionText::RichText :content'

expect { have_rich_text(:content) }.
to match_against(valid_record).
or_fail_with(expected_message)
end
end

context 'when the model does not have a RichText association' do
it 'does not match when provided with a model attribute that exist' do
invalid_record = new_post(has_invalid_content: true)
expected_message = 'Expected Post to have configured :invalid_content as a ' \
'ActionText::RichText association'

expect { have_rich_text(:invalid_content) }.
not_to match_against(invalid_record).
and_fail_with(expected_message)
end

it 'does not match when provided with a model attribute that does not exist' do
invalid_record = new_post
expected_message = 'Expected Post to have configured :invalid_attribute as a ' \
'ActionText::RichText association but :invalid_attribute does not exist'

expect { have_rich_text(:invalid_attribute) }.
not_to match_against(invalid_record).
and_fail_with(expected_message)
end
end
else
it 'does not match when provided with a model attribute that exist' do
invalid_record = new_post(has_invalid_content: true)
expected_message = 'Expected Post to have configured :invalid_content as a ' \
'ActionText::RichText association'

expect { have_rich_text(:invalid_content) }.
not_to match_against(invalid_record).
and_fail_with(expected_message)
end

it 'does not match when provided with a model attribute that does not exist' do
invalid_record = new_post
expected_message = 'Expected Post to have configured :invalid_attribute as a ' \
'ActionText::RichText association but :invalid_attribute does not exist'

expect { have_rich_text(:invalid_attribute) }.
not_to match_against(invalid_record).
and_fail_with(expected_message)
end
end

def new_post(has_invalid_content: false, is_rich_text_association: false)
columns = {}

if has_invalid_content
columns[:invalid_content] = :string
end

define_model 'Post', columns do
if is_rich_text_association
has_rich_text :content
end
end.new
end
end

0 comments on commit 0b23942

Please sign in to comment.