diff --git a/config/default.yml b/config/default.yml index cef079e57..6ed7db957 100644 --- a/config/default.yml +++ b/config/default.yml @@ -851,6 +851,8 @@ RSpec/SpecFilePathFormat: RuboCop: rubocop RSpec: rspec IgnoreMethods: false + IgnoreMetadata: + type: routing VersionAdded: "<>" Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SpecFilePathFormat diff --git a/docs/modules/ROOT/pages/cops_rspec.adoc b/docs/modules/ROOT/pages/cops_rspec.adoc index d342d7cbb..a68115b12 100644 --- a/docs/modules/ROOT/pages/cops_rspec.adoc +++ b/docs/modules/ROOT/pages/cops_rspec.adoc @@ -5078,6 +5078,14 @@ my_class_spec.rb # describe MyClass, '#method' my_class_spec.rb # describe MyClass, '#method' ---- +==== `IgnoreMetadata: {type=>routing}` (default) + +[source,ruby] +---- +# good +whatever_spec.rb # describe MyClass, type: :routing do; end +---- + === Configurable attributes |=== @@ -5098,6 +5106,10 @@ my_class_spec.rb # describe MyClass, '#method' | IgnoreMethods | `false` | Boolean + +| IgnoreMetadata +| `{"type"=>"routing"}` +| |=== === References diff --git a/lib/rubocop-rspec.rb b/lib/rubocop-rspec.rb index 943600208..21a2d1f91 100644 --- a/lib/rubocop-rspec.rb +++ b/lib/rubocop-rspec.rb @@ -17,6 +17,7 @@ # Dependent on `RuboCop::RSpec::Language::NodePattern`. require_relative 'rubocop/rspec/language' +require_relative 'rubocop/cop/rspec/mixin/file_help' require_relative 'rubocop/cop/rspec/mixin/final_end_location' require_relative 'rubocop/cop/rspec/mixin/inside_example_group' require_relative 'rubocop/cop/rspec/mixin/location_help' diff --git a/lib/rubocop/cop/rspec/mixin/file_help.rb b/lib/rubocop/cop/rspec/mixin/file_help.rb new file mode 100644 index 000000000..22bfc3f22 --- /dev/null +++ b/lib/rubocop/cop/rspec/mixin/file_help.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module RSpec + # Help methods for file. + module FileHelp + def expanded_file_path + File.expand_path(processed_source.file_path) + end + end + end + end +end diff --git a/lib/rubocop/cop/rspec/spec_file_path_format.rb b/lib/rubocop/cop/rspec/spec_file_path_format.rb index 34dc56c87..25f2d3e4d 100644 --- a/lib/rubocop/cop/rspec/spec_file_path_format.rb +++ b/lib/rubocop/cop/rspec/spec_file_path_format.rb @@ -28,9 +28,14 @@ module RSpec # # good # my_class_spec.rb # describe MyClass, '#method' # + # @example `IgnoreMetadata: {type=>routing}` (default) + # # good + # whatever_spec.rb # describe MyClass, type: :routing do; end + # class SpecFilePathFormat < Base include TopLevelGroup include Namespace + include FileHelp MSG = 'Spec path should end with `%s`.' @@ -39,13 +44,15 @@ class SpecFilePathFormat < Base (block (send #rspec? #ExampleGroups.all $_ $...) ...) PATTERN - # @!method routing_metadata?(node) - def_node_search :routing_metadata?, '(pair (sym :type) (sym :routing))' + # @!method metadata_key_value(node) + def_node_search :metadata_key_value, '(pair (sym $_key) (sym $_value))' def on_top_level_example_group(node) return unless top_level_groups.one? example_group_arguments(node) do |class_name, arguments| + next if !class_name.const_type? || ignore_metadata?(arguments) + ensure_correct_file_path(class_name, arguments) end end @@ -53,8 +60,6 @@ def on_top_level_example_group(node) private def ensure_correct_file_path(class_name, arguments) - return if !class_name.const_type? || routing_spec?(arguments) - pattern = correct_path_pattern(class_name, arguments) return if filename_ends_with?(pattern) @@ -65,8 +70,12 @@ def ensure_correct_file_path(class_name, arguments) add_global_offense(format(MSG, suffix: suffix)) end - def routing_spec?(arguments) - arguments.any?(&method(:routing_metadata?)) + def ignore_metadata?(arguments) + arguments.any? do |argument| + metadata_key_value(argument).any? do |key, value| + ignore_metadata.values_at(key.to_s).include?(value.to_s) + end + end end def correct_path_pattern(class_name, arguments) @@ -111,12 +120,12 @@ def ignore_methods? cop_config['IgnoreMethods'] end - def filename_ends_with?(pattern) - expanded_file_path.match?("#{pattern}$") + def ignore_metadata + cop_config.fetch('IgnoreMetadata', {}) end - def expanded_file_path - File.expand_path(processed_source.file_path) + def filename_ends_with?(pattern) + expanded_file_path.match?("#{pattern}$") end end end diff --git a/lib/rubocop/cop/rspec/spec_file_path_suffix.rb b/lib/rubocop/cop/rspec/spec_file_path_suffix.rb index 670d78e30..d013c47a8 100644 --- a/lib/rubocop/cop/rspec/spec_file_path_suffix.rb +++ b/lib/rubocop/cop/rspec/spec_file_path_suffix.rb @@ -19,6 +19,7 @@ module RSpec # class SpecFilePathSuffix < Base include TopLevelGroup + include FileHelp MSG = 'Spec path should end with `_spec.rb`.' @@ -33,10 +34,6 @@ def on_top_level_example_group(node) def correct_path? expanded_file_path.end_with?('_spec.rb') end - - def expanded_file_path - File.expand_path(processed_source.file_path) - end end end end diff --git a/spec/rubocop/cop/rspec/spec_file_path_format_spec.rb b/spec/rubocop/cop/rspec/spec_file_path_format_spec.rb index 06bee1971..8b1dcc3e0 100644 --- a/spec/rubocop/cop/rspec/spec_file_path_format_spec.rb +++ b/spec/rubocop/cop/rspec/spec_file_path_format_spec.rb @@ -1,19 +1,6 @@ # frozen_string_literal: true RSpec.describe RuboCop::Cop::RSpec::SpecFilePathFormat, :config do - def expect_global_offense(source, file = nil, message = '') - processed_source = parse_source(source, file) - offenses = _investigate(cop, processed_source) - expect(offenses.size).to eq(1) - expect(offenses.first.message).to eq(message) - end - - def expect_no_global_offenses(source, file = nil) - processed_source = parse_source(source, file) - offenses = _investigate(cop, processed_source) - expect(offenses.size).to eq(0) - end - let(:message) { "Spec path should end with `#{suffix}`." } let(:suffix) { 'my_class*foo*_spec.rb' } @@ -320,12 +307,6 @@ class Foo describe FooFoo::Some::Class, '#bar' do; end RUBY end - - it 'does not register an offense for routing specs' do - expect_no_global_offenses(<<-RUBY, 'foofoo/some/class/bar_spec.rb') - describe MyController, "#foo", type: :routing do; end - RUBY - end end context 'when configured with `IgnoreMethods: false`' do @@ -352,4 +333,14 @@ class Foo end end end + + context 'when configured with `IgnoreMetadata: { "foo" => "bar" }`' do + let(:cop_config) { { 'IgnoreMetadata' => { 'foo' => 'bar' } } } + + it 'does not register an offense' do + expect_no_global_offenses(<<-RUBY, 'wrong_class_spec.rb') + describe MyClass, foo: :bar do; end + RUBY + end + end end diff --git a/spec/rubocop/cop/rspec/spec_file_path_suffix_spec.rb b/spec/rubocop/cop/rspec/spec_file_path_suffix_spec.rb index b34b53b5c..0a4d801b9 100644 --- a/spec/rubocop/cop/rspec/spec_file_path_suffix_spec.rb +++ b/spec/rubocop/cop/rspec/spec_file_path_suffix_spec.rb @@ -1,19 +1,6 @@ # frozen_string_literal: true RSpec.describe RuboCop::Cop::RSpec::SpecFilePathSuffix do - def expect_global_offense(source, file = nil, message = '') - processed_source = parse_source(source, file) - offenses = _investigate(cop, processed_source) - expect(offenses.size).to eq(1) - expect(offenses.first.message).to eq(message) - end - - def expect_no_global_offenses(source, file = nil) - processed_source = parse_source(source, file) - offenses = _investigate(cop, processed_source) - expect(offenses.size).to eq(0) - end - let(:message) { 'Spec path should end with `_spec.rb`.' } it 'registers an offense for a repeated .rb' do diff --git a/spec/support/expect_offense.rb b/spec/support/expect_offense.rb index 957c728ec..c86f52e8f 100644 --- a/spec/support/expect_offense.rb +++ b/spec/support/expect_offense.rb @@ -24,4 +24,17 @@ def expect_no_offenses(source, filename = inspected_source_filename) def inspected_source_filename DEFAULT_FILENAME end + + def expect_global_offense(source, file = nil, message = '') + processed_source = parse_source(source, file) + offenses = _investigate(cop, processed_source) + expect(offenses.size).to eq(1) + expect(offenses.first.message).to eq(message) + end + + def expect_no_global_offenses(source, file = nil) + processed_source = parse_source(source, file) + offenses = _investigate(cop, processed_source) + expect(offenses.size).to eq(0) + end end