From 762cd62d3b9a66a4d071ac68395c78d42d5748ee Mon Sep 17 00:00:00 2001 From: Renzo Minelli Date: Fri, 10 Feb 2023 16:38:52 -0300 Subject: [PATCH] Support ruby 3.1 (#189) **BREAKING**: Drop support for ruby 2.4 * Upgrade rubocop to version 1.22.0 * InvalidPredicateMatcher and CustomIncludeMethods are removed * Add dependencies on `rubocop-rails` and `rubocop-performance` Thanks @bmulholland and @RenzoMinelli --- .github/workflows/rspec_rubocop.yml | 11 +- rubocop-airbnb/config/default.yml | 17 +- rubocop-airbnb/config/rubocop-performance.yml | 3 + rubocop-airbnb/config/rubocop-rails.yml | 3 + rubocop-airbnb/config/rubocop-rspec.yml | 13 +- .../class_or_module_declared_in_wrong_file.rb | 2 +- .../airbnb/const_assigned_in_wrong_file.rb | 2 +- .../rubocop/cop/airbnb/continuation_slash.rb | 2 +- .../lib/rubocop/cop/airbnb/default_scope.rb | 2 +- .../airbnb/factory_attr_references_class.rb | 2 +- .../cop/airbnb/factory_class_use_string.rb | 2 +- .../mass_assignment_accessible_modifier.rb | 2 +- .../cop/airbnb/module_method_in_wrong_file.rb | 2 +- .../lib/rubocop/cop/airbnb/no_timeout.rb | 2 +- .../rubocop/cop/airbnb/opt_arg_parameters.rb | 2 +- .../rubocop/cop/airbnb/phrase_bundle_keys.rb | 2 +- .../airbnb/risky_activerecord_invocation.rb | 2 +- ...pec_describe_or_context_under_namespace.rb | 2 +- .../airbnb/rspec_environment_modification.rb | 2 +- .../cop/airbnb/simple_modifier_conditional.rb | 2 +- .../lib/rubocop/cop/airbnb/simple_unless.rb | 2 +- .../cop/airbnb/spec_constant_assignment.rb | 7 +- .../rubocop/cop/airbnb/unsafe_yaml_marshal.rb | 2 +- rubocop-airbnb/rubocop-airbnb.gemspec | 6 +- ...s_or_module_declared_in_wrong_file_spec.rb | 171 ++++------ .../const_assigned_in_wrong_file_spec.rb | 159 ++++------ .../cop/airbnb/continuation_slash_spec.rb | 189 +++++------ .../rubocop/cop/airbnb/default_scope_spec.rb | 55 ++-- .../factory_attr_references_class_spec.rb | 202 +++++------- .../airbnb/factory_class_use_string_spec.rb | 32 +- ...ass_assignment_accessible_modifier_spec.rb | 39 +-- .../module_method_in_wrong_file_spec.rb | 189 +++++------ .../rubocop/cop/airbnb/no_timeout_spec.rb | 38 +-- .../cop/airbnb/opt_arg_parameter_spec.rb | 119 +++---- .../cop/airbnb/phrase_bundle_keys_spec.rb | 25 +- .../risky_activerecord_invocation_spec.rb | 52 ++- ...escribe_or_context_under_namespace_spec.rb | 296 +++++++----------- .../rspec_environment_modification_spec.rb | 72 ++--- .../simple_modifier_conditional_spec.rb | 152 ++++----- .../rubocop/cop/airbnb/simple_unless_spec.rb | 44 +-- .../airbnb/spec_constant_assignment_spec.rb | 102 +++--- .../cop/airbnb/unsafe_yaml_marshal_spec.rb | 60 ++-- rubocop-airbnb/spec/spec_helper.rb | 2 + 43 files changed, 834 insertions(+), 1258 deletions(-) diff --git a/.github/workflows/rspec_rubocop.yml b/.github/workflows/rspec_rubocop.yml index 994e3ff..28c7c6e 100644 --- a/.github/workflows/rspec_rubocop.yml +++ b/.github/workflows/rspec_rubocop.yml @@ -18,19 +18,16 @@ jobs: strategy: matrix: include: # use bundler 2.3 for ruby versions < 2.6 (https://bundler.io/compatibility.html) - - ruby-version: 2.4 - bundler-version: 2.3 - ruby-version: 2.5 bundler-version: 2.3 - ruby-version: 2.6 bundler-version: latest - ruby-version: 2.7 bundler-version: latest - # TODO: reenable tests against ruby 3.0 after upgrading base Rubocop version above 1.22.0, - # thus correcting the syntax errors when running the Layout/BlockAlignment cop - # https://github.com/airbnb/ruby/pull/189#discussion_r1101793307 - # - ruby-version: 3.0 - # bundler-version: latest + - ruby-version: 3.0 + bundler-version: latest + - ruby-version: 3.1 + bundler-version: latest steps: - uses: actions/checkout@v3 - name: Set up Ruby diff --git a/rubocop-airbnb/config/default.yml b/rubocop-airbnb/config/default.yml index ea5a78b..a8a9f79 100644 --- a/rubocop-airbnb/config/default.yml +++ b/rubocop-airbnb/config/default.yml @@ -15,14 +15,6 @@ AllCops: - 'vendor/**/*' - Vagrantfile - Guardfile - RSpec: - Patterns: - - _spec.rb - - "(?:^|/)spec/" - RSpec/FactoryBot: - Patterns: - - spec/factories/**/*.rb - - features/support/factories/**/*.rb # While Rubocop has released a bunch of new cops, not all of these cops have been evaluated as # part of this styleguide. To prevent new, unevaluated cops from imposing on this styleguide, we @@ -33,6 +25,15 @@ AllCops: # https://github.com/rubocop/rubocop/blob/1e55b1aa5e4c5eaeccad5d61f08b7930ed6bc341/config/default.yml#L89-L101 DisabledByDefault: true +RSpec: + Include: + - _spec.rb + - "(?:^|/)spec/" +RSpec/FactoryBot: + Include: + - spec/factories/**/*.rb + - features/support/factories/**/*.rb + inherit_from: - './rubocop-airbnb.yml' - './rubocop-bundler.yml' diff --git a/rubocop-airbnb/config/rubocop-performance.yml b/rubocop-airbnb/config/rubocop-performance.yml index 067f3ba..3300633 100644 --- a/rubocop-airbnb/config/rubocop-performance.yml +++ b/rubocop-airbnb/config/rubocop-performance.yml @@ -1,3 +1,6 @@ +require: + - rubocop-performance + Performance/Caller: Enabled: false diff --git a/rubocop-airbnb/config/rubocop-rails.yml b/rubocop-airbnb/config/rubocop-rails.yml index 1daa9eb..7ee5a1e 100644 --- a/rubocop-airbnb/config/rubocop-rails.yml +++ b/rubocop-airbnb/config/rubocop-rails.yml @@ -1,3 +1,6 @@ +require: + - rubocop-rails + # before_action doesn't seem to exist, so this doesn't make sense. Rails/ActionFilter: Enabled: false diff --git a/rubocop-airbnb/config/rubocop-rspec.yml b/rubocop-airbnb/config/rubocop-rspec.yml index 4da64d2..0503b7d 100644 --- a/rubocop-airbnb/config/rubocop-rspec.yml +++ b/rubocop-airbnb/config/rubocop-rspec.yml @@ -56,7 +56,6 @@ RSpec/DescribedClass: RSpec/EmptyExampleGroup: Description: Checks if an example group does not include any tests. Enabled: true - CustomIncludeMethods: [] RSpec/EmptyLineAfterExampleGroup: Description: Checks if there is an empty line after example group blocks. @@ -155,10 +154,6 @@ RSpec/InstanceVariable: Description: 'Checks for the usage of instance variables.' Enabled: false -RSpec/InvalidPredicateMatcher: - Description: Checks invalid usage for predicate matcher. - Enabled: false - RSpec/ItBehavesLike: Description: Checks that only one `it_behaves_like` style is used. Enabled: false @@ -314,18 +309,18 @@ RSpec/VoidExpect: Description: This cop checks void `expect()`. Enabled: false -Capybara/CurrentPathExpectation: +RSpec/Capybara/CurrentPathExpectation: Description: Checks that no expectations are set on Capybara's `current_path`. Enabled: false -Capybara/FeatureMethods: +RSpec/Capybara/FeatureMethods: Description: Checks for consistent method usage in feature specs. Enabled: false -FactoryBot/AttributeDefinedStatically: +RSpec/FactoryBot/AttributeDefinedStatically: Description: Always declare attribute values as blocks. Enabled: false -FactoryBot/CreateList: +RSpec/FactoryBot/CreateList: Description: Checks for create_list usage. Enabled: true diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/class_or_module_declared_in_wrong_file.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/class_or_module_declared_in_wrong_file.rb index 6d6bb57..96268af 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/class_or_module_declared_in_wrong_file.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/class_or_module_declared_in_wrong_file.rb @@ -56,7 +56,7 @@ module Airbnb # class Bar # nested class # end # end - class ClassOrModuleDeclaredInWrongFile < Cop + class ClassOrModuleDeclaredInWrongFile < Base include Inflections include RailsAutoloading diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/const_assigned_in_wrong_file.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/const_assigned_in_wrong_file.rb index 955fb58..ff87f1e 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/const_assigned_in_wrong_file.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/const_assigned_in_wrong_file.rb @@ -22,7 +22,7 @@ module Airbnb # module Foo # BAZ = 42 # end - class ConstAssignedInWrongFile < Cop + class ConstAssignedInWrongFile < Base include Inflections include RailsAutoloading diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/continuation_slash.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/continuation_slash.rb index 2925b59..730c05c 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/continuation_slash.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/continuation_slash.rb @@ -1,7 +1,7 @@ module RuboCop module Cop module Airbnb - class ContinuationSlash < Cop + class ContinuationSlash < Base MSG = 'Slash continuation should be reserved for closed string continuation. ' \ 'Many times it is used to get around other existing rules.'.freeze diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/default_scope.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/default_scope.rb index b2d671e..a6cbf4e 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/default_scope.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/default_scope.rb @@ -3,7 +3,7 @@ module Cop module Airbnb # Cop to help prevent the scorge of Default Scopes from ActiveRecord. # Once in place they are almost impossible to remove. - class DefaultScope < Cop + class DefaultScope < Base MSG = 'Avoid `default_scope`. Default scopes make it difficult to '\ 'refactor data access patterns since the scope becomes part '\ 'of every query unless explicitly excluded, even when it is '\ diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/factory_attr_references_class.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/factory_attr_references_class.rb index b4b5a78..1ca9de0 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/factory_attr_references_class.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/factory_attr_references_class.rb @@ -4,7 +4,7 @@ module Airbnb # Cop to enforce "attr { CONST }" instead of "attr CONST" in factories, # because the latter forces autoload, which slows down spec startup time and # Zeus reload time after touching a model. - class FactoryAttrReferencesClass < Cop + class FactoryAttrReferencesClass < Base MSG = "Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. " \ "This enables faster spec startup time and Zeus reload time.".freeze diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/factory_class_use_string.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/factory_class_use_string.rb index 035cf5b..0b53cd1 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/factory_class_use_string.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/factory_class_use_string.rb @@ -3,7 +3,7 @@ module Cop module Airbnb # Cop to tell developers to use :class => "MyClass" instead of :class => MyClass, # because the latter slows down reloading zeus. - class FactoryClassUseString < Cop + class FactoryClassUseString < Base MSG = 'Instead of :class => MyClass, use :class => "MyClass". ' \ "This enables faster spec startup time and faster Zeus reload time.".freeze diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/mass_assignment_accessible_modifier.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/mass_assignment_accessible_modifier.rb index 1fa9517..be982e1 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/mass_assignment_accessible_modifier.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/mass_assignment_accessible_modifier.rb @@ -3,7 +3,7 @@ module Cop module Airbnb # Modifying Mass assignment restrictions eliminates the entire point of disabling # mass assignment. It's a lazy, potentially dangerous approach that should be discouraged. - class MassAssignmentAccessibleModifier < Cop + class MassAssignmentAccessibleModifier < Base MSG = 'Do no override and objects mass assignment restrictions.'.freeze def on_send(node) diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/module_method_in_wrong_file.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/module_method_in_wrong_file.rb index 1989127..c5ca811 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/module_method_in_wrong_file.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/module_method_in_wrong_file.rb @@ -59,7 +59,7 @@ module Airbnb # end # end # end - class ModuleMethodInWrongFile < Cop + class ModuleMethodInWrongFile < Base include Inflections include RailsAutoloading diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/no_timeout.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/no_timeout.rb index f19f4ed..66e929c 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/no_timeout.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/no_timeout.rb @@ -1,7 +1,7 @@ module RuboCop module Cop module Airbnb - class NoTimeout < Cop + class NoTimeout < Base MSG = 'Do not use Timeout.timeout. In combination with Rails autoloading, ' \ 'timeout can cause Segmentation Faults in version of Ruby we use. ' \ diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/opt_arg_parameters.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/opt_arg_parameters.rb index b5487dc..c1cf686 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/opt_arg_parameters.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/opt_arg_parameters.rb @@ -3,7 +3,7 @@ module Cop module Airbnb # Cop to enforce use of options hash over default arguments # https://github.com/airbnb/ruby#no-default-args - class OptArgParameters < Cop + class OptArgParameters < Base MSG = 'Do not use default positional arguments. '\ 'Use keyword arguments or an options hash instead.'.freeze diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/phrase_bundle_keys.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/phrase_bundle_keys.rb index cde9564..cec6ab4 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/phrase_bundle_keys.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/phrase_bundle_keys.rb @@ -24,7 +24,7 @@ module Airbnb # ), # } # end - class PhraseBundleKeys < Cop + class PhraseBundleKeys < Base MESSAGE = 'Phrase bundle keys should match their translation keys.'.freeze diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/risky_activerecord_invocation.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/risky_activerecord_invocation.rb index 8f2b930..0a65200 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/risky_activerecord_invocation.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/risky_activerecord_invocation.rb @@ -2,7 +2,7 @@ module RuboCop module Cop module Airbnb # Disallow ActiveRecord calls that pass interpolated or added strings as an argument. - class RiskyActiverecordInvocation < Cop + class RiskyActiverecordInvocation < Base VULNERABLE_AR_METHODS = [ :delete_all, :destroy_all, diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/rspec_describe_or_context_under_namespace.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/rspec_describe_or_context_under_namespace.rb index 917a1d5..a5a3369 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/rspec_describe_or_context_under_namespace.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/rspec_describe_or_context_under_namespace.rb @@ -20,7 +20,7 @@ module Airbnb # # describe Foo::Bar # end - class RspecDescribeOrContextUnderNamespace < Cop + class RspecDescribeOrContextUnderNamespace < Base DESCRIBE_OR_CONTEXT_UNDER_NAMESPACE_MSG = 'Declaring a `module` in a spec can break autoloading because subsequent references ' \ 'to it will not cause it to be loaded from the app. This could cause flaky tests.'.freeze diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/rspec_environment_modification.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/rspec_environment_modification.rb index 7730ac2..b4ffc09 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/rspec_environment_modification.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/rspec_environment_modification.rb @@ -28,7 +28,7 @@ module Airbnb # before(:each) do # stub_env(:production) # end - class RspecEnvironmentModification < Cop + class RspecEnvironmentModification < Base def_node_matcher :allow_or_expect_rails_env, <<-PATTERN (send (send nil? {:expect :allow} (send (const nil? :Rails) :env)) :to ...) PATTERN diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/simple_modifier_conditional.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/simple_modifier_conditional.rb index 574309b..714cd9f 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/simple_modifier_conditional.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/simple_modifier_conditional.rb @@ -3,7 +3,7 @@ module Cop module Airbnb # Cop to tackle prevent more complicated modifier if/unless statements # https://github.com/airbnb/ruby#only-simple-if-unless - class SimpleModifierConditional < Cop + class SimpleModifierConditional < Base MSG = 'Modifier if/unless usage is okay when the body is simple, ' \ 'the condition is simple, and the whole thing fits on one line. ' \ 'Otherwise, avoid modifier if/unless.'.freeze diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/simple_unless.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/simple_unless.rb index 257614e..7a57f7a 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/simple_unless.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/simple_unless.rb @@ -3,7 +3,7 @@ module Cop module Airbnb # Cop to tackle prevent unless statements with multiple conditions # https://github.com/airbnb/ruby#unless-with-multiple-conditions - class SimpleUnless < Cop + class SimpleUnless < Base MSG = 'Unless usage is okay when there is only one conditional'.freeze def_node_matcher :multiple_conditionals?, '(if ({and or :^} ...) ...)' diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/spec_constant_assignment.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/spec_constant_assignment.rb index 788c8bf..92ff742 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/spec_constant_assignment.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/spec_constant_assignment.rb @@ -25,16 +25,15 @@ module Airbnb # describe Something do # before { stub_const('MyClass::PAYLOAD', [1, 2, 3]) # end - class SpecConstantAssignment < Cop - include RuboCop::RSpec::TopLevelDescribe + class SpecConstantAssignment < Base MESSAGE = "Defining constants inside of specs can cause spurious behavior. " \ - "It is almost always preferable to use `let` statements, "\ + "It is almost always preferable to use `let` statements, " \ "anonymous class/module definitions, or stub_const".freeze def on_casgn(node) return unless in_spec_file?(node) parent_module_name = node.parent_module_name - if node.parent_module_name && parent_module_name != 'Object' + if parent_module_name && parent_module_name != "Object" return end add_offense(node, message: MESSAGE) diff --git a/rubocop-airbnb/lib/rubocop/cop/airbnb/unsafe_yaml_marshal.rb b/rubocop-airbnb/lib/rubocop/cop/airbnb/unsafe_yaml_marshal.rb index 304fe12..0687379 100644 --- a/rubocop-airbnb/lib/rubocop/cop/airbnb/unsafe_yaml_marshal.rb +++ b/rubocop-airbnb/lib/rubocop/cop/airbnb/unsafe_yaml_marshal.rb @@ -2,7 +2,7 @@ module RuboCop module Cop module Airbnb # Disallow use of YAML/Marshal methods that can trigger RCE on untrusted input - class UnsafeYamlMarshal < Cop + class UnsafeYamlMarshal < Base MSG = 'Using unsafe YAML parsing methods on untrusted input can lead ' \ 'to remote code execution. Use `safe_load`, `parse`, `parse_file`, or ' \ '`parse_stream` instead'.freeze diff --git a/rubocop-airbnb/rubocop-airbnb.gemspec b/rubocop-airbnb/rubocop-airbnb.gemspec index 76baf89..c549687 100644 --- a/rubocop-airbnb/rubocop-airbnb.gemspec +++ b/rubocop-airbnb/rubocop-airbnb.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |spec| spec.license = 'MIT' spec.version = RuboCop::Airbnb::VERSION spec.platform = Gem::Platform::RUBY - spec.required_ruby_version = '>= 2.4' + spec.required_ruby_version = '>= 2.5' spec.require_paths = ['lib'] spec.files = Dir[ @@ -25,9 +25,9 @@ Gem::Specification.new do |spec| 'Gemfile', ] - spec.add_dependency('rubocop', '~> 0.93.1') + spec.add_dependency('rubocop', '~> 1.22.0') spec.add_dependency('rubocop-performance', '~> 1.10.2') spec.add_dependency('rubocop-rails', '~> 2.9.1') - spec.add_dependency('rubocop-rspec', '~> 1.44.1') + spec.add_dependency('rubocop-rspec', '~> 2.0.0') spec.add_development_dependency('rspec', '~> 3.5') end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/class_or_module_declared_in_wrong_file_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/class_or_module_declared_in_wrong_file_spec.rb index c187edc..78e1073 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/class_or_module_declared_in_wrong_file_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/class_or_module_declared_in_wrong_file_spec.rb @@ -1,6 +1,4 @@ -describe RuboCop::Cop::Airbnb::ClassOrModuleDeclaredInWrongFile do - subject(:cop) { described_class.new(config) } - +describe RuboCop::Cop::Airbnb::ClassOrModuleDeclaredInWrongFile, :config do let(:config) do RuboCop::Config.new( { @@ -25,150 +23,119 @@ end it 'rejects if class declaration is in a file with non-matching name' do - source = [ - 'module Foo', - ' module Bar', - ' class Baz', - ' end', - ' end', - 'end', - ].join("\n") - File.open "#{models_dir}/qux.rb", "w" do |file| - inspect_source(source, file) + expect_offense(<<~RUBY, file) + module Foo + ^^^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone references this class/module, move its definition to a file that matches its name. Module Foo should be defined in foo.rb. + module Bar + ^^^^^^^^^^ In order for Rails autoloading [...] + class Baz + ^^^^^^^^^ In order for Rails autoloading [...] + end + end + end + RUBY end - - expect(cop.offenses.size).to eq(3) - expect(cop.offenses.map(&:line).sort).to eq([1, 2, 3]) - expect(cop.offenses.first.message).to include('Module Foo should be defined in foo.rb.') end it 'rejects if class declaration is in a file with matching name but wrong parent dir' do - source = [ - 'module Foo', - ' module Bar', - ' class Baz', - ' end', - ' end', - 'end', - ].join("\n") - File.open "#{models_dir}/baz.rb", "w" do |file| - inspect_source(source, file) + expect_offense(<<~RUBY, file) + module Foo + ^^^^^^^^^^ In order for Rails autoloading [...] + module Bar + ^^^^^^^^^^ In order for Rails autoloading [...] + class Baz + ^^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone references this class/module, move its definition to a file that matches its name. Class Baz should be defined in foo/bar/baz.rb. + end + end + end + RUBY end - - expect(cop.offenses.size).to eq(3) - expect(cop.offenses.map(&:line).sort).to eq([1, 2, 3]) - expect(cop.offenses.last.message).to include('Class Baz should be defined in foo/bar/baz.rb.') end it 'accepts if class declaration is in a file with matching name and right parent dir' do - source = [ - 'module Foo', - ' module Bar', - ' class Baz', - ' end', - ' end', - 'end', - ].join("\n") - FileUtils.mkdir_p "#{models_dir}/foo/bar" File.open "#{models_dir}/foo/bar/baz.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + module Foo + module Bar + class Baz + end + end + end + RUBY end - - expect(cop.offenses).to be_empty end it 'rejects if class declaration is in wrong dir and parent module uses ::' do - source = [ - 'module Foo::Bar', - ' class Baz', - ' end', - 'end', - ].join("\n") - FileUtils.mkdir_p "#{models_dir}/bar" File.open "#{models_dir}/bar/baz.rb", "w" do |file| - inspect_source(source, file) + expect_offense(<<~RUBY, file) + module Foo::Bar + ^^^^^^^^^^^^^^^ In order for Rails autoloading [...] + class Baz + ^^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone references this class/module, move its definition to a file that matches its name. Class Baz should be defined in foo/bar/baz.rb. + end + end + RUBY end - - expect(cop.offenses.map(&:line).sort).to eq([1, 2]) - expect(cop.offenses.last.message).to include('Class Baz should be defined in foo/bar/baz.rb.') end it 'accepts if class declaration is in a file with matching name and parent module uses ::' do - source = [ - 'module Foo::Bar', - ' class Baz', - ' end', - 'end', - ].join("\n") - FileUtils.mkdir_p "#{models_dir}/foo/bar" File.open "#{models_dir}/foo/bar/baz.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + module Foo::Bar + class Baz + end + end + RUBY end - - expect(cop.offenses).to be_empty end it 'accepts class declaration where the containing class uses an acronym' do - source = [ - 'module CSVFoo', - ' class Baz', - ' end', - 'end', - ].join("\n") - FileUtils.mkdir_p "#{models_dir}/csv_foo" File.open "#{models_dir}/csv_foo/baz.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY) + module CSVFoo + class Baz + end + end + RUBY end - - expect(cop.offenses).to be_empty end it 'ignores class/module declaration in a rake task' do - source = [ - 'class Baz', - 'end', - ].join("\n") - File.open "#{models_dir}/foo.rake", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + class Baz + end + RUBY end - - expect(cop.offenses).to be_empty end it 'suggests moving error classes into the file that defines the owning scope' do - source = [ - 'module Foo', - ' class BarError < StandardError; end', - 'end', - ].join("\n") - File.open "#{models_dir}/bar.rb", "w" do |file| - inspect_source(source, file) + expect_offense(<<~RUBY, file) + module Foo + ^^^^^^^^^^ In order for Rails autoloading [...] + class BarError < StandardError; end + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone references this class, move its definition to a file that defines the owning module. Class BarError should be defined in foo.rb. + end + RUBY end - - expect(cop.offenses.map(&:line)).to include(2) - expect(cop.offenses.map(&:message)).to include(%r{Class BarError should be defined in foo\.rb.}) end it 'recognizes error class based on the superclass name' do - source = [ - 'module Foo', - ' class Bar < StandardError; end', - 'end', - ].join("\n") - File.open "#{models_dir}/bar.rb", "w" do |file| - inspect_source(source, file) + expect_offense(<<~RUBY, file) + module Foo + ^^^^^^^^^^ In order for Rails autoloading [...] + class Bar < StandardError; end + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone references this class, move its definition to a file that defines the owning module. Class Bar should be defined in foo.rb. + end + RUBY end - - expect(cop.offenses.map(&:line)).to include(2) - expect(cop.offenses.map(&:message)).to include(%r{Class Bar should be defined in foo\.rb.}) end end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/const_assigned_in_wrong_file_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/const_assigned_in_wrong_file_spec.rb index 3bbf3a9..216d57a 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/const_assigned_in_wrong_file_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/const_assigned_in_wrong_file_spec.rb @@ -1,6 +1,4 @@ -describe RuboCop::Cop::Airbnb::ConstAssignedInWrongFile do - subject(:cop) { described_class.new(config) } - +describe RuboCop::Cop::Airbnb::ConstAssignedInWrongFile, :config do let(:config) do RuboCop::Config.new( { @@ -25,154 +23,113 @@ end it 'rejects if const assignment is in a file with non-matching name' do - source = [ - 'module Foo', - ' module Bar', - ' BAZ = 42', - ' end', - 'end', - ].join("\n") - File.open "#{models_dir}/qux.rb", "w" do |file| - inspect_source(source, file) + expect_offense(<<~RUBY, file) + module Foo + module Bar + BAZ = 42 + ^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone references this const, move the const assignment to a file that defines the owning module. Const BAZ should be defined in foo/bar.rb. + end + end + RUBY end - - expect(cop.offenses.map(&:line)).to include(3) - expect(cop.offenses.map(&:message)).to include(%r{Const BAZ should be defined in foo/bar\.rb.}) end it 'rejects if const assignment is in a file with matching name but wrong parent dir' do - source = [ - 'module Foo', - ' module Bar', - ' BAZ = 42', - ' end', - 'end', - ].join("\n") - File.open "#{models_dir}/bar.rb", "w" do |file| - inspect_source(source, file) + expect_offense(<<~RUBY, file) + module Foo + module Bar + BAZ = 42 + ^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone references this const, move the const assignment to a file that defines the owning module. Const BAZ should be defined in foo/bar.rb. + end + end + RUBY end - - expect(cop.offenses.map(&:line)).to include(3) - expect(cop.offenses.map(&:message)).to include(%r{Const BAZ should be defined in foo/bar\.rb.}) end it 'accepts if const assignment is in a file with matching name and right parent dir' do - source = [ - 'module Foo', - ' module Bar', - ' BAZ = 42', - ' end', - 'end', - ].join("\n") - FileUtils.mkdir "#{models_dir}/foo" File.open "#{models_dir}/foo/bar.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + module Foo + module Bar + BAZ = 42 + end + end + RUBY end - - expect(cop.offenses).to be_empty end it 'accepts if const assignment is in a file with matching name and right parent dir' \ 'and parent modules were defined on a single line' do - source = [ - 'module Foo::Bar', - ' BAZ = 42', - 'end', - ].join("\n") - FileUtils.mkdir "#{models_dir}/foo" File.open "#{models_dir}/foo/bar.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + module Foo::Bar + BAZ = 42 + end + RUBY end - - expect(cop.offenses).to be_empty end it 'accepts if const assignment is in a file whose name matches the const and right parent dir' do - source = [ - 'module Foo', - ' module Bar', - ' BAZ = 42', - ' end', - 'end', - ].join("\n") - FileUtils.mkdir_p "#{models_dir}/foo/bar" File.open "#{models_dir}/foo/bar/baz.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + module Foo + module Bar + BAZ = 42 + end + end + RUBY end - - expect(cop.offenses).to be_empty end it 'ignores if const assignment is assigning something in another scope' do - source = [ - 'module Foo', - ' Bar::BAZ = 42', - 'end', - ].join("\n") - File.open "#{models_dir}/foo.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + module Foo + Bar::BAZ = 42 + end + RUBY end - - expect(cop.offenses).to be_empty end it 'accepts const assignment where the containing class uses an acronym' do - source = [ - 'module CSVFoo', - ' BAZ = 42', - 'end', - ].join("\n") - File.open "#{models_dir}/csv_foo.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + module CSVFoo + BAZ = 42 + end + RUBY end - - expect(cop.offenses).to be_empty end it 'suggests moving a global const into a namespace' do - source = [ - 'FOO = 42', - ].join("\n") - File.open "#{models_dir}/bar.rb", "w" do |file| - inspect_source(source, file) + expect_offense(<<~RUBY, file) + FOO = 42 + ^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone references this const, move the const assignment to a file that defines the owning module. Const FOO should be moved into a namespace or defined in foo.rb. + RUBY end - - expect(cop.offenses.map(&:line)).to eq([1]) - expect(cop.offenses.first.message). - to include('Const FOO should be moved into a namespace or defined in foo.rb.') end it 'ignores const assignment in global namespace in a rake task' do - source = [ - 'FOO = 42', - ].join("\n") - File.open "#{models_dir}/foo.rake", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + FOO = 42 + RUBY end - - expect(cop.offenses).to be_empty end it 'ignores const assignment in a class in a rake task' do - source = [ - 'class Baz', - ' FOO = 42', - 'end', - ].join("\n") - File.open "#{models_dir}/foo.rake", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + class Baz + FOO = 42 + end + RUBY end - - expect(cop.offenses).to be_empty end end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/continuation_slash_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/continuation_slash_spec.rb index 5bc5ca8..4728dd2 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/continuation_slash_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/continuation_slash_spec.rb @@ -1,162 +1,127 @@ -describe RuboCop::Cop::Airbnb::ContinuationSlash do - subject(:cop) { described_class.new } - +describe RuboCop::Cop::Airbnb::ContinuationSlash, :config do it 'rejects continuations used to continue a method call with trailing dot' do - source = [ - 'User. \\', - ' first_name', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.map(&:line).sort).to eq([1]) + expect_offense(<<~RUBY) + User. \\ + ^^^^^^^ Slash continuation should be reserved [...] + first_name + RUBY end context 'on assignment' do it 'rejects on constant assignment' do - source = [ - 'CONSTANT = "I am a string that \\', - ' spans multiple lines"', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + CONSTANT = "I am a string that \\ + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Slash continuation should be reserved [...] + spans multiple lines" + RUBY end it 'rejects on local variable assignment' do - source = [ - 'variable = "I am a string that \\', - ' spans multiple lines"', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + variable = "I am a string that \\ + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Slash continuation should be reserved [...] + spans multiple lines" + RUBY end it 'rejects on @var assignment' do - source = [ - 'class SomeClass', - ' @class_var = "I am a string that \\', - ' spans multiple lines"', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + class SomeClass + @class_var = "I am a string that \\ + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Slash continuation should be reserved [...] + spans multiple lines" + end + RUBY end it 'rejects on @@var assignment' do - source = [ - 'class SomeClass', - ' @@class_var = "I am a string that \\', - ' spans multiple lines"', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + class SomeClass + @@class_var = "I am a string that \\ + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Slash continuation should be reserved [...] + spans multiple lines" + end + RUBY end end context 'in conditional continuation' do it 'rejects if with continuation \\ before operator' do - source = [ - 'if true \\', - ' && false', - ' return false', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + if true \\ + ^^^^^^^^^ Slash continuation should be reserved [...] + && false + return false + end + RUBY end it 'rejects unless with continuation \\' do - source = [ - 'unless true \\', - ' && false', - ' return false', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + unless true \\ + ^^^^^^^^^^^^^ Slash continuation should be reserved [...] + && false + return false + end + RUBY end it 'rejects if with continuation \\ after operator' do - source = [ - 'if true || \\', - ' false', - ' return false', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + if true || \\ + ^^^^^^^^^^^^ Slash continuation should be reserved [...] + false + return false + end + RUBY end end context 'open string continuation' do it 'rejects contination with space before \\' do - source = [ - 'I18n.t("I am a string that \\', - ' spans multiple lines")', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + I18n.t("I am a string that \\ + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Slash continuation should be reserved [...] + spans multiple lines") + RUBY end it 'rejects contination with no space before \\' do - source = [ - 'I18n.t(\'I am a string that \\', - ' spans multiple lines\')', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + I18n.t(\'I am a string that \\ + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Slash continuation should be reserved [...] + spans multiple lines\') + RUBY end end context 'closed string continuation' do it 'allows double quote string with no space before \\' do - source = [ - 'I18n.t("I am a string that "\\', - ' "spans multiple lines")', - ].join("\n") - inspect_source(source) - - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + I18n.t("I am a string that "\\ + "spans multiple lines") + RUBY end it 'allows double quote string with space before \\' do - source = [ - 'I18n.t("I am a string that " \\', - ' "spans multiple lines")', - ].join("\n") - inspect_source(source) - - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + I18n.t("I am a string that " \\ + "spans multiple lines") + RUBY end it 'allows single quote string with no space before \\' do - source = [ - 'I18n.t(\'I am a string that \'\\', - ' \'spans multiple lines\')', - ].join("\n") - inspect_source(source) - - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + I18n.t(\'I am a string that \'\\ + \'spans multiple lines\') + RUBY end it 'allows single quote string with space before \\' do - source = [ - 'I18n.t(\'I am a string that \' \\', - ' \'spans multiple lines\')', - ].join("\n") - inspect_source(source) - - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + I18n.t(\'I am a string that \' \\ + \'spans multiple lines\') + RUBY end end end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/default_scope_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/default_scope_spec.rb index 634f159..63eec89 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/default_scope_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/default_scope_spec.rb @@ -1,38 +1,31 @@ -describe RuboCop::Cop::Airbnb::DefaultScope do - subject(:cop) { described_class.new } - +describe RuboCop::Cop::Airbnb::DefaultScope, :config do it 'rejects with default_scopes' do - source = [ - '# encoding: UTF-8', - 'module SurveyQuestion', - ' class Host < PhraseBundle', - ' db_magic :connection => AIRMISC_MASTER,', - ' :slaves => AIRMISC_DB_SLAVES,', - ' :force_slave_reads => FORCE_SLAVE_READS', - '', - ' default_scope where(active: true)', - '', - ' end', - 'end', - ].join("\n") - inspect_source(source) + expect_offense(<<~RUBY) + # encoding: UTF-8 + module SurveyQuestion + class Host < PhraseBundle + db_magic :connection => AIRMISC_MASTER, + :slaves => AIRMISC_DB_SLAVES, + :force_slave_reads => FORCE_SLAVE_READS + + default_scope where(active: true) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Avoid `default_scope`. [...] - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.map(&:line).sort).to eq([8]) + end + end + RUBY end it 'passes when there is no default_scope' do - source = [ - '# encoding: UTF-8', - 'module SurveyQuestion', - ' class Host < PhraseBundle', - ' db_magic :connection => AIRMISC_MASTER,', - ' :slaves => AIRMISC_DB_SLAVES,', - ' :force_slave_reads => FORCE_SLAVE_READS', - ' end', - 'end', - ].join("\n") - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + # encoding: UTF-8 + module SurveyQuestion + class Host < PhraseBundle + db_magic :connection => AIRMISC_MASTER, + :slaves => AIRMISC_DB_SLAVES, + :force_slave_reads => FORCE_SLAVE_READS + end + end + RUBY end end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/factory_attr_references_class_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/factory_attr_references_class_spec.rb index d0e7c91..68d2842 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/factory_attr_references_class_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/factory_attr_references_class_spec.rb @@ -1,160 +1,120 @@ -describe RuboCop::Cop::Airbnb::FactoryAttrReferencesClass do - subject(:cop) { described_class.new } - +describe RuboCop::Cop::Airbnb::FactoryAttrReferencesClass, :config do it 'rejects with `attr_name CONST_NAME` in a factory' do - source = [ - 'factory :reservation2 do', - ' status Reservation2::STATUS_NEW', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.map(&:line)).to eq([2]) + expect_offense(<<~RUBY) + factory :reservation2 do + status Reservation2::STATUS_NEW + ^^^^^^^^^^^^^^^^^^^^^^^^ Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. [...] + end + RUBY end it 'passes with `attr_name { CONST_NAME }` in a factory' do - source = [ - 'factory :reservation2 do', - ' status { Reservation2::STATUS_NEW }', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + factory :reservation2 do + status { Reservation2::STATUS_NEW } + end + RUBY end it 'rejects with `attr_name [CONST_NAME]`' do - source = [ - 'factory :reservation2 do', - ' statuses [Reservation2::STATUS_NEW]', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.map(&:line)).to eq([2]) + expect_offense(<<~RUBY) + factory :reservation2 do + statuses [Reservation2::STATUS_NEW] + ^^^^^^^^^^^^^^^^^^^^^^^^ Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. [...] + end + RUBY end it 'passes with `attr_name { [CONST_NAME] }`' do - source = [ - 'factory :reservation2 do', - ' statuses { [Reservation2::STATUS_NEW] }', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + factory :reservation2 do + statuses { [Reservation2::STATUS_NEW] } + end + RUBY end it 'rejects with `attr_name [[CONST_NAME]]`' do - source = [ - 'factory :reservation2 do', - ' statuses [[Reservation2::STATUS_NEW]]', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.map(&:line)).to eq([2]) + expect_offense(<<~RUBY) + factory :reservation2 do + statuses [[Reservation2::STATUS_NEW]] + ^^^^^^^^^^^^^^^^^^^^^^^^ Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. [...] + end + RUBY end it 'passes with `attr_name { [[CONST_NAME]] }`' do - source = [ - 'factory :reservation2 do', - ' statuses { [[Reservation2::STATUS_NEW]] }', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + factory :reservation2 do + statuses { [[Reservation2::STATUS_NEW]] } + end + RUBY end it 'rejects with `attr_name({ ConstName => something })' do - source = [ - 'factory :reservation2 do', - ' status_names({ Reservation2::STATUS_NEW => "new" })', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.map(&:line)).to eq([2]) + expect_offense(<<~RUBY) + factory :reservation2 do + status_names({ Reservation2::STATUS_NEW => "new" }) + ^^^^^^^^^^^^^^^^^^^^^^^^ Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. [...] + end + RUBY end it 'passes with `attr_name { { ConstName => something } }`' do - source = [ - 'factory :reservation2 do', - ' status_names { { Reservation2::STATUS_NEW => "new" } }', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + factory :reservation2 do + status_names { { Reservation2::STATUS_NEW => "new" } } + end + RUBY end it 'rejects with `attr_name ConstName[:symbol]`' do - source = [ - 'factory :airlock_rule do', - ' stickiness Airlock::STICKINESS[:user]', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.map(&:line)).to eq([2]) + expect_offense(<<~RUBY) + factory :airlock_rule do + stickiness Airlock::STICKINESS[:user] + ^^^^^^^^^^^^^^^^^^^ Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. [...] + end + RUBY end it 'passes with `attr_name { ConstName[:symbol] }`' do - source = [ - 'factory :airlock_rule do', - ' stickiness { Airlock::STICKINESS[:user] }', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + factory :airlock_rule do + stickiness { Airlock::STICKINESS[:user] } + end + RUBY end it 'rejects even if the const is not the first attr' do - source = [ - 'factory :reservation2 do', - ' trait :accepted do', - ' cancel_policy Hosting::CANCEL_FLEXIBLE', - ' status Reservation2::STATUS_NEW', - ' end', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(2) - expect(cop.offenses.map(&:line)).to eq([3, 4]) + expect_offense(<<~RUBY) + factory :reservation2 do + trait :accepted do + cancel_policy Hosting::CANCEL_FLEXIBLE + ^^^^^^^^^^^^^^^^^^^^^^^^ Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. [...] + status Reservation2::STATUS_NEW + ^^^^^^^^^^^^^^^^^^^^^^^^ Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. [...] + end + end + RUBY end it 'rejects with `attr_name CONST_NAME` in a trait' do - source = [ - 'factory :reservation2 do', - ' trait :accepted do', - ' status Reservation2::STATUS_NEW', - ' end', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.map(&:line)).to eq([3]) + expect_offense(<<~RUBY) + factory :reservation2 do + trait :accepted do + status Reservation2::STATUS_NEW + ^^^^^^^^^^^^^^^^^^^^^^^^ Instead of attr_name MyClass::MY_CONST, use attr_name { MyClass::MY_CONST }. [...] + end + end + RUBY end it 'passes with `attr_name { CONST_NAME }` in a trait' do - source = [ - 'factory :reservation2 do', - ' trait :accepted do', - ' status { Reservation2::STATUS_NEW }', - ' end', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + factory :reservation2 do + trait :accepted do + status { Reservation2::STATUS_NEW } + end + end + RUBY end end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/factory_class_use_string_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/factory_class_use_string_spec.rb index 1c9f259..4dbe899 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/factory_class_use_string_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/factory_class_use_string_spec.rb @@ -1,26 +1,18 @@ -describe RuboCop::Cop::Airbnb::FactoryClassUseString do - subject(:cop) { described_class.new } - +describe RuboCop::Cop::Airbnb::FactoryClassUseString, :config do it 'rejects with :class => Model' do - source = [ - 'factory :help_answer, :class => Help::Answer do', - ' text { Faker::Company.name }', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.map(&:line)).to eq([1]) + expect_offense(<<~RUBY) + factory :help_answer, :class => Help::Answer do + ^^^^^^^^^^^^^^^^^^^^^^ Instead of :class => MyClass, use :class => "MyClass". [...] + text { Faker::Company.name } + end + RUBY end it 'passes with :class => "Model"' do - source = [ - 'factory :help_answer, :class => "Help::Answer" do', - ' text { Faker::Company.name }', - 'end', - ].join("\n") - inspect_source(source) - - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + factory :help_answer, :class => "Help::Answer" do + text { Faker::Company.name } + end + RUBY end end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/mass_assignment_accessible_modifier_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/mass_assignment_accessible_modifier_spec.rb index 455834e..70ea76e 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/mass_assignment_accessible_modifier_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/mass_assignment_accessible_modifier_spec.rb @@ -1,28 +1,23 @@ -describe RuboCop::Cop::Airbnb::MassAssignmentAccessibleModifier do - subject(:cop) { described_class.new } - +describe RuboCop::Cop::Airbnb::MassAssignmentAccessibleModifier, :config do it 'rejects when accessible= is called' do - source = [ - 'def some_method', - ' user = User.new', - ' user.accessible = [:first_name, :last_name]', - ' user.update_attributes(:first_name => "Walter", :last_name => "Udoing")', - 'end', - ].join("\n") - inspect_source(source) - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + def some_method + user = User.new + user.accessible = [:first_name, :last_name] + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do no override and objects mass assignment restrictions. + user.update_attributes(:first_name => "Walter", :last_name => "Udoing") + end + RUBY end it 'accepts when accessible= is not called' do - source = [ - 'def some_method', - ' user = User.new', - ' user.first_name = "Walter"', - ' user.last_name = "Udoing"', - ' user.save!', - 'end', - ].join("\n") - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + def some_method + user = User.new + user.first_name = "Walter" + user.last_name = "Udoing" + user.save! + end + RUBY end end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/module_method_in_wrong_file_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/module_method_in_wrong_file_spec.rb index 4499a29..b053a67 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/module_method_in_wrong_file_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/module_method_in_wrong_file_spec.rb @@ -1,6 +1,4 @@ -describe RuboCop::Cop::Airbnb::ModuleMethodInWrongFile do - subject(:cop) { described_class.new(config) } - +describe RuboCop::Cop::Airbnb::ModuleMethodInWrongFile, :config do let(:config) do RuboCop::Config.new( { @@ -25,157 +23,120 @@ end it 'rejects if module method is in file with non-matching name' do - source = [ - 'module Hello', - ' module Foo', - ' def baz', # error is here if this file is named bar.rb - ' 42', - ' end', - ' end', - 'end', - ].join("\n") - File.open "#{models_dir}/bar.rb", "w" do |file| - inspect_source(source, file) + expect_offense(<<~RUBY, file) + module Hello + module Foo + def baz + ^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone calls this method, move the method definition to a file that defines the module. This file just uses the module as a namespace for another class or module. Method baz should be defined in hello/foo.rb. + 42 + end + end + end + RUBY end - - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.map(&:line).sort).to eq([3]) - expect(cop.offenses.first.message).to include("Method baz should be defined in hello/foo.rb.") end it 'accepts if module method is in file with matching name' do - source = [ - 'module Foo', - ' def baz', - ' 42', - ' end', - 'end', - ].join("\n") - File.open "#{models_dir}/foo.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + module Foo + def baz + 42 + end + end + RUBY end - - expect(cop.offenses).to be_empty end it 'rejects with "self." static methods and a non-matching name' do - source = [ - 'module Foo', - ' def self.baz', - ' 42', - ' end', - 'end', - ].join("\n") - File.open "#{models_dir}/bar.rb", "w" do |file| - inspect_source(source, file) + expect_offense(<<~RUBY, file) + module Foo + def self.baz + ^^^^^^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone calls this method, move the method definition to a file that defines the module. This file just uses the module as a namespace for another class or module. Method (self) should be defined in foo.rb. + 42 + end + end + RUBY end - - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.map(&:line).sort).to eq([2]) end it 'accepts with "self." static methods and a matching name' do - source = [ - 'module Foo', - ' def self.baz', - ' 42', - ' end', - 'end', - ].join("\n") - File.open "#{models_dir}/foo.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + module Foo + def self.baz + 42 + end + end + RUBY end - - expect(cop.offenses).to be_empty end - it 'rejects with "<<" static methods and a non-matching name' do - source = [ - 'module Foo', - ' class << self', - ' def baz', - ' 42', - ' end', - ' end', - 'end', - ].join("\n") - + xit 'rejects with "<<" static methods and a non-matching name' do File.open "#{models_dir}/bar.rb", "w" do |file| - inspect_source(source, file) + expect_offense(<<~RUBY, file) + module Foo + class << self + def baz + ^^^^^^^ In order for Rails autoloading to be able to find and load this file when someone calls this method, move the method definition to a file that defines the module. This file just uses the module as a namespace for another class or module. Method baz should be defined in foo.rb. + 42 + end + end + end + RUBY end - - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.map(&:line).sort).to eq([3]) end it 'accepts with "<<" static methods and a matching name' do - source = [ - 'module Foo', - ' class << self', - ' def baz', - ' 42', - ' end', - ' end', - 'end', - ].join("\n") - File.open "#{models_dir}/foo.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + module Foo + class << self + def baz + 42 + end + end + end + RUBY end - - expect(cop.offenses).to be_empty end it 'accepts methods defined in a nested class' do - source = [ - 'module Foo', - ' class Bar', - ' def qux', - ' end', - ' end', - 'end', - ].join("\n") - File.open "#{models_dir}/foo.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + module Foo + class Bar + def qux + end + end + end + RUBY end - - expect(cop.offenses).to be_empty end it 'accepts methods where the containing class uses an acronym' do - source = [ - 'module CSVFoo', - ' def baz', - ' 42', - ' end', - 'end', - ].join("\n") - File.open "#{models_dir}/csv_foo.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + module CSVFoo + def baz + 42 + end + end + RUBY end - - expect(cop.offenses).to be_empty end it 'ignores rake tasks' do - source = [ - 'module Hello', - ' def baz', # error is here if this file is named bar.rb - ' 42', - ' end', - 'end', - ].join("\n") - File.open "#{models_dir}/bar.rake", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + module Hello + def baz + 42 + end + end + RUBY end - - expect(cop.offenses).to be_empty end end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/no_timeout_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/no_timeout_spec.rb index 410205d..730a6d3 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/no_timeout_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/no_timeout_spec.rb @@ -1,30 +1,24 @@ -describe RuboCop::Cop::Airbnb::NoTimeout do - subject(:cop) { described_class.new } - +describe RuboCop::Cop::Airbnb::NoTimeout, :config do context 'send' do it 'rejects Timeout.timeout' do - source = [ - 'def some_method(a)', - ' Timeout.timeout(5) do', - ' some_other_method(a)', - ' end', - 'end', - ].join("\n") - inspect_source(source) - expect(cop.offenses.size).to eql(1) - expect(cop.offenses.first.message).to start_with('Do not use Timeout.timeout') + expect_offense(<<~RUBY) + def some_method(a) + Timeout.timeout(5) do + ^^^^^^^^^^^^^^^^^^ Do not use Timeout.timeout. [...] + some_other_method(a) + end + end + RUBY end it 'accepts foo.timeout' do - source = [ - 'def some_method(a)', - ' foo.timeout do', - ' some_other_method(a)', - ' end', - 'end', - ].join("\n") - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + def some_method(a) + foo.timeout do + some_other_method(a) + end + end + RUBY end end end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/opt_arg_parameter_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/opt_arg_parameter_spec.rb index eedca28..bafb659 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/opt_arg_parameter_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/opt_arg_parameter_spec.rb @@ -1,103 +1,76 @@ -describe RuboCop::Cop::Airbnb::OptArgParameters do - subject(:cop) { described_class.new } - +describe RuboCop::Cop::Airbnb::OptArgParameters, :config do it 'allows method with no parameters' do - source = [ - 'def my_method', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + def my_method + end + RUBY end it 'allows method with one parameter' do - source = [ - 'def my_method(params)', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + def my_method(params) + end + RUBY end it 'allows method with one parameter with a default hash value' do - source = [ - 'def my_method(params = {})', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + def my_method(params = {}) + end + RUBY end it 'allows method named default parameters' do - source = [ - 'def my_method(a, b, c: 5, d: 6)', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + def my_method(a, b, c: 5, d: 6) + end + RUBY end it 'allows method with multiple parameter with a default hash value' do - source = [ - 'def my_method(a, b, c, params = {})', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + def my_method(a, b, c, params = {}) + end + RUBY end it 'allows method with default parameter before block parameter' do - source = [ - 'def my_method(a, b, c, params = {}, &block)', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + def my_method(a, b, c, params = {}, &block) + end + RUBY end it 'rejects method with a default parameter other than the last non-block parameter' do - source = [ - 'def my_method(a, b = nil, c, &block)', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + def my_method(a, b = nil, c, &block) + ^^^^^^^ Do not use default positional arguments. [...] + end + RUBY end it 'rejects method with a default parameter other than the last parameter' do - source = [ - 'def my_method(a, b = 5, c = nil, params = {})', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses.size).to eq(2) + expect_offense(<<~RUBY) + def my_method(a, b = 5, c = nil, params = {}) + ^^^^^ Do not use default positional arguments. [...] + ^^^^^^^ Do not use default positional arguments. [...] + end + RUBY end it 'rejects method where last parameter has a default value of nil' do - source = [ - 'def my_method(a, b, c, params = nil)', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + def my_method(a, b, c, params = nil) + ^^^^^^^^^^^^ Do not use default positional arguments. [...] + end + RUBY end it 'rejects method where last parameter has a default value of a constant' do - source = [ - 'def my_method(a, b, c, params = Constants::A_CONSTANT)', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + def my_method(a, b, c, params = Constants::A_CONSTANT) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not use default positional arguments. [...] + end + RUBY end end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/phrase_bundle_keys_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/phrase_bundle_keys_spec.rb index 0755404..6323d4f 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/phrase_bundle_keys_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/phrase_bundle_keys_spec.rb @@ -1,8 +1,6 @@ -describe RuboCop::Cop::Airbnb::PhraseBundleKeys do - subject(:cop) { described_class.new } - +describe RuboCop::Cop::Airbnb::PhraseBundleKeys, :config do it 'generates offenses for mismatched keys in PhraseBundle classes' do - source = < t( + ^^^^^^^^^^^^^^^ Phrase bundle keys should match their translation keys. "my_real_translation_key", default: 'Does not matter', ), @@ -18,17 +17,10 @@ def phrases end end EOS - - inspect_source(source) - expect(cop.offenses.size).to eq(1) - expect(cop.offenses.map(&:line).sort).to eq([7]) - expect(cop.messages).to eq( - ['Phrase bundle keys should match their translation keys.'] - ) end it 'does not generate offenses for matching keys in PhraseBundle classes' do - source = < "Bob"})', - ].join("\n") - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + Users.where({:name => "Bob"}) + RUBY end it "allows where statement that's a flat string" do - source = 'Users.where("age = 24")' - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses('Users.where("age = 24")') end it "allows a multiline where statement" do - source = "Users.where(\"age = 24 OR \" \\\n\"age = 25\")" - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses("Users.where(\"age = 24 OR \" \\\n\"age = 25\")") end it "allows interpolation in subsequent arguments to where" do - source = 'Users.where("name like ?", "%#{name}%")' - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses('Users.where("name like ?", "%#{name}%")') end it "disallows interpolation in where statements" do - source = 'Users.where("name = #{username}")' - - inspect_source(source) - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + Users.where("name = \#{username}") + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Passing a string computed by interpolation or addition to an ActiveRecord method is likely to lead to SQL injection. [...] + RUBY end it "disallows addition in where statements" do - source = 'Users.where("name = " + username)' - - inspect_source(source) - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + Users.where("name = " + username) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Passing a string computed by interpolation or addition to an ActiveRecord method is likely to lead to SQL injection. [...] + RUBY end it "disallows interpolation in order statements" do - source = 'Users.where("age = 24").order("name #{sortorder}")' - - inspect_source(source) - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + Users.where("age = 24").order("name \#{sortorder}") + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Passing a string computed by interpolation or addition to an ActiveRecord method is likely to lead to SQL injection. [...] + RUBY end end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/rspec_describe_or_context_under_namespace_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/rspec_describe_or_context_under_namespace_spec.rb index 776cfd1..febe2b8 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/rspec_describe_or_context_under_namespace_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/rspec_describe_or_context_under_namespace_spec.rb @@ -1,6 +1,4 @@ -describe RuboCop::Cop::Airbnb::RspecDescribeOrContextUnderNamespace do - subject(:cop) { described_class.new } - +describe RuboCop::Cop::Airbnb::RspecDescribeOrContextUnderNamespace, :config do let(:tmpdir) { Dir.mktmpdir } let(:models_spec_dir) do FileUtils.mkdir_p("#{tmpdir}/spec/models").first @@ -13,125 +11,73 @@ shared_examples 'rspec namespacing rule' do context 'under a namespace' do it 'rejects without change message when argument is a string' do - source = [ - 'module Foo', - " #{method} 'SomeClass' do", - ' it "passes" do', - ' expect(true).to be_true', - ' end', - ' end', - 'end', - ].join("\n") - FileUtils.mkdir_p "#{models_spec_dir}/foo" File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| - inspect_source(source, file) + expect_offense(<<~RUBY, file) + module Foo + ^^^^^^^^^^ Declaring a `module` in a spec can break autoloading because subsequent references to it will not cause it to be loaded from the app. This could cause flaky tests. Remove `module Foo` and fix `Foo::CONST` and `Foo.method` calls accordingly. + #{method} 'SomeClass' do + it "passes" do + expect(true).to be_true + end + end + end + RUBY end - - expect(cop.offenses.size).to eql(1) - expect(cop.offenses.first.message). - to include('Declaring a `module` in a spec can break autoloading because ') - - expect(cop.offenses.first.message). - not_to include( - "Change `#{method} SomeClass do` to `#{method} Foo::SomeClass do`" - ) - - expect(cop.offenses.first.message). - to include('Remove `module Foo` and fix `Foo::CONST` and `Foo.method` calls') end it 'rejects with change message when argument is a constant' do - source = [ - 'module Foo', - " #{method} SomeClass do", - ' it "passes" do', - ' expect(true).to be_true', - ' end', - ' end', - 'end', - ].join("\n") - FileUtils.mkdir_p "#{models_spec_dir}/foo" File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| - inspect_source(source, file) + expect_offense(<<~RUBY, file) + module Foo + ^^^^^^^^^^ Declaring a `module` in a spec can break autoloading because subsequent references to it will not cause it to be loaded from the app. This could cause flaky tests. Change `#{method} SomeClass do` to `#{method} Foo::SomeClass do`. Remove `module Foo` and fix `Foo::CONST` and `Foo.method` calls accordingly. + #{method} SomeClass do + it "passes" do + expect(true).to be_true + end + end + end + RUBY end - - expect(cop.offenses.size).to eql(1) - expect(cop.offenses.first.message). - to include('Declaring a `module` in a spec can break autoloading because ') - - expect(cop.offenses.first.message). - to include( - "Change `#{method} SomeClass do` to `#{method} Foo::SomeClass do`" - ) - - expect(cop.offenses.first.message). - to include('Remove `module Foo` and fix `Foo::CONST` and `Foo.method` calls') end it 'rejects when within a nested block' do - source = [ - 'module Foo', - ' 1.times do', - " #{method} SomeClass do", - ' it "passes" do', - ' expect(true).to be_true', - ' end', - ' end', - ' end', - 'end', - ].join("\n") - FileUtils.mkdir_p "#{models_spec_dir}/foo" File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| - inspect_source(source, file) + expect_offense(<<~RUBY, file) + module Foo + ^^^^^^^^^^ Declaring a `module` in a spec can break autoloading because subsequent references to it will not cause it to be loaded from the app. This could cause flaky tests. Change `#{method} SomeClass do` to `#{method} Foo::SomeClass do`. Remove `module Foo` and fix `Foo::CONST` and `Foo.method` calls accordingly. + 1.times do + #{method} SomeClass do + it "passes" do + expect(true).to be_true + end + end + end + end + RUBY end - - expect(cop.offenses.size).to eql(1) - expect(cop.offenses.first.message). - to include('Declaring a `module` in a spec can break autoloading because ') - - expect(cop.offenses.first.message). - to include( - "Change `#{method} SomeClass do` to `#{method} Foo::SomeClass do`" - ) - - expect(cop.offenses.first.message). - to include('Remove `module Foo` and fix `Foo::CONST` and `Foo.method` calls') end it 'rejects when a block is executed prior' do - source = [ - 'module Foo', - ' [1,2,3].each do', - ' something', - ' end', - '', - " #{method} SomeClass do", - ' it "passes" do', - ' expect(true).to be_true', - ' end', - ' end', - 'end', - ].join("\n") - FileUtils.mkdir_p "#{models_spec_dir}/foo" File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| - inspect_source(source, file) + expect_offense(<<~RUBY, file) + module Foo + ^^^^^^^^^^ Declaring a `module` in a spec can break autoloading because subsequent references to it will not cause it to be loaded from the app. This could cause flaky tests. Change `#{method} SomeClass do` to `#{method} Foo::SomeClass do`. Remove `module Foo` and fix `Foo::CONST` and `Foo.method` calls accordingly. + [1,2,3].each do + something + end + + #{method} SomeClass do + it "passes" do + expect(true).to be_true + end + end + end + RUBY end - - expect(cop.offenses.size).to eql(1) - expect(cop.offenses.first.message). - to include('Declaring a `module` in a spec can break autoloading because ') - - expect(cop.offenses.first.message). - to include( - "Change `#{method} SomeClass do` to `#{method} Foo::SomeClass do`" - ) - - expect(cop.offenses.first.message). - to include('Remove `module Foo` and fix `Foo::CONST` and `Foo.method` calls') end end end @@ -161,124 +107,100 @@ end it 'accepts if file is not a spec file' do - source = [ - 'module Foo', - ' RSpec.describe "SomeClass" do', - ' it "passes" do', - ' expect(true).to be_true', - ' end', - ' end', - 'end', - ].join("\n") - FileUtils.mkdir_p "#{models_spec_dir}/foo" File.open "#{models_spec_dir}/foo/some_class.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + module Foo + RSpec.describe "SomeClass" do + it "passes" do + expect(true).to be_true + end + end + end + RUBY end - - expect(cop.offenses).to be_empty end it 'accepts if not under a module' do - source = [ - 'RSpec.describe "SomeClass" do', - ' it "passes" do', - ' expect(true).to be_true', - ' end', - 'end', - ].join("\n") - FileUtils.mkdir_p "#{models_spec_dir}/foo" File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + RSpec.describe "SomeClass" do + it "passes" do + expect(true).to be_true + end + end + RUBY end - - expect(cop.offenses).to be_empty end it 'accepts if not describe or context' do - source = [ - 'module Foo', - ' not_describe_or_context "SomeClass" do', - ' it "passes" do', - ' expect(true).to be_true', - ' end', - ' end', - 'end', - ].join("\n") - FileUtils.mkdir_p "#{models_spec_dir}/foo" File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + module Foo + not_describe_or_context "SomeClass" do + it "passes" do + expect(true).to be_true + end + end + end + RUBY end - - expect(cop.offenses).to be_empty end it 'accepts an empty module' do - source = [ - 'RSpec.describe "SomeClass" do', - ' module Bar; end', - '', - ' it "passes" do', - ' expect(true).to be_true', - ' end', - 'end', - ].join("\n") - FileUtils.mkdir_p "#{models_spec_dir}/foo" File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| - inspect_source(source, file) - end + expect_no_offenses(<<~RUBY, file) + RSpec.describe "SomeClass" do + module Bar; end - expect(cop.offenses).to be_empty + it "passes" do + expect(true).to be_true + end + end + RUBY + end end it 'accepts a module with code' do - source = [ - 'RSpec.describe "SomeClass" do', - ' module Bar', - ' def self.foo', - ' end', - ' foo', - ' end', - '', - ' it "passes" do', - ' expect(true).to be_true', - ' end', - 'end', - ].join("\n") - FileUtils.mkdir_p "#{models_spec_dir}/foo" File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + RSpec.describe "SomeClass" do + module Bar + def self.foo + end + foo + end + + it "passes" do + expect(true).to be_true + end + end + RUBY end - - expect(cop.offenses).to be_empty end it 'accepts when describe or context to be called when class or module is not RSpec' do - source = [ - 'module Foo', - ' Bar.describe "SomeClass" do', - ' it "passes" do', - ' expect(true).to be_true', - ' end', - ' end', - '', - ' Bar.context "SomeClass" do', - ' it "passes" do', - ' expect(true).to be_true', - ' end', - ' end', - 'end', - ].join("\n") - FileUtils.mkdir_p "#{models_spec_dir}/foo" File.open "#{models_spec_dir}/foo/some_class_spec.rb", "w" do |file| - inspect_source(source, file) + expect_no_offenses(<<~RUBY, file) + module Foo + Bar.describe "SomeClass" do + it "passes" do + expect(true).to be_true + end + end + + Bar.context "SomeClass" do + it "passes" do + expect(true).to be_true + end + end + end + RUBY end - - expect(cop.offenses).to be_empty end end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/rspec_environment_modification_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/rspec_environment_modification_spec.rb index 65f82e8..ac27ff2 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/rspec_environment_modification_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/rspec_environment_modification_spec.rb @@ -1,64 +1,54 @@ -describe RuboCop::Cop::Airbnb::RspecEnvironmentModification do - subject(:cop) { described_class.new } - +describe RuboCop::Cop::Airbnb::RspecEnvironmentModification, :config do before(:each) do allow(cop).to receive(:is_spec_file?).and_return(true) end it 'does not allow assignment of Rails.env' do - source = [ - 'Rails.env = :production', - ].join("\n") - inspect_source(source) - expect(cop.offenses.size).to eql(1) + expect_offense(<<~RUBY) + Rails.env = :production + ^^^^^^^^^^^^^^^^^^^^^^^ Do not stub or set Rails.env in specs. [...] + RUBY end it 'allows assignment of Rails.env when not in spec' do allow(cop).to receive(:is_spec_file?).and_return(false) - source = [ - 'Rails.env = :production', - ].join("\n") - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + Rails.env = :production + RUBY end it 'rejects allow style stubbing of Rails.env' do - source = [ - 'def some_method(a)', - ' allow(Rails.env).to receive(:production?)', - 'end', - ].join("\n") - inspect_source(source) - expect(cop.offenses.size).to eql(1) + expect_offense(<<~RUBY) + def some_method(a) + allow(Rails.env).to receive(:production?) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not stub or set Rails.env in specs. [...] + end + RUBY end it 'rejects expect style stubbing of Rails.env' do - source = [ - 'def some_method(a)', - ' expect(Rails.env).to receive(:production?)', - 'end', - ].join("\n") - inspect_source(source) - expect(cop.offenses.size).to eql(1) + expect_offense(<<~RUBY) + def some_method(a) + expect(Rails.env).to receive(:production?) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not stub or set Rails.env in specs. [...] + end + RUBY end it 'rejects .stub stubbing of Rails.env' do - source = [ - 'def some_method(a)', - ' Rails.env.stub(:production)', - 'end', - ].join("\n") - inspect_source(source) - expect(cop.offenses.size).to eql(1) + expect_offense(<<~RUBY) + def some_method(a) + Rails.env.stub(:production) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not stub or set Rails.env in specs. [...] + end + RUBY end it 'allows stub_env' do - source = [ - 'def some_method(a)', - ' stub_env(:production)', - 'end', - ].join("\n") - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + def some_method(a) + stub_env(:production) + end + RUBY end end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/simple_modifier_conditional_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/simple_modifier_conditional_spec.rb index bd44acd..c9be038 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/simple_modifier_conditional_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/simple_modifier_conditional_spec.rb @@ -1,122 +1,98 @@ -describe RuboCop::Cop::Airbnb::SimpleModifierConditional do - subject(:cop) { described_class.new } - +describe RuboCop::Cop::Airbnb::SimpleModifierConditional, :config do context 'multiple conditionals' do it 'rejects with modifier if with multiple conditionals' do - source = [ - 'return true if some_method == 0 || another_method', - ].join("\n") - - inspect_source(source) - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + return true if some_method == 0 || another_method + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Modifier if/unless usage is okay when [...] + RUBY end it 'rejects with modifier unless with multiple conditionals' do - source = [ - 'return true unless true && false', - ].join("\n") - - inspect_source(source) - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + return true unless true && false + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Modifier if/unless usage is okay when [...] + RUBY end it 'allows with modifier if operator conditional' do - source = [ - 'return true if some_method == 0', - ].join("\n") - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + return true if some_method == 0 + RUBY end it 'allows with modifier if with single conditional' do - source = [ - 'return true if some_method == 0', - 'return true if another_method', - ].join("\n") - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + return true if some_method == 0 + return true if another_method + RUBY end it 'allows with modifier if and unless with single conditional ' do - source = [ - 'return true if some_method', - 'return true unless another_method > 5', - ].join("\n") - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + return true if some_method + return true unless another_method > 5 + RUBY end it 'allows multiple conditionals in block form' do - source = [ - 'if some_method == 0 && another_method > 5 || true || false', - ' return true', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + if some_method == 0 && another_method > 5 || true || false + return true + end + RUBY end end context 'multiple lines' do it 'rejects modifier conditionals that span multiple lines' do - source = [ - 'return true if true ||', - ' false', - 'return true unless true ||', - ' false', - ].join("\n") - - inspect_source(source) - expect(cop.offenses.size).to eq(2) + expect_offense(<<~RUBY) + return true if true || + ^^^^^^^^^^^^^^^^^^^^^^ Modifier if/unless usage is okay when [...] + false + return true unless true || + ^^^^^^^^^^^^^^^^^^^^^^^^^^ Modifier if/unless usage is okay when [...] + false + RUBY end it 'rejects with modifier if with method that spans multiple lines' do - source = [ - 'return true if some_method(param1,', - ' param2,', - ' param3)', - 'return true unless some_method(param1,', - ' param2,', - ' param3)', - ].join("\n") - - inspect_source(source) - expect(cop.offenses.size).to eq(2) + expect_offense(<<~RUBY) + return true if some_method(param1, + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Modifier if/unless usage is okay when [...] + param2, + param3) + return true unless some_method(param1, + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Modifier if/unless usage is okay when [...] + param2, + param3) + RUBY end it 'rejects inline if/unless after a multiline statement' do - source = [ - 'return some_method(', - ' param1,', - ' param2,', - ' param3', - ') if another_method == 0', - 'return some_method(', - ' param1,', - ' param2,', - ' param3', - ') unless another_method == 0', - ].join("\n") - - inspect_source(source) - expect(cop.offenses.size).to eq(2) + expect_offense(<<~RUBY) + return some_method( + ^^^^^^^^^^^^^^^^^^^ Modifier if/unless usage is okay when [...] + param1, + param2, + param3 + ) if another_method == 0 + return some_method( + ^^^^^^^^^^^^^^^^^^^ Modifier if/unless usage is okay when [...] + param1, + param2, + param3 + ) unless another_method == 0 + RUBY end it 'allows multline conditionals in block form' do - source = [ - 'if some_method(param1,', - ' param2,', - ' parma3)', - ' return true', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + if some_method(param1, + param2, + parma3) + return true + end + RUBY end end end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/simple_unless_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/simple_unless_spec.rb index ae1f461..f22fd3f 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/simple_unless_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/simple_unless_spec.rb @@ -1,36 +1,26 @@ -describe RuboCop::Cop::Airbnb::SimpleUnless do - subject(:cop) { described_class.new } - +describe RuboCop::Cop::Airbnb::SimpleUnless, :config do it 'rejects unless with multiple conditionals' do - source = [ - 'unless boolean_condition || another_method', - ' return true', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + unless boolean_condition || another_method + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unless usage is okay when there is only one conditional + return true + end + RUBY end it 'allows if with multiple conditionals' do - source = [ - 'if boolean_condition || another_method', - ' return true', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + if boolean_condition || another_method + return true + end + RUBY end it 'allows with modifier if operator conditional' do - source = [ - 'unless boolean_condition', - ' return true', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + unless boolean_condition + return true + end + RUBY end end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/spec_constant_assignment_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/spec_constant_assignment_spec.rb index 6dc44f4..582a8eb 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/spec_constant_assignment_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/spec_constant_assignment_spec.rb @@ -1,80 +1,62 @@ -describe RuboCop::Cop::Airbnb::SpecConstantAssignment do - subject(:cop) { described_class.new } - +describe RuboCop::Cop::Airbnb::SpecConstantAssignment, :config do it 'rejects constant definition inside of a describe block' do - source = [ - 'describe Someclass do', - ' CONSTANT = 5', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + describe Someclass do + CONSTANT = 5 + ^^^^^^^^^^^^ Defining constants inside of specs can cause spurious behavior. [...] + end + RUBY end it 'allows constant defined inside of a module' do - source = [ - 'module Someclass', - ' CONSTANT = 5', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + module Someclass + CONSTANT = 5 + end + RUBY end - it 'allows constant defined in global space' do - source = [ - 'CONSTANT = 5', - ].join("\n") - - inspect_source(source) - expect(cop.offenses.size).to eq(1) + it 'rejects constant defined in global space' do + expect_offense(<<~RUBY) + CONSTANT = 5 + ^^^^^^^^^^^^ Defining constants inside of specs can cause spurious behavior. [...] + RUBY end it 'rejects constant assignment inside a before block' do - source = [ - 'describe Someclass do', - ' before { CONSTANT = 5 }', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + describe Someclass do + before { CONSTANT = 5 } + ^^^^^^^^^^^^ Defining constants inside of specs can cause spurious behavior. [...] + end + RUBY end it 'rejects namespaced constant assignment inside a before block' do - source = [ - 'describe Someclass do', - ' before { MyModule::CONSTANT = 5 }', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + describe Someclass do + before { MyModule::CONSTANT = 5 } + ^^^^^^^^^^^^^^^^^^^^^^ Defining constants inside of specs can cause spurious behavior. [...] + end + RUBY end it 'rejects constant assignment inside it block' do - source = [ - 'describe Someclass do', - ' it "tests stuff" do', - ' CONSTANT = 5', - ' end', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses.size).to eq(1) + expect_offense(<<~RUBY) + describe Someclass do + it "tests stuff" do + CONSTANT = 5 + ^^^^^^^^^^^^ Defining constants inside of specs can cause spurious behavior. [...] + end + end + RUBY end it 'allows let statements that do not assign constants' do - source = [ - 'describe Someclass do', - ' let(:constant) { 5 }', - 'end', - ].join("\n") - - inspect_source(source) - expect(cop.offenses).to be_empty + expect_no_offenses(<<~RUBY) + describe Someclass do + let(:constant) { 5 } + end + RUBY end end diff --git a/rubocop-airbnb/spec/rubocop/cop/airbnb/unsafe_yaml_marshal_spec.rb b/rubocop-airbnb/spec/rubocop/cop/airbnb/unsafe_yaml_marshal_spec.rb index 16190af..33737df 100644 --- a/rubocop-airbnb/spec/rubocop/cop/airbnb/unsafe_yaml_marshal_spec.rb +++ b/rubocop-airbnb/spec/rubocop/cop/airbnb/unsafe_yaml_marshal_spec.rb @@ -1,50 +1,38 @@ -describe RuboCop::Cop::Airbnb::UnsafeYamlMarshal do - subject(:cop) { described_class.new } - +describe RuboCop::Cop::Airbnb::UnsafeYamlMarshal, :config do context 'send' do it 'rejects YAML.load' do - source = [ - 'def some_method(a)', - ' YAML.load(a)', - 'end', - ].join("\n") - inspect_source(source) - expect(cop.offenses.size).to eql(1) - expect(cop.offenses.first.message).to match(/`safe_load`, `parse`, `parse_file`/) + expect_offense(<<~RUBY) + def some_method(a) + YAML.load(a) + ^^^^^^^^^^^^ Using `YAML.load` on untrusted input [...] + end + RUBY end it 'rejects Psych.load' do - source = [ - 'def some_method(a)', - ' Psych.load(a)', - 'end', - ].join("\n") - inspect_source(source) - expect(cop.offenses.size).to eql(1) - expect(cop.offenses.first.message).to match(/`safe_load`, `parse`, `parse_file`/) + expect_offense(<<~RUBY) + def some_method(a) + Psych.load(a) + ^^^^^^^^^^^^^ Using `Psych.load` on untrusted input [...] + end + RUBY end it 'accepts YAML.safe_load' do - source = [ - 'def some_method(a)', - ' YAML.safe_load(a)', - 'end', - ].join("\n") - inspect_source(source) - expect(cop.offenses.size).to eql(0) + expect_no_offenses(<<~RUBY) + def some_method(a) + YAML.safe_load(a) + end + RUBY end it 'rejects on Marshal.load' do - source = [ - 'def some_method(a)', - ' Marshal.load(a)', - 'end', - ].join("\n") - inspect_source(source) - expect(cop.offenses.size).to eql(1) - expect(cop.offenses.first.message).to match( - /`Marshal.load` on untrusted input can lead to remote code execution/ - ) + expect_offense(<<~RUBY) + def some_method(a) + Marshal.load(a) + ^^^^^^^^^^^^^^^ Using `Marshal.load` on untrusted input can lead to remote code execution. [...] + end + RUBY end end end diff --git a/rubocop-airbnb/spec/spec_helper.rb b/rubocop-airbnb/spec/spec_helper.rb index 3be6b2b..f8d6dca 100644 --- a/rubocop-airbnb/spec/spec_helper.rb +++ b/rubocop-airbnb/spec/spec_helper.rb @@ -13,6 +13,8 @@ module SpecHelper Dir.glob(spec_helper_glob).map(&method(:require)) RSpec.configure do |config| + config.include RuboCop::RSpec::ExpectOffense + config.order = :random # Define spec metadata for all rspec cop spec files