diff --git a/.rubocop.yml b/.rubocop.yml index c4a0469f9..23a3fcbb4 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,7 +8,7 @@ require: AllCops: DisplayCopNames: true TargetRubyVersion: 2.4 - NewCops: enable + NewCops: disable Exclude: - 'vendor/**/*' - 'spec/fixtures/**/*' diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index ecde48e83..2941286be 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -10,3 +10,7 @@ # Configuration parameters: CountComments, CountAsOne. Metrics/ClassLength: Max: 106 + +RSpec/ExampleLength: + Exclude: + - spec/rubocop/cop/rspec/factory_bot/attribute_defined_statically_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 509b98c7f..4ff738a46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,21 @@ -# Change log +# Changelog ## Master (Unreleased) +## 2.0.0 (2020-11-06) + +* Remove deprecated class `::RuboCop::Cop::RSpec::Cop`. ([@bquorning][]) +* Retire `RSpec/InvalidPredicateMatcher` cop. ([@pirj][]) +* Remove the code responsible for filtering files to inspect. ([@pirj][]) +* Make RSpec language elements configurable. ([@sl4vr][]) +* Remove `CustomIncludeMethods` `RSpec/EmptyExampleGroup` option in favour of the new RSpec DSL configuration. ([@pirj][]) +* Enabled pending cop (`RSpec/StubbedMock`). ([@pirj][]) + +## 2.0.0.pre (2020-10-22) + +* Update RuboCop dependency to v1.0.0. ([@bquorning][]) +* Change namespace of several cops (`Capybara/*` -> `RSpec/Capybara/*`, `FactoryBot/*` -> `RSpec/FactoryBot/*`, `Rails/*` -> `RSpec/Rails/*`). ([@pirj][], [@bquorning][]) + ## 1.44.1 (2020-10-20) * Relax `rubocop-ast` version constraint. ([@PhilCoggins][]) @@ -571,3 +585,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features. [@koic]: https://github.com/koic [@Rafix02]: https://github.com/Rafix02 [@PhilCoggins]: https://github.com/PhilCoggins +[@sl4vr]: https://github.com/sl4vr diff --git a/README.md b/README.md index fa1dc5991..b92838e1d 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,10 @@ or if you use bundler put this in your `Gemfile` gem 'rubocop-rspec', require: false ``` +### Upgrading to RuboCop RSpec v2.x + +Read all the details in our [Upgrade to Version 2.x](https://docs.rubocop.org/rubocop-rspec/2.0/upgrade_to_version_2.html) document. + ## Usage You need to tell RuboCop to load the RSpec extension. There are three diff --git a/config/default.yml b/config/default.yml index 7f7daf15d..a7be06200 100644 --- a/config/default.yml +++ b/config/default.yml @@ -1,14 +1,85 @@ --- -AllCops: - RSpec: - Patterns: - - _spec.rb - - "(?:^|/)spec/" - RSpec/FactoryBot: - Patterns: - - spec/factories.rb - - spec/factories/**/*.rb - - features/support/factories/**/*.rb +RSpec: + Include: + - "**/*_spec.rb" + - "**/spec/**/*" + Language: + ExampleGroups: + Regular: + - describe + - context + - feature + - example_group + Skipped: + - xdescribe + - xcontext + - xfeature + Focused: + - fdescribe + - fcontext + - ffeature + Examples: + Regular: + - it + - specify + - example + - scenario + - its + Focused: + - fit + - fspecify + - fexample + - fscenario + - focus + Skipped: + - xit + - xspecify + - xexample + - xscenario + - skip + Pending: + - pending + Expectations: + - expect + - is_expected + - expect_any_instance_of + Helpers: + - let + - let! + Hooks: + - prepend_before + - before + - append_before + - around + - prepend_after + - after + - append_after + HookScopes: + - each + - example + - context + - all + - suite + Includes: + Examples: + - it_behaves_like + - it_should_behave_like + - include_examples + Context: + - include_context + Runners: + - to + - to_not + - not_to + SharedGroups: + Examples: + - shared_examples + - shared_examples_for + Context: + - shared_context + Subjects: + - subject + - subject! RSpec/AlignLeftLetBrace: Description: Checks that left braces for adjacent single line lets are aligned. @@ -136,8 +207,8 @@ RSpec/Dialect: RSpec/EmptyExampleGroup: Description: Checks if an example group does not include any tests. Enabled: true - CustomIncludeMethods: [] VersionAdded: '1.7' + VersionChanged: '2.0' StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyExampleGroup RSpec/EmptyHook: @@ -241,6 +312,9 @@ RSpec/ExpectOutput: RSpec/FilePath: Description: Checks that spec file paths are consistent and well-formed. Enabled: true + Include: + - "**/*_spec*rb*" + - "**/spec/**/*" CustomTransform: RuboCop: rubocop RSpec: rspec @@ -315,12 +389,6 @@ RSpec/InstanceVariable: VersionChanged: '1.7' StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/InstanceVariable -RSpec/InvalidPredicateMatcher: - Description: Checks invalid usage for predicate matcher. - Enabled: true - VersionAdded: '1.16' - StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/InvalidPredicateMatcher - RSpec/ItBehavesLike: Description: Checks that only one `it_behaves_like` style is used. Enabled: true @@ -561,7 +629,7 @@ RSpec/SingleArgumentMessageChain: RSpec/StubbedMock: Description: Checks that message expectations do not have a configured response. - Enabled: pending + Enabled: true VersionAdded: '1.44' StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/StubbedMock @@ -620,49 +688,68 @@ RSpec/Yield: VersionAdded: '1.32' StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Yield -Capybara/CurrentPathExpectation: +RSpec/Capybara/CurrentPathExpectation: Description: Checks that no expectations are set on Capybara's `current_path`. Enabled: true VersionAdded: '1.18' + VersionChanged: '2.0' StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/CurrentPathExpectation -Capybara/FeatureMethods: +RSpec/Capybara/FeatureMethods: Description: Checks for consistent method usage in feature specs. Enabled: true EnabledMethods: [] VersionAdded: '1.17' - VersionChanged: '1.25' + VersionChanged: '2.0' StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/FeatureMethods -Capybara/VisibilityMatcher: +RSpec/Capybara/VisibilityMatcher: Description: Checks for boolean visibility in capybara finders. Enabled: true VersionAdded: '1.39' + VersionChanged: '2.0' StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/VisibilityMatcher -FactoryBot/AttributeDefinedStatically: +RSpec/FactoryBot/AttributeDefinedStatically: Description: Always declare attribute values as blocks. Enabled: true + Include: + - spec/factories.rb + - spec/factories/**/*.rb + - features/support/factories/**/*.rb VersionAdded: '1.28' + VersionChanged: '2.0' StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/AttributeDefinedStatically -FactoryBot/CreateList: +RSpec/FactoryBot/CreateList: Description: Checks for create_list usage. Enabled: true + Include: + - "**/*_spec.rb" + - "**/spec/**/*" + - spec/factories.rb + - spec/factories/**/*.rb + - features/support/factories/**/*.rb EnforcedStyle: create_list SupportedStyles: - create_list - n_times VersionAdded: '1.25' + VersionChanged: '2.0' StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/CreateList -FactoryBot/FactoryClassName: +RSpec/FactoryBot/FactoryClassName: Description: Use string value when setting the class attribute explicitly. Enabled: true + Include: + - spec/factories.rb + - spec/factories/**/*.rb + - features/support/factories/**/*.rb VersionAdded: '1.37' + VersionChanged: '2.0' StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/FactoryClassName -Rails/HttpStatus: +RSpec/Rails/HttpStatus: Description: Enforces use of symbolic or numeric value to describe HTTP status. Enabled: true EnforcedStyle: symbolic @@ -670,4 +757,5 @@ Rails/HttpStatus: - numeric - symbolic VersionAdded: '1.23' + VersionChanged: '2.0' StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HttpStatus diff --git a/docs/antora.yml b/docs/antora.yml index 432fb650b..a62eefb72 100644 --- a/docs/antora.yml +++ b/docs/antora.yml @@ -1,5 +1,5 @@ name: rubocop-rspec title: RuboCop RSpec -version: master +version: '2.0' nav: - modules/ROOT/nav.adoc diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 39838056f..119cc9cb5 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -2,6 +2,7 @@ * xref:installation.adoc[Installation] * xref:usage.adoc[Usage] * xref:cops.adoc[Cops] +* xref:upgrade_to_version_2.adoc[Upgrade to 2.x] * Cops Documentation ** xref:cops_capybara.adoc[Capybara] ** xref:cops_factorybot.adoc[FactoryBot] diff --git a/docs/modules/ROOT/pages/cops.adoc b/docs/modules/ROOT/pages/cops.adoc index 24d8e9ae9..4330d141f 100644 --- a/docs/modules/ROOT/pages/cops.adoc +++ b/docs/modules/ROOT/pages/cops.adoc @@ -1,17 +1,5 @@ // START_COP_LIST -=== Department xref:cops_capybara.adoc[Capybara] - -* xref:cops_capybara.adoc#capybaracurrentpathexpectation[Capybara/CurrentPathExpectation] -* xref:cops_capybara.adoc#capybarafeaturemethods[Capybara/FeatureMethods] -* xref:cops_capybara.adoc#capybaravisibilitymatcher[Capybara/VisibilityMatcher] - -=== Department xref:cops_factorybot.adoc[FactoryBot] - -* xref:cops_factorybot.adoc#factorybotattributedefinedstatically[FactoryBot/AttributeDefinedStatically] -* xref:cops_factorybot.adoc#factorybotcreatelist[FactoryBot/CreateList] -* xref:cops_factorybot.adoc#factorybotfactoryclassname[FactoryBot/FactoryClassName] - === Department xref:cops_rspec.adoc[RSpec] * xref:cops_rspec.adoc#rspecalignleftletbrace[RSpec/AlignLeftLetBrace] @@ -52,7 +40,6 @@ * xref:cops_rspec.adoc#rspecimplicitsubject[RSpec/ImplicitSubject] * xref:cops_rspec.adoc#rspecinstancespy[RSpec/InstanceSpy] * xref:cops_rspec.adoc#rspecinstancevariable[RSpec/InstanceVariable] -* xref:cops_rspec.adoc#rspecinvalidpredicatematcher[RSpec/InvalidPredicateMatcher] * xref:cops_rspec.adoc#rspecitbehaveslike[RSpec/ItBehavesLike] * xref:cops_rspec.adoc#rspeciteratedexpectation[RSpec/IteratedExpectation] * xref:cops_rspec.adoc#rspecleadingsubject[RSpec/LeadingSubject] @@ -95,8 +82,20 @@ * xref:cops_rspec.adoc#rspecvoidexpect[RSpec/VoidExpect] * xref:cops_rspec.adoc#rspecyield[RSpec/Yield] -=== Department xref:cops_rails.adoc[Rails] +=== Department xref:cops_rspec/capybara.adoc[RSpec/Capybara] + +* xref:cops_rspec/capybara.adoc#rspeccapybara/currentpathexpectation[RSpec/Capybara/CurrentPathExpectation] +* xref:cops_rspec/capybara.adoc#rspeccapybara/featuremethods[RSpec/Capybara/FeatureMethods] +* xref:cops_rspec/capybara.adoc#rspeccapybara/visibilitymatcher[RSpec/Capybara/VisibilityMatcher] + +=== Department xref:cops_rspec/factorybot.adoc[RSpec/FactoryBot] + +* xref:cops_rspec/factorybot.adoc#rspecfactorybot/attributedefinedstatically[RSpec/FactoryBot/AttributeDefinedStatically] +* xref:cops_rspec/factorybot.adoc#rspecfactorybot/createlist[RSpec/FactoryBot/CreateList] +* xref:cops_rspec/factorybot.adoc#rspecfactorybot/factoryclassname[RSpec/FactoryBot/FactoryClassName] + +=== Department xref:cops_rspec/rails.adoc[RSpec/Rails] -* xref:cops_rails.adoc#railshttpstatus[Rails/HttpStatus] +* xref:cops_rspec/rails.adoc#rspecrails/httpstatus[RSpec/Rails/HttpStatus] // END_COP_LIST diff --git a/docs/modules/ROOT/pages/cops_rspec.adoc b/docs/modules/ROOT/pages/cops_rspec.adoc index f14ccce99..db84dfcb6 100644 --- a/docs/modules/ROOT/pages/cops_rspec.adoc +++ b/docs/modules/ROOT/pages/cops_rspec.adoc @@ -729,13 +729,11 @@ end | Yes | No | 1.7 -| - +| 2.0 |=== Checks if an example group does not include any tests. -This cop is configurable using the `CustomIncludeMethods` option - === Examples ==== usage @@ -772,43 +770,6 @@ describe Bacon do end ---- -==== configuration - -[source,ruby] ----- -# .rubocop.yml -# RSpec/EmptyExampleGroup: -# CustomIncludeMethods: -# - include_tests - -# spec_helper.rb -RSpec.configure do |config| - config.alias_it_behaves_like_to(:include_tests) -end - -# bacon_spec.rb -describe Bacon do - let(:bacon) { Bacon.new(chunkiness) } - let(:chunkiness) { false } - - context 'extra chunky' do # not flagged by rubocop - let(:chunkiness) { true } - - include_tests 'shared tests' - end -end ----- - -=== Configurable attributes - -|=== -| Name | Default value | Configurable values - -| CustomIncludeMethods -| `[]` -| Array -|=== - === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyExampleGroup @@ -1536,6 +1497,10 @@ my_class_spec.rb # describe MyClass, '#method' |=== | Name | Default value | Configurable values +| Include +| `+**/*_spec*rb*+`, `+**/spec/**/*+` +| Array + | CustomTransform | `{"RuboCop"=>"rubocop", "RSpec"=>"rspec"}` | @@ -1984,38 +1949,6 @@ end * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/InstanceVariable -== RSpec/InvalidPredicateMatcher - -|=== -| Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged - -| Enabled -| Yes -| No -| 1.16 -| - -|=== - -Checks invalid usage for predicate matcher. - -Predicate matcher does not need a question. -This cop checks an unnecessary question in predicate matcher. - -=== Examples - -[source,ruby] ----- -# bad -expect(foo).to be_something? - -# good -expect(foo).to be_something ----- - -=== References - -* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/InvalidPredicateMatcher - == RSpec/ItBehavesLike |=== @@ -3845,7 +3778,7 @@ allow(foo).to receive("bar.baz") |=== | Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged -| Pending +| Enabled | Yes | No | 1.44 diff --git a/docs/modules/ROOT/pages/cops_capybara.adoc b/docs/modules/ROOT/pages/cops_rspec/capybara.adoc similarity index 95% rename from docs/modules/ROOT/pages/cops_capybara.adoc rename to docs/modules/ROOT/pages/cops_rspec/capybara.adoc index 80b444ef0..cc18324c3 100644 --- a/docs/modules/ROOT/pages/cops_capybara.adoc +++ b/docs/modules/ROOT/pages/cops_rspec/capybara.adoc @@ -1,6 +1,6 @@ -= Capybara += RSpec/Capybara -== Capybara/CurrentPathExpectation +== RSpec/Capybara/CurrentPathExpectation |=== | Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged @@ -9,7 +9,7 @@ | Yes | Yes | 1.18 -| - +| 2.0 |=== Checks that no expectations are set on Capybara's `current_path`. @@ -39,7 +39,7 @@ expect(page).to have_current_path(/widgets/) * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/CurrentPathExpectation -== Capybara/FeatureMethods +== RSpec/Capybara/FeatureMethods |=== | Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged @@ -48,7 +48,7 @@ expect(page).to have_current_path(/widgets/) | Yes | Yes | 1.17 -| 1.25 +| 2.0 |=== Checks for consistent method usage in feature specs. @@ -106,7 +106,7 @@ end * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/FeatureMethods -== Capybara/VisibilityMatcher +== RSpec/Capybara/VisibilityMatcher |=== | Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged @@ -115,7 +115,7 @@ end | Yes | No | 1.39 -| - +| 2.0 |=== Checks for boolean visibility in capybara finders. diff --git a/docs/modules/ROOT/pages/cops_factorybot.adoc b/docs/modules/ROOT/pages/cops_rspec/factorybot.adoc similarity index 74% rename from docs/modules/ROOT/pages/cops_factorybot.adoc rename to docs/modules/ROOT/pages/cops_rspec/factorybot.adoc index 391785a22..97814e55b 100644 --- a/docs/modules/ROOT/pages/cops_factorybot.adoc +++ b/docs/modules/ROOT/pages/cops_rspec/factorybot.adoc @@ -1,6 +1,6 @@ -= FactoryBot += RSpec/FactoryBot -== FactoryBot/AttributeDefinedStatically +== RSpec/FactoryBot/AttributeDefinedStatically |=== | Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged @@ -9,7 +9,7 @@ | Yes | Yes | 1.28 -| - +| 2.0 |=== Always declare attribute values as blocks. @@ -37,11 +37,21 @@ count 1 count { 1 } ---- +=== Configurable attributes + +|=== +| Name | Default value | Configurable values + +| Include +| `spec/factories.rb`, `spec/factories/**/*.rb`, `features/support/factories/**/*.rb` +| Array +|=== + === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/AttributeDefinedStatically -== FactoryBot/CreateList +== RSpec/FactoryBot/CreateList |=== | Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged @@ -50,7 +60,7 @@ count { 1 } | Yes | Yes | 1.25 -| - +| 2.0 |=== Checks for create_list usage. @@ -89,6 +99,10 @@ create_list :user, 3 |=== | Name | Default value | Configurable values +| Include +| `+**/*_spec.rb+`, `+**/spec/**/*+`, `spec/factories.rb`, `spec/factories/**/*.rb`, `features/support/factories/**/*.rb` +| Array + | EnforcedStyle | `create_list` | `create_list`, `n_times` @@ -98,7 +112,7 @@ create_list :user, 3 * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/CreateList -== FactoryBot/FactoryClassName +== RSpec/FactoryBot/FactoryClassName |=== | Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged @@ -107,7 +121,7 @@ create_list :user, 3 | Yes | Yes | 1.37 -| - +| 2.0 |=== Use string value when setting the class attribute explicitly. @@ -130,6 +144,16 @@ factory :foo, class: 'Foo' do end ---- +=== Configurable attributes + +|=== +| Name | Default value | Configurable values + +| Include +| `spec/factories.rb`, `spec/factories/**/*.rb`, `features/support/factories/**/*.rb` +| Array +|=== + === References * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/FactoryClassName diff --git a/docs/modules/ROOT/pages/cops_rails.adoc b/docs/modules/ROOT/pages/cops_rspec/rails.adoc similarity index 96% rename from docs/modules/ROOT/pages/cops_rails.adoc rename to docs/modules/ROOT/pages/cops_rspec/rails.adoc index 50cb8a038..9ef62fe06 100644 --- a/docs/modules/ROOT/pages/cops_rails.adoc +++ b/docs/modules/ROOT/pages/cops_rspec/rails.adoc @@ -1,6 +1,6 @@ -= Rails += RSpec/Rails -== Rails/HttpStatus +== RSpec/Rails/HttpStatus |=== | Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged @@ -9,7 +9,7 @@ | Yes | Yes | 1.23 -| - +| 2.0 |=== Enforces use of symbolic or numeric value to describe HTTP status. diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc index 48e8d5972..3fbed391c 100644 --- a/docs/modules/ROOT/pages/index.adoc +++ b/docs/modules/ROOT/pages/index.adoc @@ -3,6 +3,12 @@ RSpec-specific analysis for your projects, as an extension to https://github.com/rubocop-hq/rubocop[RuboCop]. +RuboCop RSpec follows the https://docs.rubocop.org/rubocop/versioning.html[RuboCop versioning guide]. +In a nutshell, between major versions new cops are introduced in a special `pending` status. +That means that they won’t be run unless explicitly told otherwise. +RuboCop will warn on start that certain cops are neither explicitly enabled and disabled. +On a major version release, all `pending` cops are enabled. + == Project Goals * Enforce the guidelines and best practices outlined in the community https://rspec.rubystyle.guide[RSpec style guide] diff --git a/docs/modules/ROOT/pages/upgrade_to_version_2.adoc b/docs/modules/ROOT/pages/upgrade_to_version_2.adoc new file mode 100644 index 000000000..d4852610c --- /dev/null +++ b/docs/modules/ROOT/pages/upgrade_to_version_2.adoc @@ -0,0 +1,273 @@ += Upgrade to Version 2.x +:doctype: book + +== Configuration File Update + +In version 2.x: + + - `RSpec/InvalidPredicateMatcher` cop is removed + - `CustomIncludeMethods` configuration option for `RSpec/EmptyExampleGroup` is removed + - cop departments are nested for cops with a department that doesn’t match the extension name (`Capybara`, `FactoryBot`, `Rails`) + - `AllCops/RSpec/Patterns`/`AllCops/FactoryBot/Patterns` options are removed + - Calling `super` from `#on_new_investigation` defined in a cop is mandatory now + - In specs, do not define `cop` + +[discrete] +=== Adjust the configuration of `RSpec/EmptyExampleGroup` + +[source,yaml] +---- +# .rubocop.yml + +# Before +RSpec/EmptyExampleGroup: + CustomIncludeMethods: + - include_tests + +# After +RSpec: + Language: + Includes: + Examples: + - include_tests +---- + +=== Add a top-level `RSpec` department + +RuboCop extensions had cops with clashing names and departments, e.g. both `rspec-rails` and `rubocop-rspec` had `Rails::HttpStatus` cops. +To avoid issues, e.g. inability to disable just one of the cops, each extension now has its own uber-department. +Expectedly, RuboCop RSpec’s uber-department name is `RSpec`. +Changes are only applied to cops that don’t already have the department set to `RSpec`, i.e. `Capybara`, `FactoryBot` and `Rails`. + +[source,yaml] +---- +# .rubocop.yml + +# Before +Capybara/CurrentPathExpectation: + Enabled: false + +FactoryBot/AttributeDefinedStatically: + Enabled: false + +# remains the same +RSpec/EmptyExampleGroup: + Enabled: false + +# After +RSpec/Capybara/CurrentPathExpectation: + Enabled: false + +RSpec/FactoryBot/AttributeDefinedStatically: + Enabled: false + +# remains the same +RSpec/EmptyExampleGroup: + Enabled: false +---- + +https://github.com/rubocop-hq/rubocop/pull/8490[Learn more about this change]. + + +=== Use the RuboCop standard `Include` option to filter inspected files + +`Patterns` was a RuboCop RSpec-specific option, and RuboCop has a standard replacement. + +[source,yaml] +---- +# .rubocop.yml + +# Before +AllCops: + RSpec/FactoryBot: + Patterns: + - spec/factories/**/*.rb + - property/factories/**/*.rb + +# After +RSpec/FactoryBot: + Include: + - spec/factories/**/*.rb + - property/factories/**/*.rb +---- + +NOTE: Please keep in mind that `Include`'s merge mode is set to override the default settings, so if you intend to add a path while keeping the default paths, you should include the default `Include` paths in your configuration. + +https://github.com/rubocop-hq/rubocop-rspec/pull/1063[Learn more about this change]. + +== Custom Cop Update Guide + +Due to significant API changes, custom cops may break. +Here is the summary of the changes: + +1. The base class for cops is now `RuboCop::Cop::RSpec::Base` instead of `RuboCop::Cop::RSpec::Cop`. + +2. The module `RuboCop::Cop::RSpec::TopLevelDescribe` is replaced with a more generic `RuboCop::Cop::RSpec::TopLevelGroup`. + +3. `RuboCop::RSpec::Language` has been completely rewritten to support dynamic RSpec DSL aliases and negated matchers to fully support third-party libraries such as RSpec Rails, Pundit, Action Policy and many others. + +4. RuboCop RSpec updated the dependency of RuboCop to 1.0+. + +Below are the necessary steps to update custom cops to work with `rubocop-rspec` version 2.x. + + +=== Change the Parent Class + +Change the parent class of the custom cops from `RuboCop::Cop::RSpec::Cop` to `RuboCop::Cop::RSpec::Base`. + +[source,ruby] +---- +# Before +module RuboCop + module Cop + module RSpec + class FightPowerty < Cop + +# After +module RuboCop + module Cop + module RSpec + class FightPowerty < Base +---- + +https://github.com/rubocop-hq/rubocop-rspec/pull/962[Example pull request]. + + +=== Replace `TopLevelDescribe` + +`TopLevelDescribe` was incomplete, had poor performance and did not distinguish between example groups and shared example groups. + +`TopLevelGroup` provides a similar interface, but instead of a single `on_top_level_describe` hook there are two, `on_top_level_example_group` and `on_top_level_group`. +There’s no need yet for `on_top_level_shared_group` for RuboCop core cops, but if your custom cop needs such a hook, please feel free to send a pull request. + +Additionally, `single_top_level_describe?` is removed with no direct replacement. +You may use `top_level_groups` query method instead, e.g. `top_level_groups.one?`. + +Example pull requests to replace `TopLevelDescribe` with `TopLevelGroup` [https://github.com/rubocop-hq/rubocop-rspec/pull/978[1], https://github.com/rubocop-hq/rubocop-rspec/pull/932[2], https://github.com/rubocop-hq/rubocop-rspec/pull/977[3]]. + + +=== Change the `Language` Module Usages + +To allow for lazy initialization, and for loading of the language configuration after the class are loaded, a https://docs.rubocop.org/rubocop-ast/node_pattern.html#to-call-functions[function call feature of RuboCop AST] is used. + +The `RuboCop::RSpec::Language` is completely different now. + +`Hooks::ALL` and alike, and their accompanying helpers work differently. + +[source,ruby] +---- +# Before +def_node_matcher :shared_context, + SharedGroups::CONTEXT.block_pattern + +# After +def_node_matcher :shared_context, + block_pattern('#SharedGroups.context') +---- + +[source,ruby] +---- +# Before +def_node_search :examples?, + (Includes::EXAMPLES + Examples::ALL).send_pattern + +# After +def_node_search :examples?, + send_pattern('{#Includes.examples #Examples.all}') +---- + +[source,ruby] +---- +# Before +def_node_search :find_rspec_blocks, + ExampleGroups::ALL.block_pattern + +# After +def_node_search :find_rspec_blocks, + block_pattern('#ExampleGroups.all') +---- + +If you were calling Language elements directly, you have to make the same adjustments: + +[source,ruby] +---- +# Before +node&.sym_type? && Hooks::Scopes::ALL.include?(node.value) + +# After +node&.sym_type? && Language::HookScopes.all(node.value) +---- + +You may see a common pattern in the change. +There is a small exception, though: + +[source,ruby] +---- +# Before +ExampleGroups::GROUPS + +# After +ExampleGroups.regular + +# Before +Examples::EXAMPLES + +# After +Examples.regular +---- + +https://github.com/rubocop-hq/rubocop-rspec/pull/956[Pull request with more examples]. + +=== Always call `super` from `on_new_investigation` in your cops + +`on_new_investigation` is now used for internal purposes, and not calling `super` from your cop involves a risk of configuration not being properly loaded, and dynamic RSpec DSL matchers won't work. + +NOTE: You don't have to define `on_new_investigation` in your cops unless you need to. + +[source,ruby] +---- +module RuboCop + module Cop + module RSpec + class MultipleMemoizedHelpers < Base + def on_new_investigation + super # Always call `super` + @example_group_memoized_helpers = {} + end + end + end + end +end +---- + +https://github.com/rubocop-hq/rubocop-rspec/pull/956[Pull request with more examples]. + +=== Use `:config` RSpec metadata in cop specs + +`:config` metadata should be added to the top-level example group of your cop spec. +Doing otherwise will not pass configuration to the cop, and dynamic RSpec DSL matchers might not work. + +[source,ruby] +---- +# Before +RSpec.describe 'MyMightyCop' do + let(:cop) { described_class.new } + # ... +end + +# After +RSpec.describe 'MyMightyCop', :config do + # `cop` is defined for you by RuboCop's shared context that is included + # to example groups with :config metadata + + # ... +end +---- + +https://github.com/rubocop-hq/rubocop/blob/51ff1d7e29c985732fe129082c98d66c531a2611/lib/rubocop/rspec/shared_contexts.rb#L56[RuboCop takes care of defining everything for your cop specs]. + +=== Conform with RuboCop API Changes + +The parent project, RuboCop, has API changes. +While they won’t result in cop breakages, it is recommended to update cops to use new API’s. +Follow the https://docs.rubocop.org/rubocop/v1_upgrade_notes[RuboCop v1 update guide] to adjust custom cops’ use of RuboCop’s auto-correction API. diff --git a/docs/modules/ROOT/pages/usage.adoc b/docs/modules/ROOT/pages/usage.adoc index 2ac565dd4..f694781d5 100644 --- a/docs/modules/ROOT/pages/usage.adoc +++ b/docs/modules/ROOT/pages/usage.adoc @@ -1,19 +1,79 @@ = Usage -You need to tell RuboCop to load the RSpec extension. There are three -ways to do this: +You need to tell RuboCop to load the RSpec extension. +There are three ways to do this: == RuboCop configuration file -Put this into your `.rubocop.yml`. +Put this into your `.rubocop.yml`: ---- require: rubocop-rspec ---- +or, if you are using several extensions: + +---- +require: + - rubocop-rspec + - rubocop-performance +---- + Now you can run `rubocop` and it will automatically load the RuboCop RSpec cops together with the standard cops. +=== RSpec DSL configuration + +In case you https://github.com/rspec/rspec-core/blob/b0d0843a285693c64cdbe0c85726db155b46047e/lib/rspec/core/configuration.rb#L1122[define aliases for RSpec DSL], i.e. examples, example groups, hooks, or include example statements, you need to configure it so those elements are properly detected by RuboCop RSpec. + +[source,ruby] +---- +# spec/spec_helper.rb +RSpec.configure do |c| + c.alias_example_group_to :detail, :detailed => true +end + +# spec/detail_spec.rb +RSpec.detail "a detail" do + it "can do some less important stuff" do + end +end +---- + +[source,yaml] +---- +# .rubocop.yml +RSpec: + Language: + ExampleGroups: + Regular: + - detail +---- + +Some libraries extensively define RSpec DSL aliases (e.g. Pundit, Action Policy) or augment existing elements providing the same semantics (e.g. `let_it_be` from `test-prof`). +Those libraries can provide necessary configuration, but won't necessarily do so. +If they do, their README will mention that you have to explicitly require their configuration from your `.rubocop.yml` file. + +[source,yaml] +---- +# .rubocop.yml + +require: + - rubocop-rspec + - test-prof + +# or + +RSpec: + Language: + Helpers: + - let_it_be +---- + +NOTE: the default merge mode is to inherit, so you won't remove any of the default settings. + +RuboCop RSpec's https://github.com/rubocop-hq/rubocop-rspec/blob/a43424527c09fae2e6ddb133f4b2988f6c46bb2e/config/default.yml#L6[default configuration] is a good source of information on what can be configured. + == Command line [source,bash] @@ -36,23 +96,22 @@ end == Inspecting files that don't end with `_spec.rb` -By default, `rubocop-rspec` only inspects code within paths ending in `_spec.rb` or including `spec/`. You can override this setting in your config file by specifying one or more patterns: +By default, `rubocop-rspec` only inspects code within paths ending in `_spec.rb` or including `spec/`. You can override this setting in your config file by setting `Include`: [source,yaml] ---- -# Inspect all files -AllCops: - RSpec: - Patterns: - - '.+' +# Inspect files in `test/` directory +RSpec: + Include: + - '**/test/**/*' ---- - [source,yaml] ---- # Inspect only files ending with `_test.rb` -AllCops: - RSpec: - Patterns: - - '_test.rb$' +RSpec: + Include: + - '**/*_test.rb' ---- + +NOTE: Please keep in mind that `Include`'s merge mode is set to override the default settings, so if you intend to add a path while keeping the default paths, you should include the default `Include` paths in your configuration. diff --git a/lib/rubocop-rspec.rb b/lib/rubocop-rspec.rb index 006e39ee3..2b9cef84d 100644 --- a/lib/rubocop-rspec.rb +++ b/lib/rubocop-rspec.rb @@ -5,26 +5,25 @@ require 'rubocop' -require_relative 'rubocop/rspec' require_relative 'rubocop/rspec/version' require_relative 'rubocop/rspec/inject' require_relative 'rubocop/rspec/node' -require_relative 'rubocop/rspec/top_level_describe' require_relative 'rubocop/rspec/wording' -require_relative 'rubocop/rspec/language' require_relative 'rubocop/rspec/language/node_pattern' -require_relative 'rubocop/rspec/top_level_group' +require_relative 'rubocop/rspec/language' + +require_relative 'rubocop/cop/rspec/mixin/top_level_group' +require_relative 'rubocop/cop/rspec/mixin/variable' +require_relative 'rubocop/cop/rspec/mixin/final_end_location' +require_relative 'rubocop/cop/rspec/mixin/empty_line_separation' + require_relative 'rubocop/rspec/concept' require_relative 'rubocop/rspec/example_group' require_relative 'rubocop/rspec/example' require_relative 'rubocop/rspec/hook' -require_relative 'rubocop/rspec/variable' require_relative 'rubocop/cop/rspec/base' -require_relative 'rubocop/cop/rspec/cop' require_relative 'rubocop/rspec/align_let_brace' require_relative 'rubocop/rspec/factory_bot' -require_relative 'rubocop/rspec/final_end_location' -require_relative 'rubocop/rspec/empty_line_separation' require_relative 'rubocop/rspec/corrector/move_node' RuboCop::RSpec::Inject.defaults! diff --git a/lib/rubocop/cop/rspec/align_left_let_brace.rb b/lib/rubocop/cop/rspec/align_left_let_brace.rb index ebe3189f7..1d943c23e 100644 --- a/lib/rubocop/cop/rspec/align_left_let_brace.rb +++ b/lib/rubocop/cop/rspec/align_left_let_brace.rb @@ -27,11 +27,9 @@ def self.autocorrect_incompatible_with end def on_new_investigation + super return if processed_source.blank? - token_aligner = - RuboCop::RSpec::AlignLetBrace.new(processed_source.ast, :begin) - token_aligner.offending_tokens.each do |let| add_offense(let.loc.begin) do |corrector| corrector.insert_before( @@ -40,6 +38,12 @@ def on_new_investigation end end end + + private + + def token_aligner + RuboCop::RSpec::AlignLetBrace.new(processed_source.ast, :begin) + end end end end diff --git a/lib/rubocop/cop/rspec/align_right_let_brace.rb b/lib/rubocop/cop/rspec/align_right_let_brace.rb index 62c88bc04..93e7fc464 100644 --- a/lib/rubocop/cop/rspec/align_right_let_brace.rb +++ b/lib/rubocop/cop/rspec/align_right_let_brace.rb @@ -27,11 +27,9 @@ def self.autocorrect_incompatible_with end def on_new_investigation + super return if processed_source.blank? - token_aligner = - RuboCop::RSpec::AlignLetBrace.new(processed_source.ast, :end) - token_aligner.offending_tokens.each do |let| add_offense(let.loc.end) do |corrector| corrector.insert_before( @@ -40,6 +38,12 @@ def on_new_investigation end end end + + private + + def token_aligner + RuboCop::RSpec::AlignLetBrace.new(processed_source.ast, :end) + end end end end diff --git a/lib/rubocop/cop/rspec/base.rb b/lib/rubocop/cop/rspec/base.rb index a34bd1c99..93c174a7e 100644 --- a/lib/rubocop/cop/rspec/base.rb +++ b/lib/rubocop/cop/rspec/base.rb @@ -4,30 +4,9 @@ module RuboCop module Cop module RSpec # @abstract parent class to RSpec cops - # - # The criteria for whether rubocop-rspec analyzes a certain ruby file - # is configured via `AllCops/RSpec`. For example, if you want to - # customize your project to scan all files within a `test/` directory - # then you could add this to your configuration: - # - # @example configuring analyzed paths - # # .rubocop.yml - # # AllCops: - # # RSpec: - # # Patterns: - # # - '_test.rb$' - # # - '(?:^|/)test/' class Base < ::RuboCop::Cop::Base include RuboCop::RSpec::Language - include RuboCop::RSpec::Language::NodePattern - - DEFAULT_CONFIGURATION = - RuboCop::RSpec::CONFIG.fetch('AllCops').fetch('RSpec') - - DEFAULT_PATTERN_RE = Regexp.union( - DEFAULT_CONFIGURATION.fetch('Patterns') - .map(&Regexp.public_method(:new)) - ) + extend RuboCop::RSpec::Language::NodePattern exclude_from_registry @@ -36,39 +15,11 @@ def self.inherited(subclass) # rubocop:disable Lint/MissingSuper RuboCop::Cop::Base.inherited(subclass) end - def relevant_file?(file) - relevant_rubocop_rspec_file?(file) && super - end - - private - - def relevant_rubocop_rspec_file?(file) - rspec_pattern.match?(file) - end - - def rspec_pattern - if rspec_pattern_config? - Regexp.union(rspec_pattern_config.map(&Regexp.public_method(:new))) - else - DEFAULT_PATTERN_RE - end - end - - def all_cops_config - config - .for_all_cops - end - - def rspec_pattern_config? - return unless all_cops_config.key?('RSpec') - - all_cops_config.fetch('RSpec').key?('Patterns') - end - - def rspec_pattern_config - all_cops_config - .fetch('RSpec', DEFAULT_CONFIGURATION) - .fetch('Patterns') + # Set the config for dynamic DSL configuration-aware helpers + # that have no other means of accessing the configuration. + def on_new_investigation + super + RuboCop::RSpec::Language.config = config['RSpec']['Language'] end end end diff --git a/lib/rubocop/cop/rspec/be.rb b/lib/rubocop/cop/rspec/be.rb index 5f388c299..af2cfca89 100644 --- a/lib/rubocop/cop/rspec/be.rb +++ b/lib/rubocop/cop/rspec/be.rb @@ -23,7 +23,7 @@ class Be < Base MSG = 'Don\'t use `be` without an argument.' def_node_matcher :be_without_args, <<-PATTERN - (send _ #{Runners::ALL.node_pattern_union} $(send nil? :be)) + (send _ #Runners.all $(send nil? :be)) PATTERN def on_send(node) diff --git a/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb b/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb index 9f4f1fd51..5d692843b 100644 --- a/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +++ b/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb @@ -37,13 +37,13 @@ class CurrentPathExpectation < Base # Supported matchers: eq(...) / match(/regexp/) / match('regexp') def_node_matcher :as_is_matcher, <<-PATTERN (send - #expectation_set_on_current_path $#{Runners::ALL.node_pattern_union} + #expectation_set_on_current_path $#Runners.all ${(send nil? :eq ...) (send nil? :match (regexp ...))}) PATTERN def_node_matcher :regexp_str_matcher, <<-PATTERN (send - #expectation_set_on_current_path $#{Runners::ALL.node_pattern_union} + #expectation_set_on_current_path $#Runners.all $(send nil? :match (str $_))) PATTERN diff --git a/lib/rubocop/cop/rspec/capybara/feature_methods.rb b/lib/rubocop/cop/rspec/capybara/feature_methods.rb index 5ff784806..96cdbd7ad 100644 --- a/lib/rubocop/cop/rspec/capybara/feature_methods.rb +++ b/lib/rubocop/cop/rspec/capybara/feature_methods.rb @@ -55,8 +55,9 @@ class FeatureMethods < Base feature: :describe }.freeze - def_node_matcher :capybara_speak, - SelectorSet.new(MAP.keys).node_pattern_union + def_node_matcher :capybara_speak, <<-PATTERN + {#{MAP.keys.map(&:inspect).join(' ')}} + PATTERN def_node_matcher :spec?, <<-PATTERN (block diff --git a/lib/rubocop/cop/rspec/cop.rb b/lib/rubocop/cop/rspec/cop.rb deleted file mode 100644 index 057103951..000000000 --- a/lib/rubocop/cop/rspec/cop.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - # @deprecated Use ::RuboCop::Cop::RSpec::Base instead - Cop = Base - end - end -end diff --git a/lib/rubocop/cop/rspec/describe_class.rb b/lib/rubocop/cop/rspec/describe_class.rb index e007cb245..45bb737c4 100644 --- a/lib/rubocop/cop/rspec/describe_class.rb +++ b/lib/rubocop/cop/rspec/describe_class.rb @@ -35,7 +35,7 @@ module RSpec # describe "A feature example", type: :feature do # end class DescribeClass < Base - include RuboCop::RSpec::TopLevelGroup + include TopLevelGroup MSG = 'The first argument to describe should be '\ 'the class or module being tested.' diff --git a/lib/rubocop/cop/rspec/describe_method.rb b/lib/rubocop/cop/rspec/describe_method.rb index cfc1e8148..2ff64e760 100644 --- a/lib/rubocop/cop/rspec/describe_method.rb +++ b/lib/rubocop/cop/rspec/describe_method.rb @@ -17,7 +17,7 @@ module RSpec # describe MyClass, '.my_class_method' do # end class DescribeMethod < Base - include RuboCop::RSpec::TopLevelGroup + include TopLevelGroup MSG = 'The second argument to describe should be the method '\ "being tested. '#instance' or '.class'." diff --git a/lib/rubocop/cop/rspec/described_class.rb b/lib/rubocop/cop/rspec/described_class.rb index 6f4e945d0..0d5deb2c4 100644 --- a/lib/rubocop/cop/rspec/described_class.rb +++ b/lib/rubocop/cop/rspec/described_class.rb @@ -65,8 +65,7 @@ class DescribedClass < Base (block (send (const nil? {:Class :Module :Struct}) :new ...) ...) PATTERN - def_node_matcher :rspec_block?, - RuboCop::RSpec::Language::ALL.block_pattern + def_node_matcher :rspec_block?, block_pattern('#ALL.all') def_node_matcher :scope_changing_syntax?, '{def class module}' diff --git a/lib/rubocop/cop/rspec/described_class_module_wrapping.rb b/lib/rubocop/cop/rspec/described_class_module_wrapping.rb index b5b9c867e..044ec85e2 100644 --- a/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +++ b/lib/rubocop/cop/rspec/described_class_module_wrapping.rb @@ -22,8 +22,7 @@ module RSpec class DescribedClassModuleWrapping < Base MSG = 'Avoid opening modules and defining specs within them.' - def_node_search :find_rspec_blocks, - ExampleGroups::ALL.block_pattern + def_node_search :find_rspec_blocks, block_pattern('#ExampleGroups.all') def on_module(node) find_rspec_blocks(node) do diff --git a/lib/rubocop/cop/rspec/dialect.rb b/lib/rubocop/cop/rspec/dialect.rb index d952d6958..30cd5b559 100644 --- a/lib/rubocop/cop/rspec/dialect.rb +++ b/lib/rubocop/cop/rspec/dialect.rb @@ -47,7 +47,7 @@ class Dialect < Base MSG = 'Prefer `%s` over `%s`.' - def_node_matcher :rspec_method?, ALL.send_pattern + def_node_matcher :rspec_method?, send_pattern('#ALL.all') def on_send(node) return unless rspec_method?(node) diff --git a/lib/rubocop/cop/rspec/empty_example_group.rb b/lib/rubocop/cop/rspec/empty_example_group.rb index e2aca88b3..745e59e2a 100644 --- a/lib/rubocop/cop/rspec/empty_example_group.rb +++ b/lib/rubocop/cop/rspec/empty_example_group.rb @@ -5,8 +5,6 @@ module Cop module RSpec # Checks if an example group does not include any tests. # - # This cop is configurable using the `CustomIncludeMethods` option - # # @example usage # # # bad @@ -37,31 +35,6 @@ module RSpec # describe Bacon do # pending 'will add tests later' # end - # - # @example configuration - # - # # .rubocop.yml - # # RSpec/EmptyExampleGroup: - # # CustomIncludeMethods: - # # - include_tests - # - # # spec_helper.rb - # RSpec.configure do |config| - # config.alias_it_behaves_like_to(:include_tests) - # end - # - # # bacon_spec.rb - # describe Bacon do - # let(:bacon) { Bacon.new(chunkiness) } - # let(:chunkiness) { false } - # - # context 'extra chunky' do # not flagged by rubocop - # let(:chunkiness) { true } - # - # include_tests 'shared tests' - # end - # end - # class EmptyExampleGroup < Base MSG = 'Empty example group detected.' @@ -76,7 +49,7 @@ class EmptyExampleGroup < Base # @param node [RuboCop::AST::Node] # @yield [RuboCop::AST::Node] example group body def_node_matcher :example_group_body, <<~PATTERN - (block #{ExampleGroups::ALL.send_pattern} args $_) + (block #{send_pattern('#ExampleGroups.all')} args $_) PATTERN # @!method example_or_group_or_include?(node) @@ -95,12 +68,10 @@ class EmptyExampleGroup < Base # @return [Array] matching nodes def_node_matcher :example_or_group_or_include?, <<~PATTERN { - #{Examples::ALL.send_pattern} - #{Examples::ALL.block_pattern} - #{ExampleGroups::ALL.block_pattern} - #{Includes::ALL.send_pattern} - #{Includes::ALL.block_pattern} - (send nil? #custom_include? ...) + #{block_pattern( + '{#Examples.all #ExampleGroups.all #Includes.all}' + )} + #{send_pattern('{#Examples.all #Includes.all}')} } PATTERN @@ -120,7 +91,7 @@ class EmptyExampleGroup < Base # @param node [RuboCop::AST::Node] # @return [Array] matching nodes def_node_matcher :examples_inside_block?, <<~PATTERN - (block !#{Hooks::ALL.send_pattern} _ #examples?) + (block !#{send_pattern('#Hooks.all')} _ #examples?) PATTERN # @!method examples_directly_or_in_block?(node) @@ -192,16 +163,6 @@ def conditionals_with_examples?(body) def examples_in_branches?(if_node) if_node.branches.any? { |branch| examples?(branch) } end - - def custom_include?(method_name) - custom_include_methods.include?(method_name) - end - - def custom_include_methods - cop_config - .fetch('CustomIncludeMethods', []) - .map(&:to_sym) - end end end end diff --git a/lib/rubocop/cop/rspec/empty_hook.rb b/lib/rubocop/cop/rspec/empty_hook.rb index e272a7bb2..09cb77e45 100644 --- a/lib/rubocop/cop/rspec/empty_hook.rb +++ b/lib/rubocop/cop/rspec/empty_hook.rb @@ -29,7 +29,7 @@ class EmptyHook < Base MSG = 'Empty hook detected.' def_node_matcher :empty_hook?, <<~PATTERN - (block $#{Hooks::ALL.send_pattern} _ nil?) + (block $#{send_pattern('#Hooks.all')} _ nil?) PATTERN def on_block(node) diff --git a/lib/rubocop/cop/rspec/empty_line_after_example.rb b/lib/rubocop/cop/rspec/empty_line_after_example.rb index e0d783739..912225188 100644 --- a/lib/rubocop/cop/rspec/empty_line_after_example.rb +++ b/lib/rubocop/cop/rspec/empty_line_after_example.rb @@ -43,7 +43,7 @@ module RSpec # class EmptyLineAfterExample < Base extend AutoCorrector - include RuboCop::RSpec::EmptyLineSeparation + include EmptyLineSeparation MSG = 'Add an empty line after `%s`.' diff --git a/lib/rubocop/cop/rspec/empty_line_after_example_group.rb b/lib/rubocop/cop/rspec/empty_line_after_example_group.rb index e02fb73ba..e0f89f885 100644 --- a/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +++ b/lib/rubocop/cop/rspec/empty_line_after_example_group.rb @@ -25,7 +25,7 @@ module RSpec # class EmptyLineAfterExampleGroup < Base extend AutoCorrector - include RuboCop::RSpec::EmptyLineSeparation + include EmptyLineSeparation MSG = 'Add an empty line after `%s`.' diff --git a/lib/rubocop/cop/rspec/empty_line_after_final_let.rb b/lib/rubocop/cop/rspec/empty_line_after_final_let.rb index 9bfc56391..1f91190ed 100644 --- a/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +++ b/lib/rubocop/cop/rspec/empty_line_after_final_let.rb @@ -18,7 +18,7 @@ module RSpec # it { does_something } class EmptyLineAfterFinalLet < Base extend AutoCorrector - include RuboCop::RSpec::EmptyLineSeparation + include EmptyLineSeparation MSG = 'Add an empty line after the last `%s`.' diff --git a/lib/rubocop/cop/rspec/empty_line_after_hook.rb b/lib/rubocop/cop/rspec/empty_line_after_hook.rb index 4728ffdcb..bf2ae16f6 100644 --- a/lib/rubocop/cop/rspec/empty_line_after_hook.rb +++ b/lib/rubocop/cop/rspec/empty_line_after_hook.rb @@ -35,7 +35,7 @@ module RSpec # class EmptyLineAfterHook < Base extend AutoCorrector - include RuboCop::RSpec::EmptyLineSeparation + include EmptyLineSeparation MSG = 'Add an empty line after `%s`.' diff --git a/lib/rubocop/cop/rspec/empty_line_after_subject.rb b/lib/rubocop/cop/rspec/empty_line_after_subject.rb index bed4d1606..4f7735050 100644 --- a/lib/rubocop/cop/rspec/empty_line_after_subject.rb +++ b/lib/rubocop/cop/rspec/empty_line_after_subject.rb @@ -16,7 +16,7 @@ module RSpec # let(:foo) { bar } class EmptyLineAfterSubject < Base extend AutoCorrector - include RuboCop::RSpec::EmptyLineSeparation + include EmptyLineSeparation MSG = 'Add an empty line after `%s`.' @@ -32,7 +32,7 @@ def on_block(node) def in_spec_block?(node) node.each_ancestor(:block).any? do |ancestor| - Examples::ALL.include?(ancestor.method_name) + Examples.all(ancestor.method_name) end end end diff --git a/lib/rubocop/cop/rspec/expect_actual.rb b/lib/rubocop/cop/rspec/expect_actual.rb index 2b897ad8e..b520882d8 100644 --- a/lib/rubocop/cop/rspec/expect_actual.rb +++ b/lib/rubocop/cop/rspec/expect_actual.rb @@ -48,7 +48,7 @@ class ExpectActual < Base def_node_matcher :expect_literal, <<~PATTERN (send (send nil? :expect $#literal?) - #{Runners::ALL.node_pattern_union} + #Runners.all { (send (send nil? $:be) :== $_) (send nil? $_ $_ ...) diff --git a/lib/rubocop/cop/rspec/expect_in_hook.rb b/lib/rubocop/cop/rspec/expect_in_hook.rb index 79f7c929c..dc9d6fcc4 100644 --- a/lib/rubocop/cop/rspec/expect_in_hook.rb +++ b/lib/rubocop/cop/rspec/expect_in_hook.rb @@ -23,7 +23,7 @@ module RSpec class ExpectInHook < Base MSG = 'Do not use `%s` in `%s` hook' - def_node_search :expectation, Expectations::ALL.send_pattern + def_node_search :expectation, send_pattern('#Expectations.all') def on_block(node) return unless hook?(node) diff --git a/lib/rubocop/cop/rspec/file_path.rb b/lib/rubocop/cop/rspec/file_path.rb index 544095dce..9409ad4b9 100644 --- a/lib/rubocop/cop/rspec/file_path.rb +++ b/lib/rubocop/cop/rspec/file_path.rb @@ -57,7 +57,7 @@ module RSpec # my_class_spec.rb # describe MyClass, '#method' # class FilePath < Base - include RuboCop::RSpec::TopLevelGroup + include TopLevelGroup MSG = 'Spec path should end with `%s`.' diff --git a/lib/rubocop/cop/rspec/focus.rb b/lib/rubocop/cop/rspec/focus.rb index d49c77f9e..4e106efeb 100644 --- a/lib/rubocop/cop/rspec/focus.rb +++ b/lib/rubocop/cop/rspec/focus.rb @@ -22,19 +22,25 @@ module RSpec class Focus < Base MSG = 'Focused spec found.' - focused = ExampleGroups::FOCUSED + Examples::FOCUSED - - def_node_matcher :focusable_selector?, - (ExampleGroups::GROUPS + ExampleGroups::SKIPPED + - Examples::EXAMPLES + Examples::SKIPPED + - Examples::PENDING).node_pattern_union + def_node_matcher :focusable_selector?, <<-PATTERN + { + #ExampleGroups.regular + #ExampleGroups.skipped + #Examples.regular + #Examples.skipped + #Examples.pending + } + PATTERN def_node_matcher :metadata, <<-PATTERN {(send #rspec? #focusable_selector? <$(sym :focus) ...>) (send #rspec? #focusable_selector? ... (hash <$(pair (sym :focus) true) ...>))} PATTERN - def_node_matcher :focused_block?, focused.send_pattern + def_node_matcher :focused_block?, + send_pattern(<<~PATTERN) + {#ExampleGroups.focused #Examples.focused} + PATTERN def on_send(node) focus_metadata(node) do |focus| diff --git a/lib/rubocop/cop/rspec/hook_argument.rb b/lib/rubocop/cop/rspec/hook_argument.rb index b722c10ab..fc8a1e767 100644 --- a/lib/rubocop/cop/rspec/hook_argument.rb +++ b/lib/rubocop/cop/rspec/hook_argument.rb @@ -64,13 +64,11 @@ class HookArgument < Base IMPLICIT_MSG = 'Omit the default `%p` argument for RSpec hooks.' EXPLICIT_MSG = 'Use `%p` for RSpec hooks.' - def_node_matcher :hook?, Hooks::ALL.node_pattern_union - def_node_matcher :scoped_hook, <<-PATTERN - (block $(send _ #hook? (sym ${:each :example})) ...) + (block $(send _ #Hooks.all (sym ${:each :example})) ...) PATTERN - def_node_matcher :unscoped_hook, '(block $(send _ #hook?) ...)' + def_node_matcher :unscoped_hook, '(block $(send _ #Hooks.all) ...)' def on_block(node) hook(node) do |method_send, scope_name| diff --git a/lib/rubocop/cop/rspec/hooks_before_examples.rb b/lib/rubocop/cop/rspec/hooks_before_examples.rb index fa9eea0b9..2c8bc27af 100644 --- a/lib/rubocop/cop/rspec/hooks_before_examples.rb +++ b/lib/rubocop/cop/rspec/hooks_before_examples.rb @@ -30,8 +30,8 @@ class HooksBeforeExamples < Base def_node_matcher :example_or_group?, <<-PATTERN { - #{(Examples::ALL + ExampleGroups::ALL).block_pattern} - #{Includes::EXAMPLES.send_pattern} + #{block_pattern('{#ExampleGroups.all #Examples.all}')} + #{send_pattern('#Includes.examples')} } PATTERN diff --git a/lib/rubocop/cop/rspec/implicit_expect.rb b/lib/rubocop/cop/rspec/implicit_expect.rb index c08fd877d..aeb9b974f 100644 --- a/lib/rubocop/cop/rspec/implicit_expect.rb +++ b/lib/rubocop/cop/rspec/implicit_expect.rb @@ -33,7 +33,7 @@ class ImplicitExpect < Base def_node_matcher :implicit_expect, <<-PATTERN { (send nil? ${:should :should_not} ...) - (send (send nil? $:is_expected) #{Runners::ALL.node_pattern_union} ...) + (send (send nil? $:is_expected) #Runners.all ...) } PATTERN diff --git a/lib/rubocop/cop/rspec/instance_variable.rb b/lib/rubocop/cop/rspec/instance_variable.rb index 4e1ad1b6f..76ebb7ad6 100644 --- a/lib/rubocop/cop/rspec/instance_variable.rb +++ b/lib/rubocop/cop/rspec/instance_variable.rb @@ -47,7 +47,7 @@ module RSpec # end # class InstanceVariable < Base - include RuboCop::RSpec::TopLevelGroup + include TopLevelGroup MSG = 'Avoid instance variables – use let, ' \ 'a method call, or a local variable (if possible).' diff --git a/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb b/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb deleted file mode 100644 index 018b5c9ff..000000000 --- a/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -module RuboCop - module Cop - module RSpec - # Checks invalid usage for predicate matcher. - # - # Predicate matcher does not need a question. - # This cop checks an unnecessary question in predicate matcher. - # - # @example - # - # # bad - # expect(foo).to be_something? - # - # # good - # expect(foo).to be_something - class InvalidPredicateMatcher < Base - MSG = 'Omit `?` from `%s`.' - - def_node_matcher :invalid_predicate_matcher?, <<-PATTERN - (send (send nil? :expect ...) #{Runners::ALL.node_pattern_union} $(send nil? #predicate?)) - PATTERN - - def on_send(node) - invalid_predicate_matcher?(node) do |predicate| - add_offense(predicate, - message: format(MSG, matcher: predicate.method_name)) - end - end - - private - - def predicate?(name) - name = name.to_s - name.start_with?('be_', 'have_') && name.end_with?('?') - end - end - end - end -end diff --git a/lib/rubocop/cop/rspec/let_before_examples.rb b/lib/rubocop/cop/rspec/let_before_examples.rb index aeacb76fb..e01354b95 100644 --- a/lib/rubocop/cop/rspec/let_before_examples.rb +++ b/lib/rubocop/cop/rspec/let_before_examples.rb @@ -37,8 +37,8 @@ class LetBeforeExamples < Base def_node_matcher :example_or_group?, <<-PATTERN { - #{(Examples::ALL + ExampleGroups::ALL).block_pattern} - #{Includes::EXAMPLES.send_pattern} + #{block_pattern('{#ExampleGroups.all #Examples.all}')} + #{send_pattern('#Includes.examples')} } PATTERN diff --git a/lib/rubocop/cop/rspec/let_setup.rb b/lib/rubocop/cop/rspec/let_setup.rb index 819ae5e4c..b4c929023 100644 --- a/lib/rubocop/cop/rspec/let_setup.rb +++ b/lib/rubocop/cop/rspec/let_setup.rb @@ -29,10 +29,13 @@ class LetSetup < Base MSG = 'Do not use `let!` to setup objects not referenced in tests.' def_node_matcher :example_or_shared_group_or_including?, - ( - ExampleGroups::ALL + SharedGroups::ALL + - Includes::ALL - ).block_pattern + block_pattern(<<~PATTERN) + { + #SharedGroups.all + #ExampleGroups.all + #Includes.all + } + PATTERN def_node_matcher :let_bang, <<-PATTERN { diff --git a/lib/rubocop/cop/rspec/message_spies.rb b/lib/rubocop/cop/rspec/message_spies.rb index e1460d2c0..fa30d09ee 100644 --- a/lib/rubocop/cop/rspec/message_spies.rb +++ b/lib/rubocop/cop/rspec/message_spies.rb @@ -36,7 +36,7 @@ class MessageSpies < Base SUPPORTED_STYLES = %w[have_received receive].freeze def_node_matcher :message_expectation, %( - (send (send nil? :expect $_) #{Runners::ALL.node_pattern_union} ...) + (send (send nil? :expect $_) #Runners.all ...) ) def_node_search :receive_message, %( diff --git a/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb b/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb new file mode 100644 index 000000000..5d48603f2 --- /dev/null +++ b/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module RSpec + # Helps determine the offending location if there is not an empty line + # following the node. Allows comments to follow directly after. + module EmptyLineSeparation + include FinalEndLocation + include RangeHelp + + def missing_separating_line_offense(node) + return if last_child?(node) + + missing_separating_line(node) do |location| + msg = yield(node.method_name) + add_offense(location, message: msg) do |corrector| + corrector.insert_after(location.end, "\n") + end + end + end + + def missing_separating_line(node) + line = final_end_location(node).line + + line += 1 while comment_line?(processed_source[line]) + + return if processed_source[line].blank? + + yield offending_loc(line) + end + + def offending_loc(last_line) + offending_line = processed_source[last_line - 1] + + content_length = offending_line.lstrip.length + start = offending_line.length - content_length + + source_range(processed_source.buffer, + last_line, start, content_length) + end + + def last_child?(node) + return true unless node.parent&.begin_type? + + node.equal?(node.parent.children.last) + end + end + end + end +end diff --git a/lib/rubocop/cop/rspec/mixin/final_end_location.rb b/lib/rubocop/cop/rspec/mixin/final_end_location.rb new file mode 100644 index 000000000..526049f5d --- /dev/null +++ b/lib/rubocop/cop/rspec/mixin/final_end_location.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module RSpec + # Helps find the true end location of nodes which might contain heredocs. + module FinalEndLocation + def final_end_location(start_node) + heredoc_endings = + start_node.each_node(:str, :dstr, :xstr) + .select(&:heredoc?) + .map { |node| node.loc.heredoc_end } + + [start_node.loc.end, *heredoc_endings].max_by(&:line) + end + end + end + end +end diff --git a/lib/rubocop/cop/rspec/mixin/top_level_group.rb b/lib/rubocop/cop/rspec/mixin/top_level_group.rb new file mode 100644 index 000000000..c3b48fb1d --- /dev/null +++ b/lib/rubocop/cop/rspec/mixin/top_level_group.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module RSpec + # Helper methods for top level example group cops + module TopLevelGroup + extend RuboCop::NodePattern::Macros + + def on_new_investigation + super + + top_level_groups.each do |node| + on_top_level_example_group(node) if example_group?(node) + on_top_level_group(node) + end + end + + def top_level_groups + @top_level_groups ||= + top_level_nodes(root_node).select { |n| spec_group?(n) } + end + + private + + # Dummy methods to be overridden in the consumer + def on_top_level_example_group(_node); end + + def on_top_level_group(_node); end + + def top_level_group?(node) + top_level_groups.include?(node) + end + + def top_level_nodes(node) + return [] if node.nil? + + case node.type + when :begin + node.children + when :module, :class + top_level_nodes(node.body) + else + [node] + end + end + + def root_node + processed_source.ast + end + end + end + end +end diff --git a/lib/rubocop/cop/rspec/mixin/variable.rb b/lib/rubocop/cop/rspec/mixin/variable.rb new file mode 100644 index 000000000..5a855c2ab --- /dev/null +++ b/lib/rubocop/cop/rspec/mixin/variable.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module RSpec + # Helps check offenses with variable definitions + module Variable + extend RuboCop::NodePattern::Macros + + Subjects = RuboCop::RSpec::Language::Subjects + Helpers = RuboCop::RSpec::Language::Helpers + + def_node_matcher :variable_definition?, <<~PATTERN + (send nil? {#Subjects.all #Helpers.all} + $({sym str dsym dstr} ...) ...) + PATTERN + end + end + end +end diff --git a/lib/rubocop/cop/rspec/multiple_describes.rb b/lib/rubocop/cop/rspec/multiple_describes.rb index 9ca9fb9ae..14c535f45 100644 --- a/lib/rubocop/cop/rspec/multiple_describes.rb +++ b/lib/rubocop/cop/rspec/multiple_describes.rb @@ -23,7 +23,7 @@ module RSpec # end # end class MultipleDescribes < Base - include RuboCop::RSpec::TopLevelGroup + include TopLevelGroup MSG = 'Do not use multiple top-level example groups - '\ 'try to nest them.' diff --git a/lib/rubocop/cop/rspec/multiple_expectations.rb b/lib/rubocop/cop/rspec/multiple_expectations.rb index 865e2a696..79abeb1c7 100644 --- a/lib/rubocop/cop/rspec/multiple_expectations.rb +++ b/lib/rubocop/cop/rspec/multiple_expectations.rb @@ -60,7 +60,7 @@ class MultipleExpectations < Base } ...) PATTERN - def_node_matcher :expect?, Expectations::ALL.send_pattern + def_node_matcher :expect?, send_pattern('#Expectations.all') def_node_matcher :aggregate_failures_block?, <<-PATTERN (block (send nil? :aggregate_failures ...) ...) PATTERN diff --git a/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb b/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb index 4ad7a0cf2..674b383b9 100644 --- a/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +++ b/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb @@ -85,7 +85,7 @@ module RSpec # class MultipleMemoizedHelpers < Base include ConfigurableMax - include RuboCop::RSpec::Variable + include Variable MSG = 'Example group has too many memoized helpers [%d/%d]' @@ -101,6 +101,7 @@ def on_block(node) end def on_new_investigation + super @example_group_memoized_helpers = {} end @@ -128,6 +129,7 @@ def helpers(node) def variable_nodes(node) example_group = RuboCop::RSpec::ExampleGroup.new(node) + if allow_subject? example_group.lets else diff --git a/lib/rubocop/cop/rspec/named_subject.rb b/lib/rubocop/cop/rspec/named_subject.rb index 44d30a7b1..b462d5fae 100644 --- a/lib/rubocop/cop/rspec/named_subject.rb +++ b/lib/rubocop/cop/rspec/named_subject.rb @@ -42,24 +42,20 @@ module RSpec # it { is_expected.to be_valid } # end class NamedSubject < Base - MSG = 'Name your test subject if you need '\ - 'to reference it explicitly.' + MSG = 'Name your test subject if you need to reference it explicitly.' - def_node_matcher :rspec_block?, <<-PATTERN - { - #{Examples::ALL.block_pattern} - #{Hooks::ALL.block_pattern} - } - PATTERN + def_node_matcher :example_or_hook_block?, + block_pattern('{#Examples.all #Hooks.all}') - def_node_matcher :shared_example?, <<-PATTERN - #{SharedGroups::EXAMPLES.block_pattern} - PATTERN + def_node_matcher :shared_example?, + block_pattern('#SharedGroups.examples') def_node_search :subject_usage, '$(send nil? :subject)' def on_block(node) - return if !rspec_block?(node) || ignored_shared_example?(node) + if !example_or_hook_block?(node) || ignored_shared_example?(node) + return + end subject_usage(node) do |subject_node| add_offense(subject_node.loc.selector) diff --git a/lib/rubocop/cop/rspec/nested_groups.rb b/lib/rubocop/cop/rspec/nested_groups.rb index 44301d789..c9a6e9e5f 100644 --- a/lib/rubocop/cop/rspec/nested_groups.rb +++ b/lib/rubocop/cop/rspec/nested_groups.rb @@ -87,7 +87,7 @@ module RSpec # class NestedGroups < Base include ConfigurableMax - include RuboCop::RSpec::TopLevelGroup + include TopLevelGroup MSG = 'Maximum example group nesting exceeded [%d/%d].' diff --git a/lib/rubocop/cop/rspec/overwriting_setup.rb b/lib/rubocop/cop/rspec/overwriting_setup.rb index 5e0cbe14b..973e9e007 100644 --- a/lib/rubocop/cop/rspec/overwriting_setup.rb +++ b/lib/rubocop/cop/rspec/overwriting_setup.rb @@ -24,7 +24,8 @@ module RSpec class OverwritingSetup < Base MSG = '`%s` is already defined.' - def_node_matcher :setup?, (Helpers::ALL + Subject::ALL).block_pattern + def_node_matcher :setup?, block_pattern('{#Helpers.all #Subjects.all}') + def_node_matcher :first_argument_name, '(send _ _ ({str sym} $_))' def on_block(node) diff --git a/lib/rubocop/cop/rspec/pending.rb b/lib/rubocop/cop/rspec/pending.rb index 830c18a7a..4b04ab7ff 100644 --- a/lib/rubocop/cop/rspec/pending.rb +++ b/lib/rubocop/cop/rspec/pending.rb @@ -34,10 +34,10 @@ module RSpec class Pending < Base MSG = 'Pending spec found.' - PENDING = Examples::PENDING + Examples::SKIPPED + ExampleGroups::SKIPPED - SKIPPABLE = ExampleGroups::GROUPS + Examples::EXAMPLES - - def_node_matcher :skippable?, SKIPPABLE.send_pattern + def_node_matcher :skippable?, + send_pattern(<<~PATTERN) + {#ExampleGroups.regular #Examples.regular} + PATTERN def_node_matcher :skipped_in_metadata?, <<-PATTERN { @@ -47,7 +47,15 @@ class Pending < Base PATTERN def_node_matcher :skip_or_pending?, '{(sym :skip) (sym :pending)}' - def_node_matcher :pending_block?, PENDING.send_pattern + + def_node_matcher :pending_block?, + send_pattern(<<~PATTERN) + { + #ExampleGroups.skipped + #Examples.skipped + #Examples.pending + } + PATTERN def on_send(node) return unless pending_block?(node) || skipped?(node) diff --git a/lib/rubocop/cop/rspec/predicate_matcher.rb b/lib/rubocop/cop/rspec/predicate_matcher.rb index 13d1f6fbe..77c7e1d42 100644 --- a/lib/rubocop/cop/rspec/predicate_matcher.rb +++ b/lib/rubocop/cop/rspec/predicate_matcher.rb @@ -30,7 +30,7 @@ def check_inflected(node) (send nil? :expect { (block $(send !nil? #predicate? ...) ...) $(send !nil? #predicate? ...)}) - $#{Runners::ALL.node_pattern_union} + $#Runners.all $#boolean_matcher?) PATTERN @@ -155,7 +155,7 @@ def check_explicit(node) # rubocop:disable Metrics/MethodLength def_node_matcher :predicate_matcher?, <<-PATTERN (send (send nil? :expect $!nil?) - #{Runners::ALL.node_pattern_union} + #Runners.all {$(send nil? #predicate_matcher_name? ...) (block $(send nil? #predicate_matcher_name? ...) ...)}) PATTERN @@ -164,7 +164,7 @@ def check_explicit(node) # rubocop:disable Metrics/MethodLength (block (send (send nil? :expect $!nil?) - #{Runners::ALL.node_pattern_union} + #Runners.all $(send nil? #predicate_matcher_name?)) ...) PATTERN diff --git a/lib/rubocop/cop/rspec/repeated_include_example.rb b/lib/rubocop/cop/rspec/repeated_include_example.rb index c631aa06c..57d02a2a4 100644 --- a/lib/rubocop/cop/rspec/repeated_include_example.rb +++ b/lib/rubocop/cop/rspec/repeated_include_example.rb @@ -54,10 +54,11 @@ class RepeatedIncludeExample < Base (begin <#include_examples? #include_examples? ...>) PATTERN - def_node_matcher :include_examples?, Includes::EXAMPLES.send_pattern + def_node_matcher :include_examples?, + send_pattern('#Includes.examples') def_node_matcher :shared_examples_name, <<-PATTERN - (send _ #{Includes::EXAMPLES.node_pattern_union} $_ ...) + (send _ #Includes.examples $_ ...) PATTERN def on_begin(node) diff --git a/lib/rubocop/cop/rspec/shared_context.rb b/lib/rubocop/cop/rspec/shared_context.rb index 34a908888..c15f8a276 100644 --- a/lib/rubocop/cop/rspec/shared_context.rb +++ b/lib/rubocop/cop/rspec/shared_context.rb @@ -59,14 +59,24 @@ class SharedContext < Base MSG_CONTEXT = "Use `shared_context` when you don't "\ 'define examples.' - examples = (Examples::ALL + Includes::EXAMPLES) - def_node_search :examples?, examples.send_pattern + def_node_search :examples?, + send_pattern('{#Includes.examples #Examples.all}') - context = (Hooks::ALL + Helpers::ALL + Includes::CONTEXT + Subject::ALL) - def_node_search :context?, context.send_pattern + def_node_search :context?, <<-PATTERN + ( + send #rspec? { + #Subjects.all + #Helpers.all + #Includes.context + #Hooks.all + } ... + ) + PATTERN - def_node_matcher :shared_context, SharedGroups::CONTEXT.block_pattern - def_node_matcher :shared_example, SharedGroups::EXAMPLES.block_pattern + def_node_matcher :shared_context, + block_pattern('#SharedGroups.context') + def_node_matcher :shared_example, + block_pattern('#SharedGroups.examples') def on_block(node) context_with_only_examples(node) do diff --git a/lib/rubocop/cop/rspec/shared_examples.rb b/lib/rubocop/cop/rspec/shared_examples.rb index 10732d30c..7bdf2d408 100644 --- a/lib/rubocop/cop/rspec/shared_examples.rb +++ b/lib/rubocop/cop/rspec/shared_examples.rb @@ -24,7 +24,9 @@ class SharedExamples < Base extend AutoCorrector def_node_matcher :shared_examples, - (SharedGroups::ALL + Includes::ALL).send_pattern + send_pattern( + '{#SharedGroups.all #Includes.all}' + ) def on_send(node) shared_examples(node) do diff --git a/lib/rubocop/cop/rspec/stubbed_mock.rb b/lib/rubocop/cop/rspec/stubbed_mock.rb index bd508ceaf..cdf4b9be7 100644 --- a/lib/rubocop/cop/rspec/stubbed_mock.rb +++ b/lib/rubocop/cop/rspec/stubbed_mock.rb @@ -60,7 +60,7 @@ class StubbedMock < Base # @yield [RuboCop::AST::Node] expectation, method name, matcher def_node_matcher :expectation, <<~PATTERN (send - $(send nil? $#{Expectations::ALL.node_pattern_union} ...) + $(send nil? $#Expectations.all ...) :to $_) PATTERN diff --git a/lib/rubocop/cop/rspec/subject_stub.rb b/lib/rubocop/cop/rspec/subject_stub.rb index 6d061ba4f..ca5d04d6a 100644 --- a/lib/rubocop/cop/rspec/subject_stub.rb +++ b/lib/rubocop/cop/rspec/subject_stub.rb @@ -22,7 +22,7 @@ module RSpec # end # class SubjectStub < Base - include RuboCop::RSpec::TopLevelGroup + include TopLevelGroup MSG = 'Do not stub methods of the object under test.' @@ -66,7 +66,7 @@ class SubjectStub < Base (send nil? { :expect :allow } (send nil? {% :subject})) (send nil? :is_expected) } - #{Runners::ALL.node_pattern_union} + #Runners.all #message_expectation_matcher? ) PATTERN diff --git a/lib/rubocop/cop/rspec/variable_definition.rb b/lib/rubocop/cop/rspec/variable_definition.rb index 91485d3f3..764168e94 100644 --- a/lib/rubocop/cop/rspec/variable_definition.rb +++ b/lib/rubocop/cop/rspec/variable_definition.rb @@ -24,7 +24,7 @@ module RSpec # let('user_name') { 'Adam' } class VariableDefinition < Base include ConfigurableEnforcedStyle - include RuboCop::RSpec::Variable + include Variable MSG = 'Use %