Commit 87b51c24 by Shu Fujita Committed by Andrew W. Lee

Refactor RSpec for AnnotateModels (1) (#726)

I refactored and structuralized RSpec test cases of AnnotateModels for readability and scalability because it was too complex to read. cf. #718 In this PR, I refactored test cases of some methods in `AnnotateModels`. I will refactor test cases of other methods in another PR.
parent 1c5deb09
...@@ -6,6 +6,20 @@ require 'active_support/core_ext/string' ...@@ -6,6 +6,20 @@ require 'active_support/core_ext/string'
require 'files' require 'files'
describe AnnotateModels do # rubocop:disable Metrics/BlockLength describe AnnotateModels do # rubocop:disable Metrics/BlockLength
MAGIC_COMMENTS = [
'# encoding: UTF-8',
'# coding: UTF-8',
'# -*- coding: UTF-8 -*-',
'#encoding: utf-8',
'# encoding: utf-8',
'# -*- encoding : utf-8 -*-',
"# encoding: utf-8\n# frozen_string_literal: true",
"# frozen_string_literal: true\n# encoding: utf-8",
'# frozen_string_literal: true',
'#frozen_string_literal: false',
'# -*- frozen_string_literal : true -*-'
].freeze
def mock_index(name, params = {}) def mock_index(name, params = {})
double('IndexKeyDefinition', double('IndexKeyDefinition',
name: name, name: name,
...@@ -64,16 +78,71 @@ describe AnnotateModels do # rubocop:disable Metrics/BlockLength ...@@ -64,16 +78,71 @@ describe AnnotateModels do # rubocop:disable Metrics/BlockLength
double('Column', stubs) double('Column', stubs)
end end
it { expect(AnnotateModels.quote(nil)).to eql('NULL') } describe '.quote' do
it { expect(AnnotateModels.quote(true)).to eql('TRUE') } subject do
it { expect(AnnotateModels.quote(false)).to eql('FALSE') } AnnotateModels.quote(value)
it { expect(AnnotateModels.quote(25)).to eql('25') } end
it { expect(AnnotateModels.quote(25.6)).to eql('25.6') }
it { expect(AnnotateModels.quote(1e-20)).to eql('1.0e-20') } context 'when the argument is nil' do
it { expect(AnnotateModels.quote(BigDecimal('1.2'))).to eql('1.2') } let(:value) { nil }
it { expect(AnnotateModels.quote([BigDecimal('1.2')])).to eql(['1.2']) } it 'returns string "NULL"' do
is_expected.to eq('NULL')
end
end
describe '#parse_options' do context 'when the argument is true' do
let(:value) { true }
it 'returns string "TRUE"' do
is_expected.to eq('TRUE')
end
end
context 'when the argument is false' do
let(:value) { false }
it 'returns string "FALSE"' do
is_expected.to eq('FALSE')
end
end
context 'when the argument is an integer' do
let(:value) { 25 }
it 'returns the integer as a string' do
is_expected.to eq('25')
end
end
context 'when the argument is a float number' do
context 'when the argument is like 25.6' do
let(:value) { 25.6 }
it 'returns the float number as a string' do
is_expected.to eq('25.6')
end
end
context 'when the argument is like 1e-20' do
let(:value) { 1e-20 }
it 'returns the float number as a string' do
is_expected.to eq('1.0e-20')
end
end
end
context 'when the argument is a BigDecimal number' do
let(:value) { BigDecimal('1.2') }
it 'returns the float number as a string' do
is_expected.to eq('1.2')
end
end
context 'when the argument is an array' do
let(:value) { [BigDecimal('1.2')] }
it 'returns an array of which elements are converted to string' do
is_expected.to eq(['1.2'])
end
end
end
describe '.parse_options' do
let(:options) do let(:options) do
{ {
root_dir: '/root', root_dir: '/root',
...@@ -81,20 +150,28 @@ describe AnnotateModels do # rubocop:disable Metrics/BlockLength ...@@ -81,20 +150,28 @@ describe AnnotateModels do # rubocop:disable Metrics/BlockLength
} }
end end
it 'sets @root_dir' do before :each do
AnnotateModels.send(:parse_options, options) AnnotateModels.send(:parse_options, options)
expect(AnnotateModels.instance_variable_get(:@root_dir)).to eq('/root')
end end
it 'sets @model_dir separated with a comma' do describe '@root_dir' do
AnnotateModels.send(:parse_options, options) subject do
expected = [ AnnotateModels.instance_variable_get(:@root_dir)
'app/models', end
'app/one',
'app/two', it 'sets @root_dir' do
'app/three' is_expected.to eq('/root')
] end
expect(AnnotateModels.instance_variable_get(:@model_dir)).to eq(expected) end
describe '@model_dir' do
subject do
AnnotateModels.instance_variable_get(:@model_dir)
end
it 'separates option "model_dir" with commas and sets @model_dir as an array of string' do
is_expected.to eq(['app/models', 'app/one', 'app/two', 'app/three'])
end
end end
end end
...@@ -788,14 +865,25 @@ EOS ...@@ -788,14 +865,25 @@ EOS
EOS EOS
end end
describe '#set_defaults' do describe '.set_defaults' do
it 'should default show_complete_foreign_keys to false' do subject do
expect(Annotate::Helpers.true?(ENV['show_complete_foreign_keys'])).to be(false) Annotate::Helpers.true?(ENV['show_complete_foreign_keys'])
end end
it 'should be able to set show_complete_foreign_keys to true' do context 'when default value of "show_complete_foreign_keys" is not set' do
it 'returns false' do
is_expected.to be(false)
end
end
context 'when default value of "show_complete_foreign_keys" is set' do
before do
Annotate.set_defaults('show_complete_foreign_keys' => 'true') Annotate.set_defaults('show_complete_foreign_keys' => 'true')
expect(Annotate::Helpers.true?(ENV['show_complete_foreign_keys'])).to be(true) end
it 'returns true' do
is_expected.to be(true)
end
end end
after :each do after :each do
...@@ -803,14 +891,14 @@ EOS ...@@ -803,14 +891,14 @@ EOS
end end
end end
describe '#files_by_pattern' do describe '.files_by_pattern' do
subject { AnnotateModels.files_by_pattern(root_directory, pattern_type, options) } subject { AnnotateModels.files_by_pattern(root_directory, pattern_type, options) }
context 'when pattern_type=additional_file_patterns' do context 'when pattern_type is "additional_file_patterns"' do
let(:pattern_type) { 'additional_file_patterns' }
let(:root_directory) { nil } let(:root_directory) { nil }
let(:pattern_type) { 'additional_file_patterns' }
context 'with additional_file_patterns' do context 'when additional_file_patterns is specified in the options' do
let(:additional_file_patterns) do let(:additional_file_patterns) do
[ [
'%PLURALIZED_MODEL_NAME%/**/*.rb', '%PLURALIZED_MODEL_NAME%/**/*.rb',
...@@ -820,28 +908,28 @@ EOS ...@@ -820,28 +908,28 @@ EOS
let(:options) { { additional_file_patterns: additional_file_patterns } } let(:options) { { additional_file_patterns: additional_file_patterns } }
it do it 'returns additional_file_patterns in the argument "options"' do
expect(subject).to eq(additional_file_patterns) is_expected.to eq(additional_file_patterns)
end end
end end
context 'without additional_file_patterns' do context 'when additional_file_patterns is not specified is the options' do
let(:options) { {} } let(:options) { {} }
it do it 'returns an empty array' do
expect(subject).to eq([]) is_expected.to eq([])
end end
end end
end end
end end
describe '#get_patterns' do describe '.get_patterns' do
subject { AnnotateModels.get_patterns(options, pattern_type) } subject { AnnotateModels.get_patterns(options, pattern_type) }
context 'when pattern_type=additional_file_patterns' do context 'when pattern_type is "additional_file_patterns"' do
let(:pattern_type) { 'additional_file_patterns' } let(:pattern_type) { 'additional_file_patterns' }
context 'with additional_file_patterns' do context 'when additional_file_patterns is specified in the options' do
let(:additional_file_patterns) do let(:additional_file_patterns) do
[ [
'/%PLURALIZED_MODEL_NAME%/**/*.rb', '/%PLURALIZED_MODEL_NAME%/**/*.rb',
...@@ -851,16 +939,16 @@ EOS ...@@ -851,16 +939,16 @@ EOS
let(:options) { { additional_file_patterns: additional_file_patterns } } let(:options) { { additional_file_patterns: additional_file_patterns } }
it do it 'returns additional_file_patterns in the argument "options"' do
expect(subject).to eq(additional_file_patterns) is_expected.to eq(additional_file_patterns)
end end
end end
context 'without additional_file_patterns' do context 'when additional_file_patterns is not specified is the options' do
let(:options) { {} } let(:options) { {} }
it do it 'returns an empty array' do
expect(subject).to eq([]) is_expected.to eq([])
end end
end end
end end
...@@ -1147,7 +1235,7 @@ EOS ...@@ -1147,7 +1235,7 @@ EOS
end end
end end
describe '#get_model_files' do describe '.get_model_files' do
subject { described_class.get_model_files(options) } subject { described_class.get_model_files(options) }
before do before do
...@@ -1262,432 +1350,653 @@ EOS ...@@ -1262,432 +1350,653 @@ EOS
end end
end end
describe '#get_model_class' do describe '.get_model_class' do # rubocop:disable Metrics/BlockLength
before :all do
require 'tmpdir' require 'tmpdir'
AnnotateModels.model_dir = Dir.mktmpdir('annotate_models')
end
# TODO: use 'files' gem instead # TODO: use 'files' gem instead
def create(file, body = 'hi') def create(filename, file_content)
file_path = File.join(AnnotateModels.model_dir[0], file) File.join(AnnotateModels.model_dir[0], filename).tap do |path|
FileUtils.mkdir_p(File.dirname(file_path)) FileUtils.mkdir_p(File.dirname(path))
File.open(file_path, 'wb') do |f| File.open(path, 'wb') do |f|
f.puts(body) f.puts(file_content)
end
end end
file_path
end end
def check_class_name(file, class_name) before :each do
klass = AnnotateModels.get_model_class(File.join(AnnotateModels.model_dir[0], file)) create(filename, file_content)
end
expect(klass).not_to eq(nil) let :klass do
expect(klass.name).to eq(class_name) AnnotateModels.get_model_class(File.join(AnnotateModels.model_dir[0], filename))
end end
before :each do context 'when class Foo is defined in "foo.rb"' do
AnnotateModels.model_dir = Dir.mktmpdir 'annotate_models' let :filename do
'foo.rb'
end end
it 'should work' do let :file_content do
create 'foo.rb', <<-EOS <<~EOS
class Foo < ActiveRecord::Base class Foo < ActiveRecord::Base
end end
EOS EOS
check_class_name 'foo.rb', 'Foo'
end end
it 'should find models with non standard capitalization' do it 'works' do
create 'foo_with_capitals.rb', <<-EOS expect(klass.name).to eq('Foo')
end
end
context 'when class name is not capitalized normally' do
context 'when class FooWithCAPITALS is defined in "foo_with_capitals.rb"' do
let :filename do
'foo_with_capitals.rb'
end
let :file_content do
<<~EOS
class FooWithCAPITALS < ActiveRecord::Base class FooWithCAPITALS < ActiveRecord::Base
end end
EOS EOS
check_class_name 'foo_with_capitals.rb', 'FooWithCAPITALS'
end end
it 'should find models inside modules' do it 'works' do
create 'bar/foo_inside_bar.rb', <<-EOS expect(klass.name).to eq('FooWithCAPITALS')
end
end
end
context 'when class is defined inside module' do
context 'when class Bar::FooInsideBar is defined in "bar/foo_inside_bar.rb"' do
let :filename do
'bar/foo_inside_bar.rb'
end
let :file_content do
<<~EOS
module Bar module Bar
class FooInsideBar < ActiveRecord::Base class FooInsideBar < ActiveRecord::Base
end end
end end
EOS EOS
check_class_name 'bar/foo_inside_bar.rb', 'Bar::FooInsideBar'
end end
it 'should find AR model when duplicated by a nested model' do it 'works' do
create 'foo.rb', <<-EOS expect(klass.name).to eq('Bar::FooInsideBar')
class Foo < ActiveRecord::Base end
end
end end
EOS
create 'bar/foo.rb', <<-EOS context 'when class is defined inside module and class name is not capitalized normally' do
class Bar::Foo context 'when class Bar::FooInsideCapitalsBAR is defined in "bar/foo_inside_capitals_bar.rb"' do
let :filename do
'bar/foo_inside_capitals_bar.rb'
end
let :file_content do
<<~EOS
module BAR
class FooInsideCapitalsBAR < ActiveRecord::Base
end
end end
EOS EOS
check_class_name 'bar/foo.rb', 'Bar::Foo'
check_class_name 'foo.rb', 'Foo'
end end
it 'should find AR model nested inside a class' do it 'works' do
create 'voucher.rb', <<-EOS expect(klass.name).to eq('BAR::FooInsideCapitalsBAR')
class Voucher < ActiveRecord::Base end
end
end end
EOS
create 'voucher/foo.rb', <<-EOS context 'when unknown macros exist in class' do
class Voucher context 'when class FooWithMacro is defined in "foo_with_macro.rb"' do
class Foo let :filename do
'foo_with_macro.rb'
end end
let :file_content do
<<~EOS
class FooWithMacro < ActiveRecord::Base
acts_as_awesome :yah
end end
EOS EOS
end
it 'works and does not care about known macros' do
expect(klass.name).to eq('FooWithMacro')
end
end
check_class_name 'voucher.rb', 'Voucher' context 'when class name is with ALL CAPS segments' do
check_class_name 'voucher/foo.rb', 'Voucher::Foo' context 'when class is "FooWithCAPITALS" is defined in "foo_with_capitals.rb"' do
let :filename do
'foo_with_capitals.rb'
end end
it 'should not care about unknown macros' do let :file_content do
create 'foo_with_macro.rb', <<-EOS <<~EOS
class FooWithMacro < ActiveRecord::Base class FooWithCAPITALS < ActiveRecord::Base
acts_as_awesome :yah acts_as_awesome :yah
end end
EOS EOS
check_class_name 'foo_with_macro.rb', 'FooWithMacro'
end end
it 'should not care about known macros' do it 'works' do
create('foo_with_known_macro.rb', <<-EOS) expect(klass.name).to eq('FooWithCAPITALS')
end
end
end
end
context 'when known macros exist in class' do
context 'when class FooWithKnownMacro is defined in "foo_with_known_macro.rb"' do
let :filename do
'foo_with_known_macro.rb'
end
let :file_content do
<<~EOS
class FooWithKnownMacro < ActiveRecord::Base class FooWithKnownMacro < ActiveRecord::Base
has_many :yah has_many :yah
end end
EOS EOS
check_class_name 'foo_with_known_macro.rb', 'FooWithKnownMacro'
end end
it 'should work with class names with ALL CAPS segments' do it 'works and does not care about known macros' do
create('foo_with_capitals.rb', <<-EOS) expect(klass.name).to eq('FooWithKnownMacro')
class FooWithCAPITALS < ActiveRecord::Base
acts_as_awesome :yah
end end
EOS end
check_class_name 'foo_with_capitals.rb', 'FooWithCAPITALS' end
context 'when the file includes invlaid multibyte chars (USASCII)' do
context 'when class FooWithUtf8 is defined in "foo_with_utf8.rb"' do
let :filename do
'foo_with_utf8.rb'
end end
it 'should not complain of invalid multibyte char (USASCII)' do let :file_content do
create 'foo_with_utf8.rb', <<-EOS <<~EOS
#encoding: utf-8 # encoding: utf-8
class FooWithUtf8 < ActiveRecord::Base class FooWithUtf8 < ActiveRecord::Base
UTF8STRINGS = %w[résumé façon âge] UTF8STRINGS = %w[résumé façon âge]
end end
EOS EOS
check_class_name 'foo_with_utf8.rb', 'FooWithUtf8'
end end
it 'should find models inside modules with non standard capitalization' do it 'works without complaining of invalid multibyte chars' do
create 'bar/foo_inside_capitals_bar.rb', <<-EOS expect(klass.name).to eq('FooWithUtf8')
module BAR
class FooInsideCapitalsBAR < ActiveRecord::Base
end end
end end
EOS
check_class_name 'bar/foo_inside_capitals_bar.rb', 'BAR::FooInsideCapitalsBAR'
end end
it 'should find non-namespaced models inside subdirectories' do context 'when non-namespaced model is inside subdirectory' do
create 'bar/non_namespaced_foo_inside_bar.rb', <<-EOS context 'when class NonNamespacedFooInsideBar is defined in "bar/non_namespaced_foo_inside_bar.rb"' do
let :filename do
'bar/non_namespaced_foo_inside_bar.rb'
end
let :file_content do
<<~EOS
class NonNamespacedFooInsideBar < ActiveRecord::Base class NonNamespacedFooInsideBar < ActiveRecord::Base
end end
EOS EOS
check_class_name 'bar/non_namespaced_foo_inside_bar.rb', 'NonNamespacedFooInsideBar'
end end
it 'should find non-namespaced models with non standard capitalization inside subdirectories' do it 'works' do
create 'bar/non_namespaced_foo_with_capitals_inside_bar.rb', <<-EOS expect(klass.name).to eq('NonNamespacedFooInsideBar')
end
end
context 'when class name is not capitalized normally' do
context 'when class NonNamespacedFooWithCapitalsInsideBar is defined in "bar/non_namespaced_foo_with_capitals_inside_bar.rb"' do
let :filename do
'bar/non_namespaced_foo_with_capitals_inside_bar.rb'
end
let :file_content do
<<~EOS
class NonNamespacedFooWithCapitalsInsideBar < ActiveRecord::Base class NonNamespacedFooWithCapitalsInsideBar < ActiveRecord::Base
end end
EOS EOS
check_class_name 'bar/non_namespaced_foo_with_capitals_inside_bar.rb', 'NonNamespacedFooWithCapitalsInsideBar'
end end
it 'should allow known macros' do it 'works' do
create('foo_with_known_macro.rb', <<-EOS) expect(klass.name).to eq('NonNamespacedFooWithCapitalsInsideBar')
class FooWithKnownMacro < ActiveRecord::Base end
has_many :yah
end end
EOS end
expect(capturing(:stderr) do end
check_class_name 'foo_with_known_macro.rb', 'FooWithKnownMacro'
end).to eq('') context 'when class file is loaded twice' do
context 'when class LoadedClass is defined in "loaded_class.rb"' do
let :filename do
'loaded_class.rb'
end end
it 'should not require model files twice' do let :file_content do
create 'loaded_class.rb', <<-EOS <<~EOS
class LoadedClass < ActiveRecord::Base class LoadedClass < ActiveRecord::Base
CONSTANT = 1 CONSTANT = 1
end end
EOS EOS
path = File.expand_path('loaded_class', AnnotateModels.model_dir[0]) end
Kernel.load "#{path}.rb"
before :each do
path = File.expand_path(filename, AnnotateModels.model_dir[0])
Kernel.load(path)
expect(Kernel).not_to receive(:require) expect(Kernel).not_to receive(:require)
end
expect(capturing(:stderr) do it 'does not require model file twice' do
check_class_name 'loaded_class.rb', 'LoadedClass' expect(klass.name).to eq('LoadedClass')
end).to be_blank end
end end
it 'should not require model files twice which is inside a subdirectory' do context 'when class is defined in a subdirectory' do
dir = Array.new(8) { (0..9).to_a.sample(random: Random.new) }.join dir = Array.new(8) { (0..9).to_a.sample(random: Random.new) }.join
context "when class SubdirLoadedClass is defined in \"#{dir}/subdir_loaded_class.rb\"" do
before :each do
$LOAD_PATH.unshift(File.join(AnnotateModels.model_dir[0], dir)) $LOAD_PATH.unshift(File.join(AnnotateModels.model_dir[0], dir))
create "#{dir}/subdir_loaded_class.rb", <<-EOS path = File.expand_path(filename, AnnotateModels.model_dir[0])
Kernel.load(path)
expect(Kernel).not_to receive(:require)
end
let :filename do
"#{dir}/subdir_loaded_class.rb"
end
let :file_content do
<<~EOS
class SubdirLoadedClass < ActiveRecord::Base class SubdirLoadedClass < ActiveRecord::Base
CONSTANT = 1 CONSTANT = 1
end end
EOS EOS
path = File.expand_path("#{dir}/subdir_loaded_class", AnnotateModels.model_dir[0]) end
Kernel.load "#{path}.rb"
expect(Kernel).not_to receive(:require)
expect(capturing(:stderr) do it 'does not require model file twice' do
check_class_name "#{dir}/subdir_loaded_class.rb", 'SubdirLoadedClass' expect(klass.name).to eq('SubdirLoadedClass')
end).to be_blank end
end
end end
end end
describe '#remove_annotation_of_file' do context 'when two class exist' do
require 'tmpdir' before :each do
create(filename_2, file_content_2)
end
def create(file, body = 'hi') context 'the base names are duplicated' do
path = File.join(@dir, file) let :filename do
File.open(path, 'w') do |f| 'foo.rb'
f.puts(body)
end end
path let :file_content do
<<-EOS
class Foo < ActiveRecord::Base
end
EOS
end end
def content(path) let :filename_2 do
File.read(path) 'bar/foo.rb'
end end
before :each do let :file_content_2 do
@dir = Dir.mktmpdir 'annotate_models' <<-EOS
class Bar::Foo
end
EOS
end end
it 'should remove before annotate' do let :klass_2 do
path = create 'before.rb', <<-EOS AnnotateModels.get_model_class(File.join(AnnotateModels.model_dir[0], filename_2))
# == Schema Information end
#
# Table name: foo
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
class Foo < ActiveRecord::Base it 'finds valid model' do
end expect(klass.name).to eq('Foo')
EOS expect(klass_2.name).to eq('Bar::Foo')
end
end
AnnotateModels.remove_annotation_of_file(path) context 'one of the classes is nested in another class' do
let :filename do
'voucher.rb'
end
expect(content(path)).to eq <<-EOS let :file_content do
class Foo < ActiveRecord::Base <<-EOS
end class Voucher < ActiveRecord::Base
end
EOS EOS
end end
it 'should remove annotate if CRLF is used for line breaks' do let :filename_2 do
path = create 'before.rb', <<-EOS 'voucher/foo.rb'
# == Schema Information end
#
# Table name: foo\r\n# let :file_content_2 do
# id :integer not null, primary key <<~EOS
# created_at :datetime class Voucher
# updated_at :datetime class Foo
# end
\r\n end
class Foo < ActiveRecord::Base
end
EOS EOS
end
let :klass_2 do
AnnotateModels.get_model_class(File.join(AnnotateModels.model_dir[0], filename_2))
end
it 'finds valid model' do
expect(klass.name).to eq('Voucher')
expect(klass_2.name).to eq('Voucher::Foo')
end
end
end
end
describe '.remove_annotation_of_file' do
subject do
AnnotateModels.remove_annotation_of_file(path) AnnotateModels.remove_annotation_of_file(path)
end
expect(content(path)).to eq <<-EOS let :tmpdir do
class Foo < ActiveRecord::Base require 'tmpdir'
end Dir.mktmpdir('annotate_models')
end
let :path do
File.join(tmpdir, filename).tap do |path|
File.open(path, 'w') do |f|
f.puts(file_content)
end
end
end
let :file_content_after_removal do
subject
File.read(path)
end
let :expected_result do
<<~EOS
class Foo < ActiveRecord::Base
end
EOS EOS
end end
it 'should remove after annotate' do context 'when annotation is before main content' do
path = create 'after.rb', <<-EOS let :filename do
class Foo < ActiveRecord::Base 'before.rb'
end end
# == Schema Information let :file_content do
# <<~EOS
# Table name: foo # == Schema Information
# #
# id :integer not null, primary key # Table name: foo
# created_at :datetime #
# updated_at :datetime # id :integer not null, primary key
# # created_at :datetime
# updated_at :datetime
#
class Foo < ActiveRecord::Base
end
EOS EOS
end
AnnotateModels.remove_annotation_of_file(path) it 'removes annotation' do
expect(file_content_after_removal).to eq expected_result
end
end
expect(content(path)).to eq <<-EOS context 'when annotation is before main content and CRLF is used for line breaks' do
class Foo < ActiveRecord::Base let :filename do
end 'before.rb'
end
let :file_content do
<<~EOS
# == Schema Information
#
# Table name: foo\r\n#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
\r\n
class Foo < ActiveRecord::Base
end
EOS EOS
end end
it 'should remove opening wrapper' do it 'removes annotation' do
path = create 'opening_wrapper.rb', <<-EOS expect(file_content_after_removal).to eq expected_result
# wrapper end
# == Schema Information end
#
# Table name: foo
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
class Foo < ActiveRecord::Base context 'when annotation is before main content and with opening wrapper' do
end let :filename do
'opening_wrapper.rb'
end
let :file_content do
<<~EOS
# wrapper
# == Schema Information
#
# Table name: foo
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
class Foo < ActiveRecord::Base
end
EOS EOS
end
subject do
AnnotateModels.remove_annotation_of_file(path, wrapper_open: 'wrapper') AnnotateModels.remove_annotation_of_file(path, wrapper_open: 'wrapper')
end
expect(content(path)).to eq <<-EOS it 'removes annotation' do
class Foo < ActiveRecord::Base expect(file_content_after_removal).to eq expected_result
end end
EOS
end end
it 'should remove wrapper if CRLF is used for line breaks' do context 'when annotation is before main content and with opening wrapper' do
path = create 'opening_wrapper.rb', <<-EOS let :filename do
# wrapper\r\n# == Schema Information 'opening_wrapper.rb'
# end
# Table name: foo
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
class Foo < ActiveRecord::Base let :file_content do
end <<~EOS
# wrapper\r\n# == Schema Information
#
# Table name: foo
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
class Foo < ActiveRecord::Base
end
EOS EOS
end
subject do
AnnotateModels.remove_annotation_of_file(path, wrapper_open: 'wrapper') AnnotateModels.remove_annotation_of_file(path, wrapper_open: 'wrapper')
end
it 'removes annotation' do
expect(file_content_after_removal).to eq expected_result
end
end
context 'when annotation is after main content' do
let :filename do
'after.rb'
end
let :file_content do
<<~EOS
class Foo < ActiveRecord::Base
end
# == Schema Information
#
# Table name: foo
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
expect(content(path)).to eq <<-EOS
class Foo < ActiveRecord::Base
end
EOS EOS
end end
it 'should remove closing wrapper' do it 'removes annotation' do
path = create 'closing_wrapper.rb', <<-EOS expect(file_content_after_removal).to eq expected_result
class Foo < ActiveRecord::Base end
end end
# == Schema Information context 'when annotation is after main content and with closing wrapper' do
# let :filename do
# Table name: foo 'closing_wrapper.rb'
# end
# id :integer not null, primary key
# created_at :datetime let :file_content do
# updated_at :datetime <<~EOS
# class Foo < ActiveRecord::Base
# wrapper end
# == Schema Information
#
# Table name: foo
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
# wrapper
EOS EOS
end
subject do
AnnotateModels.remove_annotation_of_file(path, wrapper_close: 'wrapper') AnnotateModels.remove_annotation_of_file(path, wrapper_close: 'wrapper')
end
expect(content(path)).to eq <<-EOS it 'removes annotation' do
class Foo < ActiveRecord::Base expect(file_content_after_removal).to eq expected_result
end end
EOS
end end
it 'does not change file with #SkipSchemaAnnotations' do context 'when annotation is before main content and with comment "-*- SkipSchemaAnnotations"' do
content = <<-EOS let :filename do
# -*- SkipSchemaAnnotations 'skip.rb'
# == Schema Information end
#
# Table name: foo
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
class Foo < ActiveRecord::Base let :file_content do
end <<~EOS
# -*- SkipSchemaAnnotations
# == Schema Information
#
# Table name: foo
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
class Foo < ActiveRecord::Base
end
EOS EOS
end
path = create 'skip.rb', content let :expected_result do
file_content
end
AnnotateModels.remove_annotation_of_file(path) it 'does not remove annotation' do
expect(content(path)).to eq(content) expect(file_content_after_removal).to eq expected_result
end
end end
end end
describe '#resolve_filename' do describe '.resolve_filename' do
it 'should return the test path for a model' do subject do
filename_template = 'test/unit/%MODEL_NAME%_test.rb' AnnotateModels.resolve_filename(filename_template, model_name, table_name)
model_name = 'example_model' end
table_name = 'example_models'
context 'When model_name is "example_model" and table_name is "example_models"' do
let(:model_name) { 'example_model' }
let(:table_name) { 'example_models' }
context "when filename_template is 'test/unit/%MODEL_NAME%_test.rb'" do
let(:filename_template) { 'test/unit/%MODEL_NAME%_test.rb' }
filename = AnnotateModels.resolve_filename(filename_template, model_name, table_name) it 'returns the test path for a model' do
expect(filename). to eq 'test/unit/example_model_test.rb' is_expected.to eq 'test/unit/example_model_test.rb'
end
end end
it 'should return the additional glob' do context "when filename_template is '/foo/bar/%MODEL_NAME%/testing.rb'" do
filename_template = '/foo/bar/%MODEL_NAME%/testing.rb' let(:filename_template) { '/foo/bar/%MODEL_NAME%/testing.rb' }
model_name = 'example_model'
table_name = 'example_models'
filename = AnnotateModels.resolve_filename(filename_template, model_name, table_name) it 'returns the additional glob' do
expect(filename). to eq '/foo/bar/example_model/testing.rb' is_expected.to eq '/foo/bar/example_model/testing.rb'
end
end end
it 'should return the additional glob' do context "when filename_template is '/foo/bar/%PLURALIZED_MODEL_NAME%/testing.rb'" do
filename_template = '/foo/bar/%PLURALIZED_MODEL_NAME%/testing.rb' let(:filename_template) { '/foo/bar/%PLURALIZED_MODEL_NAME%/testing.rb' }
model_name = 'example_model'
table_name = 'example_models'
filename = AnnotateModels.resolve_filename(filename_template, model_name, table_name) it 'returns the additional glob' do
expect(filename). to eq '/foo/bar/example_models/testing.rb' is_expected.to eq '/foo/bar/example_models/testing.rb'
end
end end
it 'should return the fixture path for a model' do context "when filename_template is 'test/fixtures/%TABLE_NAME%.yml'" do
filename_template = 'test/fixtures/%TABLE_NAME%.yml' let(:filename_template) { 'test/fixtures/%TABLE_NAME%.yml' }
model_name = 'example_model'
table_name = 'example_models'
filename = AnnotateModels.resolve_filename(filename_template, model_name, table_name) it 'returns the fixture path for a model' do
expect(filename). to eq 'test/fixtures/example_models.yml' is_expected.to eq 'test/fixtures/example_models.yml'
end end
end
end
context 'When model_name is "parent/child" and table_name is "parent_children"' do
let(:model_name) { 'parent/child' }
let(:table_name) { 'parent_children' }
it 'should return the fixture path for a nested model' do context "when filename_template is 'test/fixtures/%PLURALIZED_MODEL_NAME%.yml'" do
filename_template = 'test/fixtures/%PLURALIZED_MODEL_NAME%.yml' let(:filename_template) { 'test/fixtures/%PLURALIZED_MODEL_NAME%.yml' }
model_name = 'parent/child'
table_name = 'parent_children'
filename = AnnotateModels.resolve_filename(filename_template, model_name, table_name) it 'returns the fixture path for a nested model' do
expect(filename). to eq 'test/fixtures/parent/children.yml' is_expected.to eq 'test/fixtures/parent/children.yml'
end
end
end end
end end
describe 'annotating a file' do describe 'annotating a file' do
before do before do
@model_dir = Dir.mktmpdir('annotate_models') @model_dir = Dir.mktmpdir('annotate_models')
(@model_file_name, @file_content) = write_model 'user.rb', <<-EOS (@model_file_name, @file_content) = write_model 'user.rb', <<~EOS
class User < ActiveRecord::Base class User < ActiveRecord::Base
end end
EOS EOS
@klass = mock_class(:users, @klass = mock_class(:users,
...@@ -1720,22 +2029,6 @@ end ...@@ -1720,22 +2029,6 @@ end
Annotate::Constants::PATH_OPTIONS.each { |key| ENV[key.to_s] = '' } Annotate::Constants::PATH_OPTIONS.each { |key| ENV[key.to_s] = '' }
end end
def magic_comments_list_each
[
'# encoding: UTF-8',
'# coding: UTF-8',
'# -*- coding: UTF-8 -*-',
'#encoding: utf-8',
'# encoding: utf-8',
'# -*- encoding : utf-8 -*-',
"# encoding: utf-8\n# frozen_string_literal: true",
"# frozen_string_literal: true\n# encoding: utf-8",
'# frozen_string_literal: true',
'#frozen_string_literal: false',
'# -*- frozen_string_literal : true -*-'
].each { |magic_comment| yield magic_comment }
end
['before', :before, 'top', :top].each do |position| ['before', :before, 'top', :top].each do |position|
it "should put annotation before class if :position == #{position}" do it "should put annotation before class if :position == #{position}" do
annotate_one_file position: position annotate_one_file position: position
...@@ -1854,9 +2147,9 @@ end ...@@ -1854,9 +2147,9 @@ end
end end
it 'works with namespaced models (i.e. models inside modules/subdirectories)' do it 'works with namespaced models (i.e. models inside modules/subdirectories)' do
(model_file_name, file_content) = write_model 'foo/user.rb', <<-EOS (model_file_name, file_content) = write_model 'foo/user.rb', <<~EOS
class Foo::User < ActiveRecord::Base class Foo::User < ActiveRecord::Base
end end
EOS EOS
klass = mock_class(:'foo_users', klass = mock_class(:'foo_users',
...@@ -1871,11 +2164,11 @@ end ...@@ -1871,11 +2164,11 @@ end
end end
it 'should not touch magic comments' do it 'should not touch magic comments' do
magic_comments_list_each do |magic_comment| MAGIC_COMMENTS.each do |magic_comment|
write_model 'user.rb', <<-EOS write_model 'user.rb', <<~EOS
#{magic_comment} #{magic_comment}
class User < ActiveRecord::Base class User < ActiveRecord::Base
end end
EOS EOS
annotate_one_file position: :before annotate_one_file position: :before
...@@ -1891,7 +2184,7 @@ end ...@@ -1891,7 +2184,7 @@ end
it 'adds an empty line between magic comments and annotation (position :before)' do it 'adds an empty line between magic comments and annotation (position :before)' do
content = "class User < ActiveRecord::Base\nend\n" content = "class User < ActiveRecord::Base\nend\n"
magic_comments_list_each do |magic_comment| MAGIC_COMMENTS.each do |magic_comment|
model_file_name, = write_model 'user.rb', "#{magic_comment}\n#{content}" model_file_name, = write_model 'user.rb', "#{magic_comment}\n#{content}"
annotate_one_file position: :before annotate_one_file position: :before
...@@ -1903,7 +2196,7 @@ end ...@@ -1903,7 +2196,7 @@ end
it 'only keeps a single empty line around the annotation (position :before)' do it 'only keeps a single empty line around the annotation (position :before)' do
content = "class User < ActiveRecord::Base\nend\n" content = "class User < ActiveRecord::Base\nend\n"
magic_comments_list_each do |magic_comment| MAGIC_COMMENTS.each do |magic_comment|
schema_info = AnnotateModels.get_schema_info(@klass, '== Schema Info') schema_info = AnnotateModels.get_schema_info(@klass, '== Schema Info')
model_file_name, = write_model 'user.rb', "#{magic_comment}\n\n\n\n#{content}" model_file_name, = write_model 'user.rb', "#{magic_comment}\n\n\n\n#{content}"
...@@ -1915,7 +2208,7 @@ end ...@@ -1915,7 +2208,7 @@ end
it 'does not change whitespace between magic comments and model file content (position :after)' do it 'does not change whitespace between magic comments and model file content (position :after)' do
content = "class User < ActiveRecord::Base\nend\n" content = "class User < ActiveRecord::Base\nend\n"
magic_comments_list_each do |magic_comment| MAGIC_COMMENTS.each do |magic_comment|
model_file_name, = write_model 'user.rb', "#{magic_comment}\n#{content}" model_file_name, = write_model 'user.rb', "#{magic_comment}\n#{content}"
annotate_one_file position: :after annotate_one_file position: :after
...@@ -1929,7 +2222,7 @@ end ...@@ -1929,7 +2222,7 @@ end
before do before do
allow(AnnotateModels).to receive(:get_loaded_model_by_path).with('user').and_return(nil) allow(AnnotateModels).to receive(:get_loaded_model_by_path).with('user').and_return(nil)
write_model('user.rb', <<-EOS) write_model('user.rb', <<~EOS)
class User < ActiveRecord::Base class User < ActiveRecord::Base
raise "oops" raise "oops"
end end
...@@ -1959,7 +2252,7 @@ end ...@@ -1959,7 +2252,7 @@ end
before do before do
allow(AnnotateModels).to receive(:get_loaded_model_by_path).with('user').and_return(nil) allow(AnnotateModels).to receive(:get_loaded_model_by_path).with('user').and_return(nil)
write_model('user.rb', <<-EOS) write_model('user.rb', <<~EOS)
class User < ActiveRecord::Base class User < ActiveRecord::Base
raise "oops" raise "oops"
end end
...@@ -2012,12 +2305,14 @@ end ...@@ -2012,12 +2305,14 @@ end
allow(Foo).to receive(:table_exists?) { false } allow(Foo).to receive(:table_exists?) { false }
end end
subject do
AnnotateModels.annotate_model_file([], 'foo.rb', nil, {})
end
after { Object.send :remove_const, 'Foo' } after { Object.send :remove_const, 'Foo' }
it 'skips attempt to annotate if no table exists for model' do it 'skips attempt to annotate if no table exists for model' do
annotate_model_file = AnnotateModels.annotate_model_file([], 'foo.rb', nil, {}) is_expected.to eq nil
expect(annotate_model_file).to eq nil
end end
end end
end end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment