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
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
describe '#parse_options' do 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
Annotate.set_defaults('show_complete_foreign_keys' => 'true') it 'returns false' do
expect(Annotate::Helpers.true?(ENV['show_complete_foreign_keys'])).to be(true) 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')
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
require 'tmpdir' before :all do
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
let :file_content do
<<~EOS
class Foo < ActiveRecord::Base
end
EOS
end
it 'works' do
expect(klass.name).to eq('Foo')
end
end end
it 'should work' do context 'when class name is not capitalized normally' do
create 'foo.rb', <<-EOS context 'when class FooWithCAPITALS is defined in "foo_with_capitals.rb"' do
class Foo < ActiveRecord::Base let :filename do
'foo_with_capitals.rb'
end
let :file_content do
<<~EOS
class FooWithCAPITALS < ActiveRecord::Base
end
EOS
end end
EOS
check_class_name 'foo.rb', 'Foo'
end
it 'should find models with non standard capitalization' do it 'works' do
create 'foo_with_capitals.rb', <<-EOS expect(klass.name).to eq('FooWithCAPITALS')
class FooWithCAPITALS < ActiveRecord::Base
end end
EOS end
check_class_name 'foo_with_capitals.rb', 'FooWithCAPITALS'
end end
it 'should find models inside modules' do context 'when class is defined inside module' do
create 'bar/foo_inside_bar.rb', <<-EOS context 'when class Bar::FooInsideBar is defined in "bar/foo_inside_bar.rb"' do
module Bar let :filename do
class FooInsideBar < ActiveRecord::Base 'bar/foo_inside_bar.rb'
end
end end
EOS
check_class_name 'bar/foo_inside_bar.rb', 'Bar::FooInsideBar' let :file_content do
<<~EOS
module Bar
class FooInsideBar < ActiveRecord::Base
end
end
EOS
end
it 'works' do
expect(klass.name).to eq('Bar::FooInsideBar')
end
end
end end
it 'should find AR model when duplicated by a nested model' do context 'when class is defined inside module and class name is not capitalized normally' do
create 'foo.rb', <<-EOS context 'when class Bar::FooInsideCapitalsBAR is defined in "bar/foo_inside_capitals_bar.rb"' do
class Foo < ActiveRecord::Base let :filename do
'bar/foo_inside_capitals_bar.rb'
end end
EOS
create 'bar/foo.rb', <<-EOS let :file_content do
class Bar::Foo <<~EOS
module BAR
class FooInsideCapitalsBAR < ActiveRecord::Base
end
end
EOS
end end
EOS
check_class_name 'bar/foo.rb', 'Bar::Foo' it 'works' do
check_class_name 'foo.rb', 'Foo' expect(klass.name).to eq('BAR::FooInsideCapitalsBAR')
end
end
end end
it 'should find AR model nested inside a class' do context 'when unknown macros exist in class' do
create 'voucher.rb', <<-EOS context 'when class FooWithMacro is defined in "foo_with_macro.rb"' do
class Voucher < ActiveRecord::Base let :filename do
'foo_with_macro.rb'
end end
EOS
create 'voucher/foo.rb', <<-EOS let :file_content do
class Voucher <<~EOS
class Foo class FooWithMacro < ActiveRecord::Base
end acts_as_awesome :yah
end
EOS
end end
EOS
check_class_name 'voucher.rb', 'Voucher' it 'works and does not care about known macros' do
check_class_name 'voucher/foo.rb', 'Voucher::Foo' expect(klass.name).to eq('FooWithMacro')
end
end
context 'when class name is with ALL CAPS segments' do
context 'when class is "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
acts_as_awesome :yah
end
EOS
end
it 'works' do
expect(klass.name).to eq('FooWithCAPITALS')
end
end
end
end end
it 'should not care about unknown macros' do context 'when known macros exist in class' do
create 'foo_with_macro.rb', <<-EOS context 'when class FooWithKnownMacro is defined in "foo_with_known_macro.rb"' do
class FooWithMacro < ActiveRecord::Base let :filename do
acts_as_awesome :yah 'foo_with_known_macro.rb'
end end
EOS
check_class_name 'foo_with_macro.rb', 'FooWithMacro' let :file_content do
<<~EOS
class FooWithKnownMacro < ActiveRecord::Base
has_many :yah
end
EOS
end
it 'works and does not care about known macros' do
expect(klass.name).to eq('FooWithKnownMacro')
end
end
end end
it 'should not care about known macros' do context 'when the file includes invlaid multibyte chars (USASCII)' do
create('foo_with_known_macro.rb', <<-EOS) context 'when class FooWithUtf8 is defined in "foo_with_utf8.rb"' do
class FooWithKnownMacro < ActiveRecord::Base let :filename do
has_many :yah 'foo_with_utf8.rb'
end end
EOS
check_class_name 'foo_with_known_macro.rb', 'FooWithKnownMacro' let :file_content do
<<~EOS
# encoding: utf-8
class FooWithUtf8 < ActiveRecord::Base
UTF8STRINGS = %w[résumé façon âge]
end
EOS
end
it 'works without complaining of invalid multibyte chars' do
expect(klass.name).to eq('FooWithUtf8')
end
end
end end
it 'should work with class names with ALL CAPS segments' do context 'when non-namespaced model is inside subdirectory' do
create('foo_with_capitals.rb', <<-EOS) context 'when class NonNamespacedFooInsideBar is defined in "bar/non_namespaced_foo_inside_bar.rb"' do
class FooWithCAPITALS < ActiveRecord::Base let :filename do
acts_as_awesome :yah 'bar/non_namespaced_foo_inside_bar.rb'
end
let :file_content do
<<~EOS
class NonNamespacedFooInsideBar < ActiveRecord::Base
end
EOS
end
it 'works' do
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
end
EOS
end end
EOS
check_class_name 'foo_with_capitals.rb', 'FooWithCAPITALS'
end
it 'should not complain of invalid multibyte char (USASCII)' do it 'works' do
create 'foo_with_utf8.rb', <<-EOS expect(klass.name).to eq('NonNamespacedFooWithCapitalsInsideBar')
#encoding: utf-8 end
class FooWithUtf8 < ActiveRecord::Base
UTF8STRINGS = %w[résumé façon âge]
end end
EOS end
check_class_name 'foo_with_utf8.rb', 'FooWithUtf8'
end end
it 'should find models inside modules with non standard capitalization' do context 'when class file is loaded twice' do
create 'bar/foo_inside_capitals_bar.rb', <<-EOS context 'when class LoadedClass is defined in "loaded_class.rb"' do
module BAR let :filename do
class FooInsideCapitalsBAR < ActiveRecord::Base 'loaded_class.rb'
end
let :file_content do
<<~EOS
class LoadedClass < ActiveRecord::Base
CONSTANT = 1
end
EOS
end
before :each do
path = File.expand_path(filename, AnnotateModels.model_dir[0])
Kernel.load(path)
expect(Kernel).not_to receive(:require)
end
it 'does not require model file twice' do
expect(klass.name).to eq('LoadedClass')
end
end
context 'when class is defined in a subdirectory' do
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))
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
CONSTANT = 1
end
EOS
end
it 'does not require model file twice' do
expect(klass.name).to eq('SubdirLoadedClass')
end end
end end
EOS end
check_class_name 'bar/foo_inside_capitals_bar.rb', 'BAR::FooInsideCapitalsBAR'
end end
it 'should find non-namespaced models inside subdirectories' do context 'when two class exist' do
create 'bar/non_namespaced_foo_inside_bar.rb', <<-EOS before :each do
class NonNamespacedFooInsideBar < ActiveRecord::Base create(filename_2, file_content_2)
end
context 'the base names are duplicated' do
let :filename do
'foo.rb'
end
let :file_content do
<<-EOS
class Foo < ActiveRecord::Base
end
EOS
end end
EOS
check_class_name 'bar/non_namespaced_foo_inside_bar.rb', 'NonNamespacedFooInsideBar'
end
it 'should find non-namespaced models with non standard capitalization inside subdirectories' do let :filename_2 do
create 'bar/non_namespaced_foo_with_capitals_inside_bar.rb', <<-EOS 'bar/foo.rb'
class NonNamespacedFooWithCapitalsInsideBar < ActiveRecord::Base
end end
EOS
check_class_name 'bar/non_namespaced_foo_with_capitals_inside_bar.rb', 'NonNamespacedFooWithCapitalsInsideBar'
end
it 'should allow known macros' do let :file_content_2 do
create('foo_with_known_macro.rb', <<-EOS) <<-EOS
class FooWithKnownMacro < ActiveRecord::Base class Bar::Foo
has_many :yah end
EOS
end end
EOS
expect(capturing(:stderr) do
check_class_name 'foo_with_known_macro.rb', 'FooWithKnownMacro'
end).to eq('')
end
it 'should not require model files twice' do let :klass_2 do
create 'loaded_class.rb', <<-EOS AnnotateModels.get_model_class(File.join(AnnotateModels.model_dir[0], filename_2))
class LoadedClass < ActiveRecord::Base
CONSTANT = 1
end end
EOS
path = File.expand_path('loaded_class', AnnotateModels.model_dir[0])
Kernel.load "#{path}.rb"
expect(Kernel).not_to receive(:require)
expect(capturing(:stderr) do it 'finds valid model' do
check_class_name 'loaded_class.rb', 'LoadedClass' expect(klass.name).to eq('Foo')
end).to be_blank expect(klass_2.name).to eq('Bar::Foo')
end end
end
it 'should not require model files twice which is inside a subdirectory' do context 'one of the classes is nested in another class' do
dir = Array.new(8) { (0..9).to_a.sample(random: Random.new) }.join let :filename do
$LOAD_PATH.unshift(File.join(AnnotateModels.model_dir[0], dir)) 'voucher.rb'
end
create "#{dir}/subdir_loaded_class.rb", <<-EOS let :file_content do
class SubdirLoadedClass < ActiveRecord::Base <<-EOS
CONSTANT = 1 class Voucher < ActiveRecord::Base
end
EOS
end end
EOS
path = File.expand_path("#{dir}/subdir_loaded_class", AnnotateModels.model_dir[0])
Kernel.load "#{path}.rb"
expect(Kernel).not_to receive(:require)
expect(capturing(:stderr) do let :filename_2 do
check_class_name "#{dir}/subdir_loaded_class.rb", 'SubdirLoadedClass' 'voucher/foo.rb'
end).to be_blank end
end
end
describe '#remove_annotation_of_file' do let :file_content_2 do
require 'tmpdir' <<~EOS
class Voucher
class Foo
end
end
EOS
end
def create(file, body = 'hi') let :klass_2 do
path = File.join(@dir, file) AnnotateModels.get_model_class(File.join(AnnotateModels.model_dir[0], filename_2))
File.open(path, 'w') do |f| end
f.puts(body)
it 'finds valid model' do
expect(klass.name).to eq('Voucher')
expect(klass_2.name).to eq('Voucher::Foo')
end
end end
end
end
path describe '.remove_annotation_of_file' do
subject do
AnnotateModels.remove_annotation_of_file(path)
end end
def content(path) let :tmpdir do
File.read(path) require 'tmpdir'
Dir.mktmpdir('annotate_models')
end end
before :each do let :path do
@dir = Dir.mktmpdir 'annotate_models' File.join(tmpdir, filename).tap do |path|
File.open(path, 'w') do |f|
f.puts(file_content)
end
end
end end
it 'should remove before annotate' do let :file_content_after_removal do
path = create 'before.rb', <<-EOS subject
# == Schema Information File.read(path)
# end
# Table name: foo
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
class Foo < ActiveRecord::Base let :expected_result do
end <<~EOS
class Foo < ActiveRecord::Base
end
EOS EOS
end
AnnotateModels.remove_annotation_of_file(path) context 'when annotation is before main content' do
let :filename do
'before.rb'
end
expect(content(path)).to eq <<-EOS let :file_content do
class Foo < ActiveRecord::Base <<~EOS
end # == Schema Information
EOS #
# Table name: foo
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
class Foo < ActiveRecord::Base
end
EOS
end
it 'removes annotation' do
expect(file_content_after_removal).to eq expected_result
end
end end
it 'should remove annotate if CRLF is used for line breaks' do context 'when annotation is before main content and CRLF is used for line breaks' do
path = create 'before.rb', <<-EOS let :filename do
# == Schema Information 'before.rb'
# end
# 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
AnnotateModels.remove_annotation_of_file(path) 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
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 after annotate' do context 'when annotation is before main content and with opening wrapper' do
path = create 'after.rb', <<-EOS let :filename do
class Foo < ActiveRecord::Base 'opening_wrapper.rb'
end end
# == Schema Information
#
# Table name: foo
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
EOS 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
end
AnnotateModels.remove_annotation_of_file(path) subject do
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 opening wrapper' do context 'when annotation is before main content and with opening wrapper' do
path = create 'opening_wrapper.rb', <<-EOS let :filename do
# wrapper 'opening_wrapper.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
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
end
AnnotateModels.remove_annotation_of_file(path, wrapper_open: 'wrapper') subject do
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 after main content' do
path = create 'opening_wrapper.rb', <<-EOS let :filename do
# wrapper\r\n# == Schema Information 'after.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
EOS class Foo < ActiveRecord::Base
end
AnnotateModels.remove_annotation_of_file(path, wrapper_open: 'wrapper') # == Schema Information
#
# Table name: foo
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
expect(content(path)).to eq <<-EOS EOS
class Foo < ActiveRecord::Base end
end
EOS it 'removes annotation' do
expect(file_content_after_removal).to eq expected_result
end
end end
it 'should remove closing wrapper' do context 'when annotation is after main content and with closing wrapper' do
path = create 'closing_wrapper.rb', <<-EOS let :filename do
class Foo < ActiveRecord::Base 'closing_wrapper.rb'
end end
# == Schema Information let :file_content do
# <<~EOS
# Table name: foo class Foo < ActiveRecord::Base
# end
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
# wrapper
EOS # == Schema Information
#
# Table name: foo
#
# id :integer not null, primary key
# created_at :datetime
# updated_at :datetime
#
# wrapper
AnnotateModels.remove_annotation_of_file(path, wrapper_close: 'wrapper') EOS
end
expect(content(path)).to eq <<-EOS subject do
class Foo < ActiveRecord::Base AnnotateModels.remove_annotation_of_file(path, wrapper_close: 'wrapper')
end end
EOS
it 'removes annotation' do
expect(file_content_after_removal).to eq expected_result
end
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
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
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'
table_name = 'example_models'
filename = AnnotateModels.resolve_filename(filename_template, model_name, table_name)
expect(filename). to eq 'test/unit/example_model_test.rb'
end end
it 'should return the additional glob' do context 'When model_name is "example_model" and table_name is "example_models"' do
filename_template = '/foo/bar/%MODEL_NAME%/testing.rb' let(:model_name) { 'example_model' }
model_name = 'example_model' let(:table_name) { 'example_models' }
table_name = 'example_models'
filename = AnnotateModels.resolve_filename(filename_template, model_name, table_name) context "when filename_template is 'test/unit/%MODEL_NAME%_test.rb'" do
expect(filename). to eq '/foo/bar/example_model/testing.rb' let(:filename_template) { 'test/unit/%MODEL_NAME%_test.rb' }
end
it 'should return the additional glob' do it 'returns the test path for a model' do
filename_template = '/foo/bar/%PLURALIZED_MODEL_NAME%/testing.rb' is_expected.to eq 'test/unit/example_model_test.rb'
model_name = 'example_model' end
table_name = 'example_models' end
filename = AnnotateModels.resolve_filename(filename_template, model_name, table_name) context "when filename_template is '/foo/bar/%MODEL_NAME%/testing.rb'" do
expect(filename). to eq '/foo/bar/example_models/testing.rb' let(:filename_template) { '/foo/bar/%MODEL_NAME%/testing.rb' }
end
it 'should return the fixture path for a model' do it 'returns the additional glob' do
filename_template = 'test/fixtures/%TABLE_NAME%.yml' is_expected.to eq '/foo/bar/example_model/testing.rb'
model_name = 'example_model' end
table_name = 'example_models' end
context "when filename_template is '/foo/bar/%PLURALIZED_MODEL_NAME%/testing.rb'" do
let(:filename_template) { '/foo/bar/%PLURALIZED_MODEL_NAME%/testing.rb' }
filename = AnnotateModels.resolve_filename(filename_template, model_name, table_name) it 'returns the additional glob' do
expect(filename). to eq 'test/fixtures/example_models.yml' is_expected.to eq '/foo/bar/example_models/testing.rb'
end
end
context "when filename_template is 'test/fixtures/%TABLE_NAME%.yml'" do
let(:filename_template) { 'test/fixtures/%TABLE_NAME%.yml' }
it 'returns the fixture path for a model' do
is_expected.to eq 'test/fixtures/example_models.yml'
end
end
end end
it 'should return the fixture path for a nested model' do context 'When model_name is "parent/child" and table_name is "parent_children"' do
filename_template = 'test/fixtures/%PLURALIZED_MODEL_NAME%.yml' let(:model_name) { 'parent/child' }
model_name = 'parent/child' let(:table_name) { 'parent_children' }
table_name = 'parent_children'
context "when filename_template is 'test/fixtures/%PLURALIZED_MODEL_NAME%.yml'" do
let(:filename_template) { 'test/fixtures/%PLURALIZED_MODEL_NAME%.yml' }
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