diff --git a/CHANGELOG.md b/CHANGELOG.md index bc53d25a6..7cb5ed5bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,12 @@ * Update `RSpec/ExampleWording` cop to raise error for insufficient descriptions. ([@akrox58][]) * Add new `RSpec/Capybara/NegationMatcher` cop. ([@ydah][]) * Add `AllowedPatterns` configuration option to `RSpec/NoExpectationExample`. ([@ydah][]) -* Improve `RSpec/NoExpectationExample` cop to ignore examples skipped or pending via metatada. ([@pirj][]) +* Improve `RSpec/NoExpectationExample` cop to ignore examples skipped or pending via metadata. ([@pirj][]) * Add `RSpec/FactoryBot/ConsistentParenthesesStyle` cop. ([@Liberatys][]) * Add `RSpec/Rails/InferredSpecType` cop. ([@r7kamura][]) * Add new `RSpec/Capybara/SpecificActions` cop. ([@ydah][]) * Update `config/default.yml` removing deprecated option to make the config correctable by users. ([@ignaciovillaverde][]) +* Do not attempt to auto-correct example groups with `include_examples` in `RSpec/LetBeforeExamples`. ([@pirj][]) ## 2.13.2 (2022-09-23) diff --git a/lib/rubocop/cop/rspec/let_before_examples.rb b/lib/rubocop/cop/rspec/let_before_examples.rb index 5059b568b..d48ad12c6 100644 --- a/lib/rubocop/cop/rspec/let_before_examples.rb +++ b/lib/rubocop/cop/rspec/let_before_examples.rb @@ -43,6 +43,14 @@ class LetBeforeExamples < Base } PATTERN + # @!method include_examples?(node) + def_node_matcher :include_examples?, <<~PATTERN + { + #{block_pattern(':include_examples')} + #{send_pattern(':include_examples')} + } + PATTERN + def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler return unless example_group_with_body?(node) @@ -51,6 +59,10 @@ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler private + def example_group_with_include_examples?(body) + body.children.any? { |sibling| include_examples?(sibling) } + end + def multiline_block?(block) block.begin_type? end @@ -59,11 +71,13 @@ def check_let_declarations(node) first_example = find_first_example(node) return unless first_example + correct = !example_group_with_include_examples?(node) + first_example.right_siblings.each do |sibling| next unless let?(sibling) add_offense(sibling) do |corrector| - autocorrect(corrector, sibling, first_example) + autocorrect(corrector, sibling, first_example) if correct end end end diff --git a/spec/rubocop/cop/rspec/let_before_examples_spec.rb b/spec/rubocop/cop/rspec/let_before_examples_spec.rb index a36c5dbdb..d7b1ad419 100644 --- a/spec/rubocop/cop/rspec/let_before_examples_spec.rb +++ b/spec/rubocop/cop/rspec/let_before_examples_spec.rb @@ -41,10 +41,26 @@ RUBY end - it 'flags `let` after `include_examples`' do + it 'flags `let` after `include_examples`, but does not autocorrect' do + # NOTE: include_examples may define the same variable as `let`, + # and changing the order may break the spec due to order dependency + # if `let` is moved above. expect_offense(<<-RUBY) RSpec.describe User do - include_examples('should be after let') + include_examples('should be BEFORE let as it defines `let(:foo)`, too') + + let(:foo) { 'bar' } + ^^^^^^^^^^^^^^^^^^^ Move `let` before the examples in the group. + end + RUBY + + expect_no_corrections + end + + it 'flags `let` after `it_behaves_like`' do + expect_offense(<<-RUBY) + RSpec.describe User do + it_behaves_like('should be after let') let(:foo) { bar } ^^^^^^^^^^^^^^^^^ Move `let` before the examples in the group. @@ -54,7 +70,7 @@ expect_correction(<<-RUBY) RSpec.describe User do let(:foo) { bar } - include_examples('should be after let') + it_behaves_like('should be after let') end RUBY @@ -63,7 +79,7 @@ it 'flags `let` with proc argument' do expect_offense(<<-RUBY) RSpec.describe User do - include_examples('should be after let') + it_behaves_like('should be after let') let(:user, &args[:build_user]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Move `let` before the examples in the group. @@ -73,7 +89,7 @@ expect_correction(<<-RUBY) RSpec.describe User do let(:user, &args[:build_user]) - include_examples('should be after let') + it_behaves_like('should be after let') end RUBY @@ -82,7 +98,7 @@ it 'flags `let` with a heredoc argument' do expect_offense(<<-RUBY) RSpec.describe User do - include_examples('should be after let') + it_behaves_like('should be after let') let(:foo) { (<<-SOURCE) } ^^^^^^^^^^^^^^^^^^^^^^^^^ Move `let` before the examples in the group. @@ -96,7 +112,7 @@ let(:foo) { (<<-SOURCE) } some long text here SOURCE - include_examples('should be after let') + it_behaves_like('should be after let') end RUBY @@ -113,7 +129,7 @@ it { is_expected.to work } end - include_examples('everything is fine') + it_behaves_like('everything is fine') end RUBY end @@ -128,7 +144,7 @@ it { is_expected.to work } end - include_examples('everything is fine') + it_behaves_like('everything is fine') end RUBY end @@ -146,7 +162,7 @@ it 'ignores single-line example blocks' do expect_no_offenses(<<-RUBY) RSpec.describe User do - include_examples 'special user' do + it_behaves_like 'special user' do let(:foo) { bar } end end