From 811ae08ea3a60453e18a8bcb9dfaefafd848f779 Mon Sep 17 00:00:00 2001 From: Eito Katagiri Date: Thu, 6 Feb 2020 09:10:09 +0900 Subject: [PATCH 001/972] Use relative path to match with glob --- CHANGELOG.md | 3 +++ lib/rubocop/cop/rspec/file_path.rb | 4 +++- spec/rubocop/cop/rspec/file_path_spec.rb | 9 +++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7897bccd..452db612e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Master (Unreleased) +* Fix `RSpec/FilePath` detection when absolute path includes test subject. ([@eitoball][]) + ## 1.38.1 (2020-02-15) * Fix `RSpec/RepeatedDescription` to detect descriptions with interpolation and methods. ([@lazycoder9][]) @@ -489,3 +491,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features. [@dduugg]: https://github.com/dduugg [@lazycoder9]: https://github.com/lazycoder9 [@elebow]: https://github.com/elebow +[@eitoball]: https://github.com/eitoball diff --git a/lib/rubocop/cop/rspec/file_path.rb b/lib/rubocop/cop/rspec/file_path.rb index b4d81df62..e368424ef 100644 --- a/lib/rubocop/cop/rspec/file_path.rb +++ b/lib/rubocop/cop/rspec/file_path.rb @@ -103,7 +103,9 @@ def ignore_methods? end def filename_ends_with?(glob) - File.fnmatch?("*#{glob}", processed_source.buffer.name) + filename = + RuboCop::PathUtil.relative_path(processed_source.buffer.name) + File.fnmatch?("*#{glob}", filename) end def relevant_rubocop_rspec_file?(_file) diff --git a/spec/rubocop/cop/rspec/file_path_spec.rb b/spec/rubocop/cop/rspec/file_path_spec.rb index 389474553..1e3162474 100644 --- a/spec/rubocop/cop/rspec/file_path_spec.rb +++ b/spec/rubocop/cop/rspec/file_path_spec.rb @@ -172,6 +172,15 @@ RUBY end + it 'uses relative path' do + allow(RuboCop::PathUtil) + .to receive(:relative_path).and_return('spec/models/bar_spec.rb') + expect_offense(<<-RUBY, '/home/foo/spec/models/bar_spec.rb') + describe Foo do; end + ^^^^^^^^^^^^ Spec path should end with `foo*_spec.rb`. + RUBY + end + context 'when configured with CustomTransform' do let(:cop_config) { { 'CustomTransform' => { 'FooFoo' => 'foofoo' } } } From 9aed15e76892c82ed70b96f81cf0416764056748 Mon Sep 17 00:00:00 2001 From: Yuji Hanamura Date: Tue, 25 Feb 2020 19:19:03 +0900 Subject: [PATCH 002/972] Remove already removed `AggregateFailuresByDefault` from default.yml `AggregateFailuresByDefault` option of `MultipleExpectations` has been removed in #760. --- config/default.yml | 1 - manual/cops_rspec.md | 1 - 2 files changed, 2 deletions(-) diff --git a/config/default.yml b/config/default.yml index 739e3afd4..696ebb2d8 100644 --- a/config/default.yml +++ b/config/default.yml @@ -324,7 +324,6 @@ RSpec/MultipleExpectations: Description: Checks if examples contain too many `expect` calls. Enabled: true Max: 1 - AggregateFailuresByDefault: false StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MultipleExpectations RSpec/MultipleSubjects: diff --git a/manual/cops_rspec.md b/manual/cops_rspec.md index fa2caddd7..1bafe122c 100644 --- a/manual/cops_rspec.md +++ b/manual/cops_rspec.md @@ -2058,7 +2058,6 @@ end Name | Default value | Configurable values --- | --- | --- Max | `1` | Integer -AggregateFailuresByDefault | `false` | Boolean ### References From 7aa334090da3f5cd73183dbc33dfb9568af74385 Mon Sep 17 00:00:00 2001 From: Anton Rieder Date: Tue, 24 Mar 2020 19:08:28 +0100 Subject: [PATCH 003/972] Add Capybara/VisibilityMatcher cop --- CHANGELOG.md | 2 + config/default.yml | 6 ++ .../cop/rspec/capybara/visibility_matcher.rb | 54 +++++++++++++++ lib/rubocop/cop/rspec_cops.rb | 1 + manual/cops.md | 1 + manual/cops_capybara.md | 45 ++++++++++++ .../rspec/capybara/visibility_matcher_spec.rb | 69 +++++++++++++++++++ 7 files changed, 178 insertions(+) create mode 100644 lib/rubocop/cop/rspec/capybara/visibility_matcher.rb create mode 100644 spec/rubocop/cop/rspec/capybara/visibility_matcher_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 452db612e..5ee89c633 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Master (Unreleased) * Fix `RSpec/FilePath` detection when absolute path includes test subject. ([@eitoball][]) +* Add new `Capybara/VisibilityMatcher` cop. ([@aried3r][]) ## 1.38.1 (2020-02-15) @@ -492,3 +493,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features. [@lazycoder9]: https://github.com/lazycoder9 [@elebow]: https://github.com/elebow [@eitoball]: https://github.com/eitoball +[@aried3r]: https://github.com/aried3r diff --git a/config/default.yml b/config/default.yml index 696ebb2d8..2b1e87b16 100644 --- a/config/default.yml +++ b/config/default.yml @@ -475,6 +475,12 @@ Capybara/FeatureMethods: EnabledMethods: [] StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/FeatureMethods +Capybara/VisibilityMatcher: + Description: Checks for boolean visibility in capybara finders. + Enabled: true + VersionAdded: '1.39' + StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/VisibilityMatcher + FactoryBot/AttributeDefinedStatically: Description: Always declare attribute values as blocks. Enabled: true diff --git a/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb b/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb new file mode 100644 index 000000000..35f4579e1 --- /dev/null +++ b/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module RSpec + module Capybara + # Checks for boolean visibility in capybara finders. + # + # Capybara lets you find elements that match a certain visibility using + # the `:visible` option. `:visible` accepts both boolean and symbols as + # values, however using booleans can have unwanted effects. `visible: + # false` does not find just invisible elements, but both visible and + # invisible elements. For expressiveness and clarity, use one of the + # symbol values, `:all`, `:hidden` or `:visible`. + # (https://www.rubydoc.info/gems/capybara/Capybara%2FNode%2FFinders:all) + # + # @example + # + # # bad + # expect(page).to have_selector('.foo', visible: false) + # + # # bad + # expect(page).to have_selector('.foo', visible: true) + # + # # good + # expect(page).to have_selector('.foo', visible: :all) + # + # # good + # expect(page).to have_selector('.foo', visible: :hidden) + # + # # good + # expect(page).to have_selector('.foo', visible: :visible) + # + class VisibilityMatcher < Cop + MSG_FALSE = 'Use `:all` or `:hidden` instead of `false`.' + MSG_TRUE = 'Use `:visible` instead of `true`.' + + def_node_matcher :visible_true?, <<~PATTERN + (send nil? :have_selector ... (hash <$(pair (sym :visible) true) ...>)) + PATTERN + + def_node_matcher :visible_false?, <<~PATTERN + (send nil? :have_selector ... (hash <$(pair (sym :visible) false) ...>)) + PATTERN + + def on_send(node) + visible_false?(node) { |arg| add_offense(arg, message: MSG_FALSE) } + visible_true?(node) { |arg| add_offense(arg, message: MSG_TRUE) } + end + end + end + end + end +end diff --git a/lib/rubocop/cop/rspec_cops.rb b/lib/rubocop/cop/rspec_cops.rb index 5f2a1719b..f499949a6 100644 --- a/lib/rubocop/cop/rspec_cops.rb +++ b/lib/rubocop/cop/rspec_cops.rb @@ -2,6 +2,7 @@ require_relative 'rspec/capybara/current_path_expectation' require_relative 'rspec/capybara/feature_methods' +require_relative 'rspec/capybara/visibility_matcher' require_relative 'rspec/factory_bot/attribute_defined_statically' require_relative 'rspec/factory_bot/create_list' diff --git a/manual/cops.md b/manual/cops.md index a5a62faad..f20653afe 100644 --- a/manual/cops.md +++ b/manual/cops.md @@ -3,6 +3,7 @@ * [Capybara/CurrentPathExpectation](cops_capybara.md#capybaracurrentpathexpectation) * [Capybara/FeatureMethods](cops_capybara.md#capybarafeaturemethods) +* [Capybara/VisibilityMatcher](cops_capybara.md#capybaravisibilitymatcher) #### Department [FactoryBot](cops_factorybot.md) diff --git a/manual/cops_capybara.md b/manual/cops_capybara.md index c4f60f9cb..53922f6d0 100644 --- a/manual/cops_capybara.md +++ b/manual/cops_capybara.md @@ -87,3 +87,48 @@ EnabledMethods | `[]` | Array ### References * [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/FeatureMethods](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/FeatureMethods) + +## Capybara/VisibilityMatcher + +Enabled by default | Supports autocorrection +--- | --- +Enabled | No + +Checks for boolean visibility in capybara finders. + +Capybara lets you find elements that match a certain visibility using +the `:visible` option. `:visible` accepts both boolean and symbols as +values, however using booleans can have unwanted effects. `visible: +false` does not find just invisible elements, but both visible and +invisible elements. For expressiveness and clarity, use one of the +symbol values, `:all`, `:hidden` or `:visible`. +(https://www.rubydoc.info/gems/capybara/Capybara%2FNode%2FFinders:all) + +### Examples + +```ruby +# bad +expect(page).to have_selector('.foo', visible: false) + +# bad +expect(page).to have_selector('.foo', visible: true) + +# good +expect(page).to have_selector('.foo', visible: :all) + +# good +expect(page).to have_selector('.foo', visible: :hidden) + +# good +expect(page).to have_selector('.foo', visible: :visible) +``` + +### Configurable attributes + +Name | Default value | Configurable values +--- | --- | --- +VersionAdded | `1.39` | String + +### References + +* [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/VisibilityMatcher](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/VisibilityMatcher) diff --git a/spec/rubocop/cop/rspec/capybara/visibility_matcher_spec.rb b/spec/rubocop/cop/rspec/capybara/visibility_matcher_spec.rb new file mode 100644 index 000000000..411eddee4 --- /dev/null +++ b/spec/rubocop/cop/rspec/capybara/visibility_matcher_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::RSpec::Capybara::VisibilityMatcher do + subject(:cop) { described_class.new } + + it 'registers an offense when using `visible: true`' do + expect_offense(<<-RUBY) + expect(page).to have_selector('.my_element', visible: true) + ^^^^^^^^^^^^^ Use `:visible` instead of `true`. + RUBY + end + + it 'registers an offense when using `visible: false`' do + expect_offense(<<-RUBY) + expect(page).to have_selector('.my_element', visible: false) + ^^^^^^^^^^^^^^ Use `:all` or `:hidden` instead of `false`. + RUBY + end + + it 'registers an offense when using a selector`' do + expect_offense(<<-RUBY) + expect(page).to have_selector(:css, '.my_element', visible: false) + ^^^^^^^^^^^^^^ Use `:all` or `:hidden` instead of `false`. + RUBY + end + + it 'registers an offense when using a using multiple options`' do + expect_offense(<<-RUBY) + expect(page).to have_selector('.my_element', count: 1, visible: false, normalize_ws: true) + ^^^^^^^^^^^^^^ Use `:all` or `:hidden` instead of `false`. + RUBY + end + + it 'does not register an offense when no options are given`' do + expect_no_offenses(<<~RUBY) + expect(page).to have_selector('.my_element') + RUBY + end + + it 'does not register an offense when using `visible: :all`' do + expect_no_offenses(<<~RUBY) + expect(page).to have_selector('.my_element', visible: :all) + RUBY + end + + it 'does not register an offense when using `visible: :visible`' do + expect_no_offenses(<<~RUBY) + expect(page).to have_selector('.my_element', visible: :visible) + RUBY + end + + it 'does not register an offense when using `visible: :hidden`' do + expect_no_offenses(<<~RUBY) + expect(page).to have_selector('.my_element', visible: :hidden) + RUBY + end + + it 'does not register an offense when using other options' do + expect_no_offenses(<<~RUBY) + expect(page).to have_selector('.my_element', normalize_ws: true) + RUBY + end + + it 'does not register an offense when using multiple options' do + expect_no_offenses(<<~RUBY) + expect(page).to have_selector('.my_element', count: 1, normalize_ws: true) + RUBY + end +end From 478ab25a1d07af02bed6e4cc1c7c385cc74534e5 Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Wed, 1 Apr 2020 17:25:54 +0900 Subject: [PATCH 004/972] Fix an offenses when using RuboCop 0.81.0 RuboCop 0.81 has been released. https://github.com/rubocop-hq/rubocop/releases/tag/v0.81.0 This PR fixes the following offenses when using RuboCop 0.81.0. ```console % cd path/to/rubocop-rspec % bundle exec rake internal_investigation (snip) Offenses: lib/rubocop/cop/rspec/cop.rb:4:14: W: Lint/RedundantCopDisableDirective: Unnecessary disabling of Style/Documentation. module Cop # rubocop:disable Style/Documentation ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lib/rubocop/cop/rspec_cops.rb:12:18: W: Lint/RedundantCopDisableDirective: Unnecessary disabling of Lint/SuppressedException. rescue LoadError # rubocop:disable Lint/SuppressedException ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 202 files inspected, 2 offenses detected rake aborted! Command failed with status (1): [bundle exec rubocop --require rubocop-rspe...] /Users/koic/src/github.com/rubocop-hq/rubocop-rspec/Rakefile:32:in `block in ' /Users/koic/.rbenv/versions/2.7.1/bin/bundle:23:in `load' /Users/koic/.rbenv/versions/2.7.1/bin/bundle:23:in `
' Tasks: TOP => internal_investigation (See full trace by running task with --trace) ``` https://circleci.com/gh/rubocop-hq/rubocop-rspec/11350 --- lib/rubocop/cop/rspec/cop.rb | 2 +- lib/rubocop/cop/rspec_cops.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rubocop/cop/rspec/cop.rb b/lib/rubocop/cop/rspec/cop.rb index 636333212..fa8e30427 100644 --- a/lib/rubocop/cop/rspec/cop.rb +++ b/lib/rubocop/cop/rspec/cop.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module RuboCop - module Cop # rubocop:disable Style/Documentation + module Cop WorkaroundCop = Cop.dup # Clone of the the normal RuboCop::Cop::Cop class so we can rewrite diff --git a/lib/rubocop/cop/rspec_cops.rb b/lib/rubocop/cop/rspec_cops.rb index 5f2a1719b..c74a8e1f1 100644 --- a/lib/rubocop/cop/rspec_cops.rb +++ b/lib/rubocop/cop/rspec_cops.rb @@ -9,7 +9,7 @@ begin require_relative 'rspec/rails/http_status' -rescue LoadError # rubocop:disable Lint/SuppressedException +rescue LoadError # Rails/HttpStatus cannot be loaded if rack/utils is unavailable. end From 21610c0ba903fd7d67aa17e4de2d69ea31472ef8 Mon Sep 17 00:00:00 2001 From: Alexander Popov Date: Fri, 3 Apr 2020 01:30:59 +0300 Subject: [PATCH 005/972] Ignore String constants by `RSpec/Describe` --- .github/PULL_REQUEST_TEMPLATE.md | 1 + CHANGELOG.md | 2 + lib/rubocop/cop/rspec/describe_class.rb | 17 ++++- manual/cops_rspec.md | 5 ++ spec/rubocop/cop/rspec/describe_class_spec.rb | 69 +++++++++++++++++++ 5 files changed, 92 insertions(+), 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 3b40d7539..8b3935da0 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -7,6 +7,7 @@ Before submitting the PR make sure the following are checked: * [ ] Feature branch is up-to-date with `master` (if not - rebase it). * [ ] Squashed related commits together. * [ ] Added tests. +* [ ] Updated documentation. * [ ] Added an entry to the [changelog](https://github.com/rubocop-hq/rubocop-rspec/blob/master/CHANGELOG.md) if the new code introduces user-observable changes. * [ ] The build (`bundle exec rake`) passes (be sure to run this locally, since it may produce updated documentation that you will need to commit). diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ee89c633..ddb9c35e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Fix `RSpec/FilePath` detection when absolute path includes test subject. ([@eitoball][]) * Add new `Capybara/VisibilityMatcher` cop. ([@aried3r][]) +* Ignore String constants by `RSpec/Describe`. ([@AlexWayfer][]) ## 1.38.1 (2020-02-15) @@ -494,3 +495,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features. [@elebow]: https://github.com/elebow [@eitoball]: https://github.com/eitoball [@aried3r]: https://github.com/aried3r +[@AlexWayfer]: https://github.com/AlexWayfer diff --git a/lib/rubocop/cop/rspec/describe_class.rb b/lib/rubocop/cop/rspec/describe_class.rb index 101fabecb..a2f1aa06c 100644 --- a/lib/rubocop/cop/rspec/describe_class.rb +++ b/lib/rubocop/cop/rspec/describe_class.rb @@ -12,6 +12,11 @@ module RSpec # # # good # describe TestedClass do + # subject { described_class } + # end + # + # describe 'TestedClass::VERSION' do + # subject { Object.const_get(self.class.description) } # end # # describe "A feature example", type: :feature do @@ -44,12 +49,20 @@ class DescribeClass < Cop def_node_matcher :shared_group?, SharedGroups::ALL.block_pattern - def on_top_level_describe(node, args) + def on_top_level_describe(node, (described_value, _)) return if shared_group?(root_node) return if valid_describe?(node) return if describe_with_rails_metadata?(node) + return if string_constant_describe?(described_value) + + add_offense(described_value) + end + + private - add_offense(args.first) + def string_constant_describe?(described_value) + described_value.str_type? && + described_value.value =~ /^((::)?[A-Z]\w*)+$/ end end end diff --git a/manual/cops_rspec.md b/manual/cops_rspec.md index 1bafe122c..78f371f1c 100644 --- a/manual/cops_rspec.md +++ b/manual/cops_rspec.md @@ -333,6 +333,11 @@ end # good describe TestedClass do + subject { described_class } +end + +describe 'TestedClass::VERSION' do + subject { Object.const_get(self.class.description) } end describe "A feature example", type: :feature do diff --git a/spec/rubocop/cop/rspec/describe_class_spec.rb b/spec/rubocop/cop/rspec/describe_class_spec.rb index 1521c0737..04d17bb80 100644 --- a/spec/rubocop/cop/rspec/describe_class_spec.rb +++ b/spec/rubocop/cop/rspec/describe_class_spec.rb @@ -51,6 +51,75 @@ RUBY end + context 'when argument is a String literal' do + it 'ignores class without namespace' do + expect_no_offenses(<<-RUBY) + describe 'Thing' do + subject { Object.const_get(self.class.description) } + end + RUBY + end + + it 'ignores class with namespace' do + expect_no_offenses(<<-RUBY) + describe 'Some::Thing' do + subject { Object.const_get(self.class.description) } + end + RUBY + end + + it 'ignores value constants' do + expect_no_offenses(<<-RUBY) + describe 'VERSION' do + subject { Object.const_get(self.class.description) } + end + RUBY + end + + it 'ignores value constants with namespace' do + expect_no_offenses(<<-RUBY) + describe 'Some::VERSION' do + subject { Object.const_get(self.class.description) } + end + RUBY + end + + it 'ignores top-level constants with `::` at start' do + expect_no_offenses(<<-RUBY) + describe '::Some::VERSION' do + subject { Object.const_get(self.class.description) } + end + RUBY + end + + it 'checks `camelCase`' do + expect_offense(<<-RUBY) + describe 'activeRecord' do + ^^^^^^^^^^^^^^ The first argument to describe should be the class or module being tested. + subject { Object.const_get(self.class.description) } + end + RUBY + end + + it 'checks numbers at start' do + expect_offense(<<-RUBY) + describe '2Thing' do + ^^^^^^^^ The first argument to describe should be the class or module being tested. + subject { Object.const_get(self.class.description) } + end + RUBY + end + + it 'checks empty strings' do + expect_offense(<<-RUBY) + describe '' do + ^^ The first argument to describe should be the class or module being tested. + subject { Object.const_get(self.class.description) } + end + RUBY + end + end + it 'ignores request specs' do expect_no_offenses(<<-RUBY) describe 'my new feature', type: :request do From 3038e0faaed2cc9e28a4acfdd2f922892d4f88e3 Mon Sep 17 00:00:00 2001 From: Benjamin Quorning Date: Mon, 27 Apr 2020 22:05:55 +0200 Subject: [PATCH 006/972] Drop support for Ruby 2.3 RuboCop has dropped support for Ruby 2.3 (https://github.com/rubocop-hq/rubocop/commit/58738943eb3f7a41266b4546a829fd0a5a13703d) and so should we. --- .circleci/config.yml | 14 -------------- .rubocop.yml | 2 +- CHANGELOG.md | 1 + rubocop-rspec.gemspec | 2 +- 4 files changed, 3 insertions(+), 16 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f1fb38f7d..12245ac36 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,16 +21,6 @@ jobs: - run: bundle install - run: rake confirm_config documentation_syntax_check confirm_documentation - # Ruby 2.3 - ruby-2.3-rspec: - docker: - - image: circleci/ruby:2.3 - <<: *rspec - ruby-2.3-rubocop: - docker: - - image: circleci/ruby:2.3 - <<: *rubocop - # Ruby 2.4 ruby-2.4-rspec: docker: @@ -128,10 +118,6 @@ workflows: - confirm_config_and_documentation # Use `requires: [confirm_config_and_documentation]` to trick Circle CI into starting the slow jruby job early. - - ruby-2.3-rspec: - requires: [confirm_config_and_documentation] - - ruby-2.3-rubocop: - requires: [confirm_config_and_documentation] - ruby-2.4-rspec: requires: [confirm_config_and_documentation] - ruby-2.4-rubocop: diff --git a/.rubocop.yml b/.rubocop.yml index a0b9122b9..0847a8660 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,7 +5,7 @@ require: AllCops: DisplayCopNames: true - TargetRubyVersion: 2.3 + TargetRubyVersion: 2.4 Exclude: - 'vendor/**/*' - 'spec/fixtures/**/*' diff --git a/CHANGELOG.md b/CHANGELOG.md index ddb9c35e0..b5a0d3230 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Fix `RSpec/FilePath` detection when absolute path includes test subject. ([@eitoball][]) * Add new `Capybara/VisibilityMatcher` cop. ([@aried3r][]) * Ignore String constants by `RSpec/Describe`. ([@AlexWayfer][]) +* Drop support for ruby 2.3. ([@bquorning][]) ## 1.38.1 (2020-02-15) diff --git a/rubocop-rspec.gemspec b/rubocop-rspec.gemspec index c98ce4a1f..48af29b11 100644 --- a/rubocop-rspec.gemspec +++ b/rubocop-rspec.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| spec.version = RuboCop::RSpec::Version::STRING spec.platform = Gem::Platform::RUBY - spec.required_ruby_version = '>= 2.3.0' + spec.required_ruby_version = '>= 2.4.0' spec.require_paths = ['lib'] spec.files = Dir[ From 5ee222c40ea18964f524c3d9f183b98d600b37b7 Mon Sep 17 00:00:00 2001 From: Benjamin Quorning Date: Mon, 27 Apr 2020 21:43:25 +0200 Subject: [PATCH 007/972] Use CircleCI matrix feature CircleCI recently added a feature for a workflow to configure a matrix of jobs to run. This allows us to remove a lot of duplicated configuration. --- .circleci/config.yml | 90 +++++++++++++------------------------------- 1 file changed, 27 insertions(+), 63 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 12245ac36..14a81454c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,16 +1,4 @@ -version: 2 - -rspec: &rspec - steps: - - checkout - - run: bundle install - - run: rake spec - -rubocop: &rubocop - steps: - - checkout - - run: bundle install - - run: rake internal_investigation +version: 2.1 jobs: confirm_config_and_documentation: @@ -21,45 +9,27 @@ jobs: - run: bundle install - run: rake confirm_config documentation_syntax_check confirm_documentation - # Ruby 2.4 - ruby-2.4-rspec: - docker: - - image: circleci/ruby:2.4 - <<: *rspec - ruby-2.4-rubocop: - docker: - - image: circleci/ruby:2.4 - <<: *rubocop - - # Ruby 2.5 - ruby-2.5-rspec: - docker: - - image: circleci/ruby:2.5 - <<: *rspec - ruby-2.5-rubocop: - docker: - - image: circleci/ruby:2.5 - <<: *rubocop - - # Ruby 2.6 - ruby-2.6-rspec: - docker: - - image: circleci/ruby:2.6 - <<: *rspec - ruby-2.6-rubocop: + rspec: + parameters: + ruby: + type: string docker: - - image: circleci/ruby:2.6 - <<: *rubocop + - image: circleci/ruby:<> + steps: + - checkout + - run: bundle install + - run: rake spec - # Ruby 2.7 - ruby-2.7-rspec: - docker: - - image: circleci/ruby:2.7 - <<: *rspec - ruby-2.7-rubocop: + rubocop: + parameters: + ruby: + type: string docker: - - image: circleci/ruby:2.7 - <<: *rubocop + - image: circleci/ruby:<> + steps: + - checkout + - run: bundle install + - run: rake internal_investigation edge-rubocop: docker: @@ -118,22 +88,16 @@ workflows: - confirm_config_and_documentation # Use `requires: [confirm_config_and_documentation]` to trick Circle CI into starting the slow jruby job early. - - ruby-2.4-rspec: - requires: [confirm_config_and_documentation] - - ruby-2.4-rubocop: - requires: [confirm_config_and_documentation] - - ruby-2.5-rspec: - requires: [confirm_config_and_documentation] - - ruby-2.5-rubocop: - requires: [confirm_config_and_documentation] - - ruby-2.6-rspec: - requires: [confirm_config_and_documentation] - - ruby-2.6-rubocop: - requires: [confirm_config_and_documentation] - - ruby-2.7-rspec: + - rspec: requires: [confirm_config_and_documentation] - - ruby-2.7-rubocop: + matrix: + parameters: + ruby: ['2.4', '2.5', '2.6', '2.7'] + - rubocop: requires: [confirm_config_and_documentation] + matrix: + parameters: + ruby: ['2.4', '2.5', '2.6', '2.7'] - edge-rubocop: requires: [confirm_config_and_documentation] - jruby From ff6b291101e490f14988913c1252064b3121d5b2 Mon Sep 17 00:00:00 2001 From: Nastia Gorokhova-Alekseeva Date: Mon, 27 Apr 2020 20:41:57 +0300 Subject: [PATCH 008/972] Replace `should`-example with `expect`-example --- lib/rubocop/cop/rspec/named_subject.rb | 12 ++++++------ manual/cops_rspec.md | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/rubocop/cop/rspec/named_subject.rb b/lib/rubocop/cop/rspec/named_subject.rb index ed12f133a..ac93b7124 100644 --- a/lib/rubocop/cop/rspec/named_subject.rb +++ b/lib/rubocop/cop/rspec/named_subject.rb @@ -6,11 +6,11 @@ module RSpec # Checks for explicitly referenced test subjects. # # RSpec lets you declare an "implicit subject" using `subject { ... }` - # which allows for tests like `it { should be_valid }`. If you need to - # reference your test subject you should explicitly name it using - # `subject(:your_subject_name) { ... }`. Your test subjects should be - # the most important object in your tests so they deserve a descriptive - # name. + # which allows for tests like `it { is_expected.to be_valid }`. + # If you need to reference your test subject you should explicitly + # name it using `subject(:your_subject_name) { ... }`. Your test subjects + # should be the most important object in your tests so they deserve + # a descriptive name. # # This cop can be configured in your configuration using the # `IgnoreSharedExamples` which will not report offenses for implicit @@ -39,7 +39,7 @@ module RSpec # RSpec.describe Foo do # subject(:user) { described_class.new } # - # it { should be_valid } + # it { is_expected.to be_valid } # end class NamedSubject < Cop MSG = 'Name your test subject if you need '\ diff --git a/manual/cops_rspec.md b/manual/cops_rspec.md index 78f371f1c..ca6524bee 100644 --- a/manual/cops_rspec.md +++ b/manual/cops_rspec.md @@ -2120,11 +2120,11 @@ Enabled | No Checks for explicitly referenced test subjects. RSpec lets you declare an "implicit subject" using `subject { ... }` -which allows for tests like `it { should be_valid }`. If you need to -reference your test subject you should explicitly name it using -`subject(:your_subject_name) { ... }`. Your test subjects should be -the most important object in your tests so they deserve a descriptive -name. +which allows for tests like `it { is_expected.to be_valid }`. +If you need to reference your test subject you should explicitly +name it using `subject(:your_subject_name) { ... }`. Your test subjects +should be the most important object in your tests so they deserve +a descriptive name. This cop can be configured in your configuration using the `IgnoreSharedExamples` which will not report offenses for implicit @@ -2155,7 +2155,7 @@ end RSpec.describe Foo do subject(:user) { described_class.new } - it { should be_valid } + it { is_expected.to be_valid } end ``` From 7e046a0ac79f1f82b77687e073c51b98c71460ed Mon Sep 17 00:00:00 2001 From: Tejas Bubane Date: Wed, 29 Apr 2020 13:27:52 +0530 Subject: [PATCH 009/972] Fix multiple cops to detect `let` with proc argument Affected cops: 1. `RSpec/ScatteredLet` 2. `RSpec/AlignLeftLetBrace` 3. `RSpec/AlignRightLetBrace` 4. `RSpec/LeadingSubject` 5. `RSpec/EmptyLineAfterFinalLet` 6. `RSpec/LetBeforeExamples` Closes #771 --- CHANGELOG.md | 2 ++ lib/rubocop/rspec/language.rb | 8 ++++++++ lib/rubocop/rspec/language/node_pattern.rb | 2 +- .../cop/rspec/align_left_let_brace_spec.rb | 6 ++++++ .../cop/rspec/align_right_let_brace_spec.rb | 6 ++++++ .../rspec/empty_line_after_final_let_spec.rb | 20 +++++++++++++++++++ .../rubocop/cop/rspec/leading_subject_spec.rb | 19 ++++++++++++++++++ .../cop/rspec/let_before_examples_spec.rb | 19 ++++++++++++++++++ spec/rubocop/cop/rspec/scattered_let_spec.rb | 11 ++++++++++ 9 files changed, 92 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5a0d3230..606fedadc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Add new `Capybara/VisibilityMatcher` cop. ([@aried3r][]) * Ignore String constants by `RSpec/Describe`. ([@AlexWayfer][]) * Drop support for ruby 2.3. ([@bquorning][]) +* Fix multiple cops to detect `let` with proc argument. ([@tejasbubane][]) ## 1.38.1 (2020-02-15) @@ -497,3 +498,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features. [@eitoball]: https://github.com/eitoball [@aried3r]: https://github.com/aried3r [@AlexWayfer]: https://github.com/AlexWayfer +[@tejasbubane]: https://github.com/tejasbubane diff --git a/lib/rubocop/rspec/language.rb b/lib/rubocop/rspec/language.rb index 60e182a82..1e5ef49db 100644 --- a/lib/rubocop/rspec/language.rb +++ b/lib/rubocop/rspec/language.rb @@ -28,6 +28,14 @@ def block_pattern "(block #{send_pattern} ...)" end + def block_pass_pattern + "(send #{RSPEC} #{node_pattern_union} _ block_pass)" + end + + def block_or_block_pass_pattern + "{#{block_pattern} #{block_pass_pattern}}" + end + def send_pattern "(send #{RSPEC} #{node_pattern_union} ...)" end diff --git a/lib/rubocop/rspec/language/node_pattern.rb b/lib/rubocop/rspec/language/node_pattern.rb index 63378fd42..665b7ad70 100644 --- a/lib/rubocop/rspec/language/node_pattern.rb +++ b/lib/rubocop/rspec/language/node_pattern.rb @@ -17,7 +17,7 @@ module NodePattern def_node_matcher :hook?, Hooks::ALL.block_pattern - def_node_matcher :let?, Helpers::ALL.block_pattern + def_node_matcher :let?, Helpers::ALL.block_or_block_pass_pattern def_node_matcher :subject?, Subject::ALL.block_pattern end diff --git a/spec/rubocop/cop/rspec/align_left_let_brace_spec.rb b/spec/rubocop/cop/rspec/align_left_let_brace_spec.rb index e923f8003..67106a4d2 100644 --- a/spec/rubocop/cop/rspec/align_left_let_brace_spec.rb +++ b/spec/rubocop/cop/rspec/align_left_let_brace_spec.rb @@ -45,6 +45,12 @@ end # rubocop:enable RSpec/ExampleLength + it 'does not register offense for let with proc argument' do + expect_no_offenses(<<-RUBY) + let(:user, &args[:build_user]) + RUBY + end + it 'works with empty file' do expect_no_offenses('') end diff --git a/spec/rubocop/cop/rspec/align_right_let_brace_spec.rb b/spec/rubocop/cop/rspec/align_right_let_brace_spec.rb index 6a2c0028a..1fa350c6b 100644 --- a/spec/rubocop/cop/rspec/align_right_let_brace_spec.rb +++ b/spec/rubocop/cop/rspec/align_right_let_brace_spec.rb @@ -45,6 +45,12 @@ end # rubocop:enable RSpec/ExampleLength + it 'does not register offense for let with proc argument' do + expect_no_offenses(<<-RUBY) + let(:user, &args[:build_user]) + RUBY + end + it 'works with empty file' do expect_no_offenses('') end diff --git a/spec/rubocop/cop/rspec/empty_line_after_final_let_spec.rb b/spec/rubocop/cop/rspec/empty_line_after_final_let_spec.rb index d43a2fea3..777b6f510 100644 --- a/spec/rubocop/cop/rspec/empty_line_after_final_let_spec.rb +++ b/spec/rubocop/cop/rspec/empty_line_after_final_let_spec.rb @@ -47,6 +47,26 @@ RUBY end + it 'checks for empty line after let with proc argument' do + expect_offense(<<-RUBY) + RSpec.describe User do + let(:a) { a } + let(:user, &args[:build_user]) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Add an empty line after the last `let` block. + it { expect(a).to eq(b) } + end + RUBY + + expect_correction(<<-RUBY) + RSpec.describe User do + let(:a) { a } + let(:user, &args[:build_user]) + + it { expect(a).to eq(b) } + end + RUBY + end + it 'approves empty line after let' do expect_no_offenses(<<-RUBY) RSpec.describe User do diff --git a/spec/rubocop/cop/rspec/leading_subject_spec.rb b/spec/rubocop/cop/rspec/leading_subject_spec.rb index 20c6fe72a..d214b3e76 100644 --- a/spec/rubocop/cop/rspec/leading_subject_spec.rb +++ b/spec/rubocop/cop/rspec/leading_subject_spec.rb @@ -41,6 +41,25 @@ RUBY end + it 'checks subject below let with proc argument' do + expect_offense(<<-RUBY) + RSpec.describe User do + let(:user, &args[:build_user]) + + subject { described_class.new } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Declare `subject` above any other `let` declarations. + end + RUBY + + expect_correction(<<-RUBY) + RSpec.describe User do + subject { described_class.new } + let(:user, &args[:build_user]) + + end + RUBY + end + it 'approves of subject above let' do expect_no_offenses(<<-RUBY) RSpec.describe User do diff --git a/spec/rubocop/cop/rspec/let_before_examples_spec.rb b/spec/rubocop/cop/rspec/let_before_examples_spec.rb index a2a1077b0..1fc106d37 100644 --- a/spec/rubocop/cop/rspec/let_before_examples_spec.rb +++ b/spec/rubocop/cop/rspec/let_before_examples_spec.rb @@ -62,6 +62,25 @@ RUBY end + it 'flags `let` with proc argument' do + expect_offense(<<-RUBY) + RSpec.describe User do + include_examples('should be after let') + + let(:user, &args[:build_user]) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Move `let` before the examples in the group. + end + RUBY + + expect_correction(<<-RUBY) + RSpec.describe User do + let(:user, &args[:build_user]) + include_examples('should be after let') + + end + RUBY + end + it 'does not flag `let` before the examples' do expect_no_offenses(<<-RUBY) RSpec.describe User do diff --git a/spec/rubocop/cop/rspec/scattered_let_spec.rb b/spec/rubocop/cop/rspec/scattered_let_spec.rb index 09ecb4cf0..9044b5bc6 100644 --- a/spec/rubocop/cop/rspec/scattered_let_spec.rb +++ b/spec/rubocop/cop/rspec/scattered_let_spec.rb @@ -25,4 +25,15 @@ end RUBY end + + it 'flags `let` with proc argument' do + expect_offense(<<-RUBY) + RSpec.describe User do + let(:a) { a } + subject { User } + let(:user, &args[:build_user]) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Group all let/let! blocks in the example group together. + end + RUBY + end end From 7615d62a4bc8baca7e31a2300a46ecbe3381a4cb Mon Sep 17 00:00:00 2001 From: Maxim Krizhanovski Date: Tue, 28 Apr 2020 15:39:19 +0300 Subject: [PATCH 010/972] Extract helper class for moving nodes arround --- lib/rubocop-rspec.rb | 1 + .../cop/rspec/hooks_before_examples.rb | 24 ++------- lib/rubocop/cop/rspec/leading_subject.rb | 13 ++--- lib/rubocop/cop/rspec/let_before_examples.rb | 24 ++------- lib/rubocop/rspec/corrector/move_node.rb | 52 +++++++++++++++++++ 5 files changed, 62 insertions(+), 52 deletions(-) create mode 100644 lib/rubocop/rspec/corrector/move_node.rb diff --git a/lib/rubocop-rspec.rb b/lib/rubocop-rspec.rb index 840b3a5a6..9aa56c6f2 100644 --- a/lib/rubocop-rspec.rb +++ b/lib/rubocop-rspec.rb @@ -22,6 +22,7 @@ require_relative 'rubocop/rspec/factory_bot' require_relative 'rubocop/rspec/final_end_location' require_relative 'rubocop/rspec/blank_line_separation' +require_relative 'rubocop/rspec/corrector/move_node' RuboCop::RSpec::Inject.defaults! diff --git a/lib/rubocop/cop/rspec/hooks_before_examples.rb b/lib/rubocop/cop/rspec/hooks_before_examples.rb index 0d4b94500..1c924da42 100644 --- a/lib/rubocop/cop/rspec/hooks_before_examples.rb +++ b/lib/rubocop/cop/rspec/hooks_before_examples.rb @@ -24,9 +24,6 @@ module RSpec # end # class HooksBeforeExamples < Cop - include RangeHelp - include RuboCop::RSpec::FinalEndLocation - MSG = 'Move `%s` above the examples in the group.' def_node_matcher :example_or_group?, <<-PATTERN @@ -45,11 +42,9 @@ def on_block(node) def autocorrect(node) lambda do |corrector| first_example = find_first_example(node.parent) - first_example_pos = first_example.loc.expression - indent = "\n" + ' ' * first_example.loc.column - - corrector.insert_before(first_example_pos, source(node) + indent) - corrector.remove(node_range_with_surrounding_space(node)) + RuboCop::RSpec::Corrector::MoveNode.new( + node, corrector, processed_source + ).move_before(first_example) end end @@ -77,19 +72,6 @@ def check_hooks(node) def find_first_example(node) node.children.find { |sibling| example_or_group?(sibling) } end - - def node_range_with_surrounding_space(node) - range = node_range(node) - range_by_whole_lines(range, include_final_newline: true) - end - - def source(node) - node_range(node).source - end - - def node_range(node) - node.loc.expression.with(end_pos: final_end_location(node).end_pos) - end end end end diff --git a/lib/rubocop/cop/rspec/leading_subject.rb b/lib/rubocop/cop/rspec/leading_subject.rb index 513163685..6176f5c58 100644 --- a/lib/rubocop/cop/rspec/leading_subject.rb +++ b/lib/rubocop/cop/rspec/leading_subject.rb @@ -32,8 +32,6 @@ module RSpec # it { expect_something_else } # class LeadingSubject < Cop - include RangeHelp - MSG = 'Declare `subject` above any other `%s` declarations.' def on_block(node) @@ -58,10 +56,9 @@ def check_previous_nodes(node) def autocorrect(node) lambda do |corrector| first_node = find_first_offending_node(node) - first_node_position = first_node.loc.expression - indent = "\n" + ' ' * first_node.loc.column - corrector.insert_before(first_node_position, node.source + indent) - corrector.remove(node_range(node)) + RuboCop::RSpec::Corrector::MoveNode.new( + node, corrector, processed_source + ).move_before(first_node) end end @@ -75,10 +72,6 @@ def find_first_offending_node(node) node.parent.children.find { |sibling| offending?(sibling) } end - def node_range(node) - range_by_whole_lines(node.source_range, include_final_newline: true) - end - def in_spec_block?(node) node.each_ancestor(:block).any? do |ancestor| example?(ancestor) diff --git a/lib/rubocop/cop/rspec/let_before_examples.rb b/lib/rubocop/cop/rspec/let_before_examples.rb index 5efb30d32..c41b60add 100644 --- a/lib/rubocop/cop/rspec/let_before_examples.rb +++ b/lib/rubocop/cop/rspec/let_before_examples.rb @@ -31,9 +31,6 @@ module RSpec # expect(some).to be # end class LetBeforeExamples < Cop - include RangeHelp - include RuboCop::RSpec::FinalEndLocation - MSG = 'Move `let` before the examples in the group.' def_node_matcher :example_or_group?, <<-PATTERN @@ -52,11 +49,9 @@ def on_block(node) def autocorrect(node) lambda do |corrector| first_example = find_first_example(node.parent) - first_example_pos = first_example.loc.expression - indent = "\n" + ' ' * first_example.loc.column - - corrector.insert_before(first_example_pos, source(node) + indent) - corrector.remove(node_range_with_surrounding_space(node)) + RuboCop::RSpec::Corrector::MoveNode.new( + node, corrector, processed_source + ).move_before(first_example) end end @@ -80,19 +75,6 @@ def check_let_declarations(node) def find_first_example(node) node.children.find { |sibling| example_or_group?(sibling) } end - - def node_range_with_surrounding_space(node) - range = node_range(node) - range_by_whole_lines(range, include_final_newline: true) - end - - def source(node) - node_range(node).source - end - - def node_range(node) - node.loc.expression.with(end_pos: final_end_location(node).end_pos) - end end end end diff --git a/lib/rubocop/rspec/corrector/move_node.rb b/lib/rubocop/rspec/corrector/move_node.rb new file mode 100644 index 000000000..d85ff6c70 --- /dev/null +++ b/lib/rubocop/rspec/corrector/move_node.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module RuboCop + module RSpec + module Corrector + # Helper methods to move a node + class MoveNode + include RuboCop::Cop::RangeHelp + include RuboCop::RSpec::FinalEndLocation + + attr_reader :original, :corrector, :processed_source + + def initialize(node, corrector, processed_source) + @original = node + @corrector = corrector + @processed_source = processed_source # used by RangeHelp + end + + def move_before(other) # rubocop:disable Metrics/AbcSize + position = other.loc.expression + indent = "\n" + ' ' * other.loc.column + + corrector.insert_before(position, source(original) + indent) + corrector.remove(node_range_with_surrounding_space(original)) + end + + def move_after(other) + position = final_end_location(other) + indent = "\n" + ' ' * other.loc.column + + corrector.insert_after(position, indent + source(original)) + corrector.remove(node_range_with_surrounding_space(original)) + end + + private + + def source(node) + node_range(node).source + end + + def node_range(node) + node.loc.expression.with(end_pos: final_end_location(node).end_pos) + end + + def node_range_with_surrounding_space(node) + range = node_range(node) + range_by_whole_lines(range, include_final_newline: true) + end + end + end + end +end From 9941338ccbdb945e04f7d6c5031ce09e64f25f0a Mon Sep 17 00:00:00 2001 From: Maxim Krizhanovski Date: Tue, 28 Apr 2020 15:39:38 +0300 Subject: [PATCH 011/972] Implement auto-correct for ScatteredLet cop --- CHANGELOG.md | 1 + config/default.yml | 1 + lib/rubocop/cop/rspec/scattered_let.rb | 13 +++ manual/cops_rspec.md | 8 +- spec/rubocop/cop/rspec/scattered_let_spec.rb | 105 ++++++++++++++++++- 5 files changed, 123 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 606fedadc..5a75fd429 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Ignore String constants by `RSpec/Describe`. ([@AlexWayfer][]) * Drop support for ruby 2.3. ([@bquorning][]) * Fix multiple cops to detect `let` with proc argument. ([@tejasbubane][]) +* Add autocorrect support for `RSpec/ScatteredLet`. ([@Darhazer][]) ## 1.38.1 (2020-02-15) diff --git a/config/default.yml b/config/default.yml index 2b1e87b16..7a82d7bda 100644 --- a/config/default.yml +++ b/config/default.yml @@ -416,6 +416,7 @@ RSpec/ScatteredLet: Description: Checks for let scattered across the example group. Enabled: true StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ScatteredLet + VersionChanged: '1.39' RSpec/ScatteredSetup: Description: Checks for setup scattered across multiple hooks in an example group. diff --git a/lib/rubocop/cop/rspec/scattered_let.rb b/lib/rubocop/cop/rspec/scattered_let.rb index b707e048c..07c88c6e1 100644 --- a/lib/rubocop/cop/rspec/scattered_let.rb +++ b/lib/rubocop/cop/rspec/scattered_let.rb @@ -35,6 +35,15 @@ def on_block(node) check_let_declarations(node.body) end + def autocorrect(node) + lambda do |corrector| + first_let = find_first_let(node.parent) + RuboCop::RSpec::Corrector::MoveNode.new( + node, corrector, processed_source + ).move_after(first_let) + end + end + private def check_let_declarations(body) @@ -47,6 +56,10 @@ def check_let_declarations(body) add_offense(node) end end + + def find_first_let(node) + node.children.find { |child| let?(child) } + end end end end diff --git a/manual/cops_rspec.md b/manual/cops_rspec.md index ca6524bee..ddf21a531 100644 --- a/manual/cops_rspec.md +++ b/manual/cops_rspec.md @@ -2723,7 +2723,7 @@ EnforcedStyle | `and_return` | `and_return`, `block` Enabled by default | Supports autocorrection --- | --- -Enabled | No +Enabled | Yes Checks for let scattered across the example group. @@ -2751,6 +2751,12 @@ describe Foo do end ``` +### Configurable attributes + +Name | Default value | Configurable values +--- | --- | --- +VersionChanged | `1.39` | String + ### References * [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ScatteredLet](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ScatteredLet) diff --git a/spec/rubocop/cop/rspec/scattered_let_spec.rb b/spec/rubocop/cop/rspec/scattered_let_spec.rb index 9044b5bc6..f496b41cb 100644 --- a/spec/rubocop/cop/rspec/scattered_let_spec.rb +++ b/spec/rubocop/cop/rspec/scattered_let_spec.rb @@ -7,16 +7,86 @@ expect_offense(<<-RUBY) RSpec.describe User do let(:a) { a } - subject { User } + it { expect(subject.foo).to eq(a) } let(:b) { b } ^^^^^^^^^^^^^ Group all let/let! blocks in the example group together. end RUBY + + expect_correction(<<-RUBY) + RSpec.describe User do + let(:a) { a } + let(:b) { b } + it { expect(subject.foo).to eq(a) } + end + RUBY + end + + it 'works with heredocs' do + expect_offense(<<-RUBY) + describe User do + let(:a) { <<-BAR } + hello + world + BAR + it { expect(subject.foo).to eq(a) } + let(:b) { <<-BAZ } + ^^^^^^^^^^^^^^^^^^ Group all let/let! blocks in the example group together. + again + BAZ + end + RUBY + + expect_correction(<<-RUBY) + describe User do + let(:a) { <<-BAR } + hello + world + BAR + let(:b) { <<-BAZ } + again + BAZ + it { expect(subject.foo).to eq(a) } + end + RUBY + end + + it 'flags `let` at different nesting levels' do + expect_offense(<<-RUBY) + describe User do + let(:a) { a } + it { expect(subject.foo).to eq(a) } + + describe '#property' do + let(:c) { c } + + it { expect(subject.property).to eq c } + + let(:d) { d } + ^^^^^^^^^^^^^ Group all let/let! blocks in the example group together. + end + end + RUBY + + expect_correction(<<-RUBY) + describe User do + let(:a) { a } + it { expect(subject.foo).to eq(a) } + + describe '#property' do + let(:c) { c } + let(:d) { d } + + it { expect(subject.property).to eq c } + + end + end + RUBY end it 'doesnt flag `let!` in the middle of multiple `let`s' do expect_no_offenses(<<-RUBY) - RSpec.describe User do + describe User do subject { User } let(:a) { a } @@ -26,14 +96,41 @@ RUBY end + it 'flags scattered `let!`s' do + expect_offense(<<-RUBY) + describe User do + let!(:a) { a } + it { expect(subject.foo).to eq(a) } + let!(:c) { c } + ^^^^^^^^^^^^^^ Group all let/let! blocks in the example group together. + end + RUBY + + expect_correction(<<-RUBY) + describe User do + let!(:a) { a } + let!(:c) { c } + it { expect(subject.foo).to eq(a) } + end + RUBY + end + it 'flags `let` with proc argument' do expect_offense(<<-RUBY) - RSpec.describe User do + describe User do let(:a) { a } - subject { User } + it { expect(subject.foo).to eq(a) } let(:user, &args[:build_user]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Group all let/let! blocks in the example group together. end RUBY + + expect_correction(<<-RUBY) + describe User do + let(:a) { a } + let(:user, &args[:build_user]) + it { expect(subject.foo).to eq(a) } + end + RUBY end end From 184cba78e85e36a07f60d815a649a43d9129340e Mon Sep 17 00:00:00 2001 From: Tejas Bubane Date: Mon, 20 Apr 2020 23:28:17 +0530 Subject: [PATCH 012/972] Add new `RSpec/EmptyHook` cop Closes #811 --- CHANGELOG.md | 1 + config/default.yml | 6 + lib/rubocop/cop/rspec/empty_hook.rb | 50 +++++ lib/rubocop/cop/rspec_cops.rb | 1 + manual/cops.md | 1 + manual/cops_rspec.md | 39 ++++ spec/rubocop/cop/rspec/empty_hook_spec.rb | 243 ++++++++++++++++++++++ 7 files changed, 341 insertions(+) create mode 100644 lib/rubocop/cop/rspec/empty_hook.rb create mode 100644 spec/rubocop/cop/rspec/empty_hook_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a75fd429..b3df8c0e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Drop support for ruby 2.3. ([@bquorning][]) * Fix multiple cops to detect `let` with proc argument. ([@tejasbubane][]) * Add autocorrect support for `RSpec/ScatteredLet`. ([@Darhazer][]) +* Add new `RSpec/EmptyHook` cop. ([@tejasbubane][]) ## 1.38.1 (2020-02-15) diff --git a/config/default.yml b/config/default.yml index 7a82d7bda..566d5bd56 100644 --- a/config/default.yml +++ b/config/default.yml @@ -105,6 +105,12 @@ RSpec/EmptyExampleGroup: CustomIncludeMethods: [] StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyExampleGroup +RSpec/EmptyHook: + Description: Checks for empty before and after hooks. + Enabled: true + VersionAdded: 1.39.0 + StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyHook + RSpec/EmptyLineAfterExample: Description: Checks if there is an empty line after example blocks. Enabled: true diff --git a/lib/rubocop/cop/rspec/empty_hook.rb b/lib/rubocop/cop/rspec/empty_hook.rb new file mode 100644 index 000000000..ea5618032 --- /dev/null +++ b/lib/rubocop/cop/rspec/empty_hook.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module RSpec + # Checks for empty before and after hooks. + # + # @example + # # bad + # before {} + # after do; end + # before(:all) do + # end + # after(:all) { } + # + # # good + # before { create_users } + # after do + # cleanup_users + # end + # before(:all) do + # create_feed + # end + # after(:all) { cleanup_feed } + class EmptyHook < Cop + include RuboCop::Cop::RangeHelp + + MSG = 'Empty hook detected.' + + def_node_matcher :empty_hook?, <<~PATTERN + (block $#{Hooks::ALL.send_pattern} _ nil?) + PATTERN + + def on_block(node) + empty_hook?(node) do |hook| + add_offense(hook) + end + end + + def autocorrect(node) + lambda do |corrector| + block = node.parent + range = range_with_surrounding_space(range: block.loc.expression) + corrector.remove(range) + end + end + end + end + end +end diff --git a/lib/rubocop/cop/rspec_cops.rb b/lib/rubocop/cop/rspec_cops.rb index f9b2e5d36..0231d2060 100644 --- a/lib/rubocop/cop/rspec_cops.rb +++ b/lib/rubocop/cop/rspec_cops.rb @@ -30,6 +30,7 @@ require_relative 'rspec/described_class_module_wrapping' require_relative 'rspec/dialect' require_relative 'rspec/empty_example_group' +require_relative 'rspec/empty_hook' require_relative 'rspec/empty_line_after_example' require_relative 'rspec/empty_line_after_example_group' require_relative 'rspec/empty_line_after_final_let' diff --git a/manual/cops.md b/manual/cops.md index f20653afe..20ddb9617 100644 --- a/manual/cops.md +++ b/manual/cops.md @@ -29,6 +29,7 @@ * [RSpec/DescribedClassModuleWrapping](cops_rspec.md#rspecdescribedclassmodulewrapping) * [RSpec/Dialect](cops_rspec.md#rspecdialect) * [RSpec/EmptyExampleGroup](cops_rspec.md#rspecemptyexamplegroup) +* [RSpec/EmptyHook](cops_rspec.md#rspecemptyhook) * [RSpec/EmptyLineAfterExample](cops_rspec.md#rspecemptylineafterexample) * [RSpec/EmptyLineAfterExampleGroup](cops_rspec.md#rspecemptylineafterexamplegroup) * [RSpec/EmptyLineAfterFinalLet](cops_rspec.md#rspecemptylineafterfinallet) diff --git a/manual/cops_rspec.md b/manual/cops_rspec.md index ddf21a531..15459f75b 100644 --- a/manual/cops_rspec.md +++ b/manual/cops_rspec.md @@ -639,6 +639,45 @@ CustomIncludeMethods | `[]` | Array * [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyExampleGroup](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyExampleGroup) +## RSpec/EmptyHook + +Enabled by default | Supports autocorrection +--- | --- +Enabled | Yes + +Checks for empty before and after hooks. + +### Examples + +```ruby +# bad +before {} +after do; end +before(:all) do +end +after(:all) { } + +# good +before { create_users } +after do + cleanup_users +end +before(:all) do + create_feed +end +after(:all) { cleanup_feed } +``` + +### Configurable attributes + +Name | Default value | Configurable values +--- | --- | --- +VersionAdded | `1.39.0` | String + +### References + +* [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyHook](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyHook) + ## RSpec/EmptyLineAfterExample Enabled by default | Supports autocorrection diff --git a/spec/rubocop/cop/rspec/empty_hook_spec.rb b/spec/rubocop/cop/rspec/empty_hook_spec.rb new file mode 100644 index 000000000..0c01480cf --- /dev/null +++ b/spec/rubocop/cop/rspec/empty_hook_spec.rb @@ -0,0 +1,243 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::RSpec::EmptyHook do + subject(:cop) { described_class.new } + + context 'with `before` hook' do + it 'detects offense for empty `before`' do + expect_offense(<<~RUBY) + before {} + ^^^^^^ Empty hook detected. + RUBY + + expect_correction('') + end + + it 'detects offense for empty `before` with :each scope' do + expect_offense(<<~RUBY) + before(:each) {} + ^^^^^^^^^^^^^ Empty hook detected. + RUBY + + expect_correction('') + end + + it 'detects offense for empty `before` with :example scope' do + expect_offense(<<~RUBY) + before(:example) {} + ^^^^^^^^^^^^^^^^ Empty hook detected. + RUBY + + expect_correction('') + end + + it 'detects offense for empty `before` with :context scope' do + expect_offense(<<~RUBY) + before(:context) {} + ^^^^^^^^^^^^^^^^ Empty hook detected. + RUBY + + expect_correction('') + end + + it 'detects offense for empty `before` with :all scope' do + expect_offense(<<~RUBY) + before(:all) {} + ^^^^^^^^^^^^ Empty hook detected. + RUBY + + expect_correction('') + end + + it 'detects offense for empty `before` with :suite scope' do + expect_offense(<<~RUBY) + before(:suite) {} + ^^^^^^^^^^^^^^ Empty hook detected. + RUBY + + expect_correction('') + end + + it 'accepts non-empty `before` hook' do + expect_no_offenses(<<~RUBY) + before { create_users } + RUBY + end + + it 'accepts multiline `before` hook' do + expect_no_offenses(<<~RUBY) + before(:all) do + create_users + create_products + end + RUBY + end + + it 'autocorrects `before` with semicolon' do + expect_offense(<<~RUBY) + before {}; after { clean_up(:foo) } + ^^^^^^ Empty hook detected. + RUBY + + expect_correction(<<~RUBY) + ; after { clean_up(:foo) } + RUBY + end + end + + context 'with `after` hook' do + it 'detects offense for empty `after`' do + expect_offense(<<~RUBY) + after {} + ^^^^^ Empty hook detected. + RUBY + + expect_correction('') + end + + it 'accepts non-empty `after` hook' do + expect_no_offenses(<<~RUBY) + after { cleanup_users } + RUBY + end + + it 'accepts multiline `after` hook' do + expect_no_offenses(<<~RUBY) + after(:suite) do + cleanup_users + cleanup_products + end + RUBY + end + end + + context 'with `around` hook' do + it 'detects offense for empty `around`' do + expect_offense(<<~RUBY) + around {} + ^^^^^^ Empty hook detected. + RUBY + + expect_correction('') + end + + it 'accepts non-empty `around` hook' do + expect_no_offenses(<<~RUBY) + around { yield } + RUBY + end + + it 'accepts multiline `around` hook' do + expect_no_offenses(<<~RUBY) + around(:suite) do + setup_users + yield + end + RUBY + end + end + + context 'with `prepend_before` hook' do + it 'detects offense for empty `prepend_before`' do + expect_offense(<<~RUBY) + prepend_before {} + ^^^^^^^^^^^^^^ Empty hook detected. + RUBY + + expect_correction('') + end + + it 'accepts non-empty `prepend_before` hook' do + expect_no_offenses(<<~RUBY) + prepend_before { create_users } + RUBY + end + + it 'accepts multiline `prepend_before` hook' do + expect_no_offenses(<<~RUBY) + prepend_before(:all) do + create_users + create_products + end + RUBY + end + end + + context 'with `append_before` hook' do + it 'detects offense for empty `append_before`' do + expect_offense(<<~RUBY) + append_before {} + ^^^^^^^^^^^^^ Empty hook detected. + RUBY + + expect_correction('') + end + + it 'accepts non-empty `append_before` hook' do + expect_no_offenses(<<~RUBY) + append_before { create_users } + RUBY + end + + it 'accepts multiline `append_before` hook' do + expect_no_offenses(<<~RUBY) + append_before(:each) do + create_users + create_products + end + RUBY + end + end + + context 'with `prepend_after` hook' do + it 'detects offense for empty `prepend_after`' do + expect_offense(<<~RUBY) + prepend_after {} + ^^^^^^^^^^^^^ Empty hook detected. + RUBY + + expect_correction('') + end + + it 'accepts non-empty `prepend_after` hook' do + expect_no_offenses(<<~RUBY) + prepend_after { cleanup_users } + RUBY + end + + it 'accepts multiline `prepend_after` hook' do + expect_no_offenses(<<~RUBY) + prepend_after(:all) do + cleanup_users + cleanup_products + end + RUBY + end + end + + context 'with `append_after` hook' do + it 'detects offense for empty `append_after`' do + expect_offense(<<~RUBY) + append_after {} + ^^^^^^^^^^^^ Empty hook detected. + RUBY + + expect_correction('') + end + + it 'accepts non-empty `append_after` hook' do + expect_no_offenses(<<~RUBY) + append_after { cleanup_users } + RUBY + end + + it 'accepts multiline `append_after` hook' do + expect_no_offenses(<<~RUBY) + append_after(:all) do + cleanup_users + cleanup_products + end + RUBY + end + end +end From 5d2d3f012143e920aed980bd818c61917e138dd4 Mon Sep 17 00:00:00 2001 From: Benjamin Quorning Date: Fri, 1 May 2020 11:49:17 +0200 Subject: [PATCH 013/972] Bump version to v1.39.0 --- CHANGELOG.md | 2 ++ lib/rubocop/rspec/version.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3df8c0e1..3a257078a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Master (Unreleased) +## 1.39.0 (2020-05-01) + * Fix `RSpec/FilePath` detection when absolute path includes test subject. ([@eitoball][]) * Add new `Capybara/VisibilityMatcher` cop. ([@aried3r][]) * Ignore String constants by `RSpec/Describe`. ([@AlexWayfer][]) diff --git a/lib/rubocop/rspec/version.rb b/lib/rubocop/rspec/version.rb index 534d90e10..d5be298f0 100644 --- a/lib/rubocop/rspec/version.rb +++ b/lib/rubocop/rspec/version.rb @@ -4,7 +4,7 @@ module RuboCop module RSpec # Version information for the RSpec RuboCop plugin. module Version - STRING = '1.38.1' + STRING = '1.39.0' end end end From 05bdba837c984324802b4594a41b6477473ec094 Mon Sep 17 00:00:00 2001 From: Tejas Bubane Date: Fri, 1 May 2020 15:34:21 +0530 Subject: [PATCH 014/972] Add new `RSpec/VariableName` cop Closes #807 --- CHANGELOG.md | 2 + config/default.yml | 10 + lib/rubocop/cop/rspec/variable_name.rb | 49 +++++ lib/rubocop/cop/rspec_cops.rb | 1 + manual/cops.md | 1 + manual/cops_rspec.md | 44 +++++ spec/rubocop/cop/rspec/variable_name_spec.rb | 181 +++++++++++++++++++ 7 files changed, 288 insertions(+) create mode 100644 lib/rubocop/cop/rspec/variable_name.rb create mode 100644 spec/rubocop/cop/rspec/variable_name_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a257078a..436a407c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Master (Unreleased) +* Add new `RSpec/VariableName` cop. ([@tejasbubane][]) + ## 1.39.0 (2020-05-01) * Fix `RSpec/FilePath` detection when absolute path includes test subject. ([@eitoball][]) diff --git a/config/default.yml b/config/default.yml index 566d5bd56..a802a90f0 100644 --- a/config/default.yml +++ b/config/default.yml @@ -454,6 +454,16 @@ RSpec/UnspecifiedException: Enabled: true StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/UnspecifiedException +RSpec/VariableName: + Description: This cop makes sure that all variables use the configured style. + Enabled: true + EnforcedStyle: snake_case + SupportedStyles: + - snake_case + - camelCase + VersionAdded: '1.40' + StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/VariableName + RSpec/VerifiedDoubles: Description: Prefer using verifying doubles over normal doubles. Enabled: true diff --git a/lib/rubocop/cop/rspec/variable_name.rb b/lib/rubocop/cop/rspec/variable_name.rb new file mode 100644 index 000000000..b713681eb --- /dev/null +++ b/lib/rubocop/cop/rspec/variable_name.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module RSpec + # This cop makes sure that all variables use the configured style. + # + # @example EnforcedStyle: snake_case (default) + # # bad + # let(:userName) { 'Adam' } + # subject(:userName) { 'Adam' } + # + # # good + # let(:user_name) { 'Adam' } + # subject(:user_name) { 'Adam' } + # + # @example EnforcedStyle: camelCase + # # bad + # let(:user_name) { 'Adam' } + # subject(:user_name) { 'Adam' } + # + # # good + # let(:userName) { 'Adam' } + # subject(:userName) { 'Adam' } + class VariableName < Cop + include ConfigurableNaming + + MSG = 'Use %