diff --git a/.rubocop.yml b/.rubocop.yml index 3d0f38336..fb0d0791e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -118,6 +118,8 @@ RSpec/ExcessiveDocstringSpacing: Enabled: true RSpec/IdenticalEqualityAssertion: Enabled: true +RSpec/MemoizedHelperBlockDelimiter: + Enabled: true RSpec/NoExpectationExample: Enabled: true RSpec/SortMetadata: diff --git a/CHANGELOG.md b/CHANGELOG.md index e35434431..1f5fc47b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ * 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][]) * Add new `RSpec/SortMetadata` cop. ([@leoarnold][]) +* Add `RSpec/MemoizedHelperBlockDelimiter` cop. ([@r7kamura][]) ## 2.13.2 (2022-09-23) diff --git a/config/default.yml b/config/default.yml index 1c36c0e6b..4d5adb7d4 100644 --- a/config/default.yml +++ b/config/default.yml @@ -558,6 +558,16 @@ RSpec/LetSetup: VersionAdded: '1.7' Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/LetSetup +RSpec/MemoizedHelperBlockDelimiter: + Description: Use consistent style block delimiters for memoized helpers. + Enabled: pending + EnforcedStyle: do_end + SupportedStyles: + - braces + - do_end + VersionAdded: "<>" + Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MemoizedHelperBlockDelimiter + RSpec/MessageChain: Description: Check that chains of messages are not being stubbed. Enabled: true diff --git a/docs/modules/ROOT/pages/cops.adoc b/docs/modules/ROOT/pages/cops.adoc index 9e29262d0..494f74793 100644 --- a/docs/modules/ROOT/pages/cops.adoc +++ b/docs/modules/ROOT/pages/cops.adoc @@ -52,6 +52,7 @@ * xref:cops_rspec.adoc#rspecleakyconstantdeclaration[RSpec/LeakyConstantDeclaration] * xref:cops_rspec.adoc#rspecletbeforeexamples[RSpec/LetBeforeExamples] * xref:cops_rspec.adoc#rspecletsetup[RSpec/LetSetup] +* xref:cops_rspec.adoc#rspecmemoizedhelperblockdelimiter[RSpec/MemoizedHelperBlockDelimiter] * xref:cops_rspec.adoc#rspecmessagechain[RSpec/MessageChain] * xref:cops_rspec.adoc#rspecmessageexpectation[RSpec/MessageExpectation] * xref:cops_rspec.adoc#rspecmessagespies[RSpec/MessageSpies] diff --git a/docs/modules/ROOT/pages/cops_rspec.adoc b/docs/modules/ROOT/pages/cops_rspec.adoc index e4d799540..0774316e1 100644 --- a/docs/modules/ROOT/pages/cops_rspec.adoc +++ b/docs/modules/ROOT/pages/cops_rspec.adoc @@ -2800,6 +2800,78 @@ end * https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/LetSetup +== RSpec/MemoizedHelperBlockDelimiter + +|=== +| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed + +| Pending +| Yes +| Yes +| <> +| - +|=== + +Use consistent style block delimiters for memoized helpers. + +=== Examples + +==== EnforcedStyle: do_end (default) + +[source,ruby] +---- +# bad +let(:foo) { 'bar' } + +# good +let(:foo) do + 'bar' +end + +# bad +subject(:foo) { 'bar' } + +# good +subject(:foo) do + 'bar' +end +---- + +==== EnforcedStyle: braces + +[source,ruby] +---- +# bad +let(:foo) do + 'bar' +end + +# good +let(:foo) { 'bar' } + +# bad +subject(:foo) do + 'bar' +end + +# good +subject(:foo) { 'bar' } +---- + +=== Configurable attributes + +|=== +| Name | Default value | Configurable values + +| EnforcedStyle +| `do_end` +| `braces`, `do_end` +|=== + +=== References + +* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/MemoizedHelperBlockDelimiter + == RSpec/MessageChain |=== diff --git a/lib/rubocop/cop/rspec/memoized_helper_block_delimiter.rb b/lib/rubocop/cop/rspec/memoized_helper_block_delimiter.rb new file mode 100644 index 000000000..59285a838 --- /dev/null +++ b/lib/rubocop/cop/rspec/memoized_helper_block_delimiter.rb @@ -0,0 +1,135 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module RSpec + # Use consistent style block delimiters for memoized helpers. + # + # @example EnforcedStyle: do_end (default) + # # bad + # let(:foo) { 'bar' } + # + # # good + # let(:foo) do + # 'bar' + # end + # + # # bad + # subject(:foo) { 'bar' } + # + # # good + # subject(:foo) do + # 'bar' + # end + # + # @example EnforcedStyle: braces + # # bad + # let(:foo) do + # 'bar' + # end + # + # # good + # let(:foo) { 'bar' } + # + # # bad + # subject(:foo) do + # 'bar' + # end + # + # # good + # subject(:foo) { 'bar' } + class MemoizedHelperBlockDelimiter < Base + extend AutoCorrector + + include ConfigurableEnforcedStyle + include RangeHelp + include RuboCop::RSpec::Language + + # @param node [RuboCop::AST::BlockNode] + # @return [void] + def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler + return unless bad?(node) + + add_offense( + node.location.begin.with( + end_pos: node.location.end.end_pos + ), + message: "Use #{style} style block delimiters." + ) do |corrector| + autocorrect(corrector, node) + end + end + + private + + # @param corrector [RuboCop::Cop::Corrector] + # @param node [RuboCop::AST::BlockNode] + # @return [void] + def autocorrect(corrector, node) + case style + when :braces + # Not supported because it conflicts with Style/BlockDelimiters. + when :do_end + autocorrect_braces(corrector, node) + end + end + + # @param corrector [RuboCop::Cop::Corrector] + # @param node [RuboCop::AST::BlockNode] + # @return [void] + def autocorrect_braces(corrector, node) + unless whitespace_before?(node.location.begin) + corrector.insert_before(node.location.begin, ' ') + end + corrector.replace(node.location.begin, 'do') + corrector.replace(node.location.end, 'end') + wrap_in_newlines(corrector, node) if node.single_line? + end + + # @param node [RuboCop::AST::BlockNode] + # @return [Boolean] + def bad?(node) + memoized_helper_method?(node) && + !preferred_block_delimiter?(node) + end + + # @param node [RuboCop::AST::BlockNode] + # @return [Boolean] + def memoized_helper_method?(node) + let?(node) || subject?(node) + end + + # @param node [RuboCop::AST::BlockNode] + # @return [Boolean] + def preferred_block_delimiter?(node) + case style + when :braces + node.braces? + when :do_end + !node.braces? + end + end + + # @param range [Parser::Source::Range] + # @return [Boolean] + def whitespace_before?(range) + range.source_buffer.source[range.begin_pos - 1].match?(/\s/) + end + + # @param corrector [RuboCop::Cop::Corrector] + # @param node [RuboCop::AST::BlockNode] + # @return [void] + def wrap_in_newlines(corrector, node) + corrector.wrap( + node.location.begin.with( + begin_pos: node.location.begin.end_pos, + end_pos: node.location.end.begin_pos + ), + "\n", + "\n" + ) + end + end + end + end +end diff --git a/lib/rubocop/cop/rspec_cops.rb b/lib/rubocop/cop/rspec_cops.rb index b9b9d103c..69f177070 100644 --- a/lib/rubocop/cop/rspec_cops.rb +++ b/lib/rubocop/cop/rspec_cops.rb @@ -73,6 +73,7 @@ require_relative 'rspec/leaky_constant_declaration' require_relative 'rspec/let_before_examples' require_relative 'rspec/let_setup' +require_relative 'rspec/memoized_helper_block_delimiter' require_relative 'rspec/message_chain' require_relative 'rspec/message_expectation' require_relative 'rspec/message_spies' diff --git a/spec/project/changelog_spec.rb b/spec/project/changelog_spec.rb index 26a01ecf4..953412d13 100644 --- a/spec/project/changelog_spec.rb +++ b/spec/project/changelog_spec.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true RSpec.describe 'CHANGELOG.md' do - subject(:changelog) { SpecHelper::ROOT.join('CHANGELOG.md').read } + subject(:changelog) do + SpecHelper::ROOT.join('CHANGELOG.md').read + end it 'has link definitions for all implicit links' do implicit_link_names = changelog.scan(/\[([^\]]+)\]\[\]/).flatten.uniq @@ -32,9 +34,13 @@ end describe 'entry' do - subject(:entries) { lines.grep(/^\*/).map(&:chomp) } + subject(:entries) do + lines.grep(/^\*/).map(&:chomp) + end - let(:lines) { changelog.each_line } + let(:lines) do + changelog.each_line + end it 'has a whitespace between the * and the body' do expect(entries).to all(match(/^\* \S/)) diff --git a/spec/rubocop/cli/autocorrect_spec.rb b/spec/rubocop/cli/autocorrect_spec.rb index fce3b57d9..51f180a72 100644 --- a/spec/rubocop/cli/autocorrect_spec.rb +++ b/spec/rubocop/cli/autocorrect_spec.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true RSpec.describe 'RuboCop::CLI --autocorrect' do # rubocop:disable RSpec/DescribeClass - subject(:cli) { RuboCop::CLI.new } + subject(:cli) do + RuboCop::CLI.new + end include_context 'isolated environment' diff --git a/spec/rubocop/cop/rspec/base_spec.rb b/spec/rubocop/cop/rspec/base_spec.rb index fed26061b..0fd46be6f 100644 --- a/spec/rubocop/cop/rspec/base_spec.rb +++ b/spec/rubocop/cop/rspec/base_spec.rb @@ -1,8 +1,12 @@ # frozen_string_literal: true RSpec.describe RuboCop::Cop::RSpec::Base do - let(:cop_class) { RuboCop::RSpec::FakeCop } - let(:cop_config) { { 'Exclude' => %w[bar_spec.rb] } } + let(:cop_class) do + RuboCop::RSpec::FakeCop + end + let(:cop_config) do + { 'Exclude' => %w[bar_spec.rb] } + end before do stub_const('RuboCop::RSpec::FakeCop', @@ -89,7 +93,9 @@ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler end) end - let(:cop_class) { RuboCop::RSpec::ExampleGroupHaterCop } + let(:cop_class) do + RuboCop::RSpec::ExampleGroupHaterCop + end shared_examples_for 'it detects `describe`' do it 'detects `describe` as an example group' do diff --git a/spec/rubocop/cop/rspec/be_nil_spec.rb b/spec/rubocop/cop/rspec/be_nil_spec.rb index 3405f6d75..41f14f109 100644 --- a/spec/rubocop/cop/rspec/be_nil_spec.rb +++ b/spec/rubocop/cop/rspec/be_nil_spec.rb @@ -6,7 +6,9 @@ end context 'with EnforcedStyle `be_nil`' do - let(:enforced_style) { 'be_nil' } + let(:enforced_style) do + 'be_nil' + end it 'registers an offense when using `#be` for `nil` value' do expect_offense(<<~RUBY) @@ -37,7 +39,9 @@ end context 'with EnforcedStyle `be`' do - let(:enforced_style) { 'be' } + let(:enforced_style) do + 'be' + end it 'does not register an offense when using `#be` for `nil` value' do expect_no_offenses(<<~RUBY) diff --git a/spec/rubocop/cop/rspec/capybara/feature_methods_spec.rb b/spec/rubocop/cop/rspec/capybara/feature_methods_spec.rb index 44f466ca6..39c4488c6 100644 --- a/spec/rubocop/cop/rspec/capybara/feature_methods_spec.rb +++ b/spec/rubocop/cop/rspec/capybara/feature_methods_spec.rb @@ -133,7 +133,9 @@ end context 'with configured `EnabledMethods`' do - let(:cop_config) { { 'EnabledMethods' => %w[feature] } } + let(:cop_config) do + { 'EnabledMethods' => %w[feature] } + end it 'ignores usage of the enabled method' do expect_no_offenses(<<-RUBY) diff --git a/spec/rubocop/cop/rspec/capybara/negation_matcher_spec.rb b/spec/rubocop/cop/rspec/capybara/negation_matcher_spec.rb index f84702c1d..7b785c788 100644 --- a/spec/rubocop/cop/rspec/capybara/negation_matcher_spec.rb +++ b/spec/rubocop/cop/rspec/capybara/negation_matcher_spec.rb @@ -1,10 +1,14 @@ # frozen_string_literal: true RSpec.describe RuboCop::Cop::RSpec::Capybara::NegationMatcher, :config do - let(:cop_config) { { 'EnforcedStyle' => enforced_style } } + let(:cop_config) do + { 'EnforcedStyle' => enforced_style } + end context 'with EnforcedStyle `have_no`' do - let(:enforced_style) { 'have_no' } + let(:enforced_style) do + 'have_no' + end %i[selector css xpath text title current_path link button field checked_field unchecked_field select table @@ -50,7 +54,9 @@ end context 'with EnforcedStyle `not_to`' do - let(:enforced_style) { 'not_to' } + let(:enforced_style) do + 'not_to' + end %i[selector css xpath text title current_path link button field checked_field unchecked_field select table diff --git a/spec/rubocop/cop/rspec/change_by_zero_spec.rb b/spec/rubocop/cop/rspec/change_by_zero_spec.rb index d47ee9d66..e4f473aea 100644 --- a/spec/rubocop/cop/rspec/change_by_zero_spec.rb +++ b/spec/rubocop/cop/rspec/change_by_zero_spec.rb @@ -174,7 +174,9 @@ end context "with `NegatedMatcher: 'not_change'`" do - let(:cop_config) { { 'NegatedMatcher' => 'not_change' } } + let(:cop_config) do + { 'NegatedMatcher' => 'not_change' } + end it 'registers an offense and autocorrect when ' \ 'the argument to `by` is zero with compound expectations' do diff --git a/spec/rubocop/cop/rspec/context_wording_spec.rb b/spec/rubocop/cop/rspec/context_wording_spec.rb index 875bf557a..78dc703db 100644 --- a/spec/rubocop/cop/rspec/context_wording_spec.rb +++ b/spec/rubocop/cop/rspec/context_wording_spec.rb @@ -81,7 +81,9 @@ end context 'when configured' do - let(:cop_config) { { 'Prefixes' => %w[if], 'AllowedPatterns' => [] } } + let(:cop_config) do + { 'Prefixes' => %w[if], 'AllowedPatterns' => [] } + end it 'finds context without allowed prefixes at the beginning' do expect_offense(<<-RUBY) @@ -112,7 +114,9 @@ end context 'with special regex characters' do - let(:cop_config) { { 'Prefixes' => ['a$b\d'], 'AllowedPatterns' => [] } } + let(:cop_config) do + { 'Prefixes' => ['a$b\d'], 'AllowedPatterns' => [] } + end it 'matches the full prefix' do expect_offense(<<-RUBY) diff --git a/spec/rubocop/cop/rspec/described_class_spec.rb b/spec/rubocop/cop/rspec/described_class_spec.rb index 9da01955b..dde094713 100644 --- a/spec/rubocop/cop/rspec/described_class_spec.rb +++ b/spec/rubocop/cop/rspec/described_class_spec.rb @@ -1,10 +1,14 @@ # frozen_string_literal: true RSpec.describe RuboCop::Cop::RSpec::DescribedClass do - let(:cop_config) { {} } + let(:cop_config) do + {} + end context 'when SkipBlocks is `true`' do - let(:cop_config) { { 'SkipBlocks' => true } } + let(:cop_config) do + { 'SkipBlocks' => true } + end it 'ignores violations within non-rspec blocks' do expect_offense(<<-RUBY) @@ -50,7 +54,9 @@ end context 'when EnforcedStyle is :described_class' do - let(:cop_config) { { 'EnforcedStyle' => :described_class } } + let(:cop_config) do + { 'EnforcedStyle' => :described_class } + end it 'flags for the use of the described class' do expect_offense(<<-RUBY) @@ -291,7 +297,9 @@ module SomeGem end context 'when EnforcedStyle is :explicit' do - let(:cop_config) { { 'EnforcedStyle' => :explicit } } + let(:cop_config) do + { 'EnforcedStyle' => :explicit } + end it 'flags the use of the described_class' do expect_offense(<<-RUBY) diff --git a/spec/rubocop/cop/rspec/empty_line_after_example_spec.rb b/spec/rubocop/cop/rspec/empty_line_after_example_spec.rb index f7a81704a..faf07362e 100644 --- a/spec/rubocop/cop/rspec/empty_line_after_example_spec.rb +++ b/spec/rubocop/cop/rspec/empty_line_after_example_spec.rb @@ -248,7 +248,9 @@ end context 'when AllowConsecutiveOneLiners is false' do - let(:cop_config) { { 'AllowConsecutiveOneLiners' => false } } + let(:cop_config) do + { 'AllowConsecutiveOneLiners' => false } + end it 'ignores consecutive one-liners' do expect_offense(<<-RUBY) diff --git a/spec/rubocop/cop/rspec/empty_line_after_hook_spec.rb b/spec/rubocop/cop/rspec/empty_line_after_hook_spec.rb index 23298f851..41cebc012 100644 --- a/spec/rubocop/cop/rspec/empty_line_after_hook_spec.rb +++ b/spec/rubocop/cop/rspec/empty_line_after_hook_spec.rb @@ -379,7 +379,9 @@ end context 'when AllowConsecutiveOneLiners option `false`' do - let(:cop_config) { { 'AllowConsecutiveOneLiners' => false } } + let(:cop_config) do + { 'AllowConsecutiveOneLiners' => false } + end include_examples 'always require empty line after hook groups' include_examples 'never allows consecutive multiline blocks' diff --git a/spec/rubocop/cop/rspec/example_length_spec.rb b/spec/rubocop/cop/rspec/example_length_spec.rb index 5a1346277..786e88676 100644 --- a/spec/rubocop/cop/rspec/example_length_spec.rb +++ b/spec/rubocop/cop/rspec/example_length_spec.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true RSpec.describe RuboCop::Cop::RSpec::ExampleLength do - let(:cop_config) { { 'Max' => 3 } } + let(:cop_config) do + { 'Max' => 3 } + end it 'ignores non-spec blocks' do expect_no_offenses(<<-RUBY) diff --git a/spec/rubocop/cop/rspec/example_without_description_spec.rb b/spec/rubocop/cop/rspec/example_without_description_spec.rb index e2156925d..06a061ff1 100644 --- a/spec/rubocop/cop/rspec/example_without_description_spec.rb +++ b/spec/rubocop/cop/rspec/example_without_description_spec.rb @@ -6,7 +6,9 @@ end context 'with EnforcedStyle `always_allow`' do - let(:enforced_style) { 'always_allow' } + let(:enforced_style) do + 'always_allow' + end it 'flags empty strings for description' do expect_offense(<<-RUBY) @@ -35,7 +37,9 @@ end context 'with EnforcedStyle `single_line_only`' do - let(:enforced_style) { 'single_line_only' } + let(:enforced_style) do + 'single_line_only' + end it 'flags missing description in multi-line examples' do expect_offense(<<-RUBY) @@ -61,7 +65,9 @@ end context 'with EnforcedStyle `disallow`' do - let(:enforced_style) { 'disallow' } + let(:enforced_style) do + 'disallow' + end it 'flags missing description in multi-line examples' do expect_offense(<<-RUBY) diff --git a/spec/rubocop/cop/rspec/example_wording_spec.rb b/spec/rubocop/cop/rspec/example_wording_spec.rb index 0a76ad836..0b5eb9bc8 100644 --- a/spec/rubocop/cop/rspec/example_wording_spec.rb +++ b/spec/rubocop/cop/rspec/example_wording_spec.rb @@ -213,7 +213,9 @@ end context 'when `DisallowedExamples: Workz`' do - let(:cop_config) { { 'DisallowedExamples' => ['Workz'] } } + let(:cop_config) do + { 'DisallowedExamples' => ['Workz'] } + end it 'finds a valid sentence across two lines' do expect_no_offenses(<<-'RUBY') diff --git a/spec/rubocop/cop/rspec/expect_actual_spec.rb b/spec/rubocop/cop/rspec/expect_actual_spec.rb index cc21b7f28..fb06598ce 100644 --- a/spec/rubocop/cop/rspec/expect_actual_spec.rb +++ b/spec/rubocop/cop/rspec/expect_actual_spec.rb @@ -318,7 +318,9 @@ end context 'when inspecting rspec-rails routing specs' do - let(:cop_config) { {} } + let(:cop_config) do + {} + end it 'ignores rspec-rails routing specs' do expect_no_offenses( diff --git a/spec/rubocop/cop/rspec/expect_change_spec.rb b/spec/rubocop/cop/rspec/expect_change_spec.rb index 3ec1fca31..28f3dac6c 100644 --- a/spec/rubocop/cop/rspec/expect_change_spec.rb +++ b/spec/rubocop/cop/rspec/expect_change_spec.rb @@ -6,7 +6,9 @@ end context 'with EnforcedStyle `method_call`' do - let(:enforced_style) { 'method_call' } + let(:enforced_style) do + 'method_call' + end it 'flags blocks that contain simple message sending' do expect_offense(<<-RUBY) @@ -119,7 +121,9 @@ end context 'with EnforcedStyle `block`' do - let(:enforced_style) { 'block' } + let(:enforced_style) do + 'block' + end it 'flags change matcher without block' do expect_offense(<<-RUBY) diff --git a/spec/rubocop/cop/rspec/factory_bot/consistent_parentheses_style_spec.rb b/spec/rubocop/cop/rspec/factory_bot/consistent_parentheses_style_spec.rb index 15c0ac065..d34568324 100644 --- a/spec/rubocop/cop/rspec/factory_bot/consistent_parentheses_style_spec.rb +++ b/spec/rubocop/cop/rspec/factory_bot/consistent_parentheses_style_spec.rb @@ -6,7 +6,9 @@ end context 'when EnforcedStyle is :enforce_parentheses' do - let(:enforced_style) { :require_parentheses } + let(:enforced_style) do + :require_parentheses + end context 'with create' do it 'flags the call to use parentheses' do @@ -134,7 +136,9 @@ end context 'when EnforcedStyle is :omit_parentheses' do - let(:enforced_style) { :omit_parentheses } + let(:enforced_style) do + :omit_parentheses + end context 'with create' do it 'flags the call to not use parentheses' do diff --git a/spec/rubocop/cop/rspec/factory_bot/create_list_spec.rb b/spec/rubocop/cop/rspec/factory_bot/create_list_spec.rb index 7de312664..e33347885 100644 --- a/spec/rubocop/cop/rspec/factory_bot/create_list_spec.rb +++ b/spec/rubocop/cop/rspec/factory_bot/create_list_spec.rb @@ -6,7 +6,9 @@ end context 'when EnforcedStyle is :create_list' do - let(:enforced_style) { :create_list } + let(:enforced_style) do + :create_list + end it 'flags usage of n.times with no arguments' do expect_offense(<<~RUBY) @@ -192,7 +194,9 @@ end context 'when EnforcedStyle is :n_times' do - let(:enforced_style) { :n_times } + let(:enforced_style) do + :n_times + end it 'flags usage of create_list' do expect_offense(<<~RUBY) diff --git a/spec/rubocop/cop/rspec/file_path_spec.rb b/spec/rubocop/cop/rspec/file_path_spec.rb index 392e76189..59883107c 100644 --- a/spec/rubocop/cop/rspec/file_path_spec.rb +++ b/spec/rubocop/cop/rspec/file_path_spec.rb @@ -249,7 +249,9 @@ class Foo end context 'when configured with CustomTransform' do - let(:cop_config) { { 'CustomTransform' => { 'FooFoo' => 'foofoo' } } } + let(:cop_config) do + { 'CustomTransform' => { 'FooFoo' => 'foofoo' } } + end it 'does not register an offense for custom module name transformation' do expect_no_offenses(<<-RUBY, 'foofoo/some/class/bar_spec.rb') @@ -265,7 +267,9 @@ class Foo end context 'when configured with IgnoreMethods' do - let(:cop_config) { { 'IgnoreMethods' => true } } + let(:cop_config) do + { 'IgnoreMethods' => true } + end it 'does not register an offense for the described method' do expect_no_offenses(<<-RUBY, 'my_class_spec.rb') @@ -275,7 +279,9 @@ class Foo end context 'when configured with SpecSuffixOnly' do - let(:cop_config) { { 'SpecSuffixOnly' => true } } + let(:cop_config) do + { 'SpecSuffixOnly' => true } + end it 'does not register an offense for the described class' do expect_no_offenses(<<-RUBY, 'whatever_spec.rb') diff --git a/spec/rubocop/cop/rspec/hook_argument_spec.rb b/spec/rubocop/cop/rspec/hook_argument_spec.rb index c7f15cf40..b7818044f 100644 --- a/spec/rubocop/cop/rspec/hook_argument_spec.rb +++ b/spec/rubocop/cop/rspec/hook_argument_spec.rb @@ -36,7 +36,9 @@ end context 'when EnforcedStyle is :implicit' do - let(:enforced_style) { :implicit } + let(:enforced_style) do + :implicit + end it 'detects :each for hooks' do expect_offense(<<-RUBY) @@ -121,7 +123,9 @@ end context 'when EnforcedStyle is :each' do - let(:enforced_style) { :each } + let(:enforced_style) do + :each + end it 'does not flag :each for hooks' do expect_no_offenses(<<-RUBY) @@ -206,7 +210,9 @@ end context 'when EnforcedStyle is :example' do - let(:enforced_style) { :example } + let(:enforced_style) do + :example + end it 'does not flag :example for hooks' do expect_no_offenses(<<-RUBY) diff --git a/spec/rubocop/cop/rspec/implicit_subject_spec.rb b/spec/rubocop/cop/rspec/implicit_subject_spec.rb index a4e37ae21..fb1df2665 100644 --- a/spec/rubocop/cop/rspec/implicit_subject_spec.rb +++ b/spec/rubocop/cop/rspec/implicit_subject_spec.rb @@ -6,7 +6,9 @@ end context 'with EnforcedStyle `single_line_only`' do - let(:enforced_style) { 'single_line_only' } + let(:enforced_style) do + 'single_line_only' + end it 'flags `is_expected` in multi-line examples' do expect_offense(<<-RUBY) @@ -88,7 +90,9 @@ def permits(actions) end context 'with EnforcedStyle `single_statement_only`' do - let(:enforced_style) { 'single_statement_only' } + let(:enforced_style) do + 'single_statement_only' + end it 'allows `is_expected` in multi-line example with single statement' do expect_no_offenses(<<-RUBY) @@ -117,7 +121,9 @@ def permits(actions) end context 'with EnforcedStyle `disallow`' do - let(:enforced_style) { 'disallow' } + let(:enforced_style) do + 'disallow' + end it 'flags `is_expected` in multi-line examples' do expect_offense(<<-RUBY) @@ -185,7 +191,9 @@ def permits(actions) end context 'with EnforcedStyle `require_implicit`' do - let(:enforced_style) { 'require_implicit' } + let(:enforced_style) do + 'require_implicit' + end context 'with `is_expected`' do it 'does not register an offense' do diff --git a/spec/rubocop/cop/rspec/it_behaves_like_spec.rb b/spec/rubocop/cop/rspec/it_behaves_like_spec.rb index 5cf52274c..9bfb02e82 100644 --- a/spec/rubocop/cop/rspec/it_behaves_like_spec.rb +++ b/spec/rubocop/cop/rspec/it_behaves_like_spec.rb @@ -6,7 +6,9 @@ end context 'when the enforced style is `it_behaves_like`' do - let(:enforced_style) { :it_behaves_like } + let(:enforced_style) do + :it_behaves_like + end it 'flags a violation for it_should_behave_like' do expect_offense(<<-RUBY) @@ -25,7 +27,9 @@ end context 'when the enforced style is `it_should_behave_like`' do - let(:enforced_style) { :it_should_behave_like } + let(:enforced_style) do + :it_should_behave_like + end it 'flags a violation for it_behaves_like' do expect_offense(<<-RUBY) diff --git a/spec/rubocop/cop/rspec/memoized_helper_block_delimiter_spec.rb b/spec/rubocop/cop/rspec/memoized_helper_block_delimiter_spec.rb new file mode 100644 index 000000000..bddda5687 --- /dev/null +++ b/spec/rubocop/cop/rspec/memoized_helper_block_delimiter_spec.rb @@ -0,0 +1,131 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::RSpec::MemoizedHelperBlockDelimiter do + let(:cop_config) do + { 'EnforcedStyle' => enforced_style } + end + + context 'when EnforcedStyle is :braces' do + let(:enforced_style) do + :braces + end + + context 'with braces style is used' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + let(:foo) { + 'bar' + } + RUBY + end + end + + context 'with do_end style is used' do + it 'registers an offense' do + expect_offense(<<~RUBY) + let(:foo) do + ^^ Use braces style block delimiters. + 'bar' + end + RUBY + + expect_no_corrections + end + end + + context 'with do_end style is used with rescue' do + it 'registers an offense' do + expect_offense(<<~RUBY) + let(:foo) do + ^^ Use braces style block delimiters. + 'bar' + rescue + 'baz' + end + RUBY + + expect_no_corrections + end + end + end + + context 'when EnforcedStyle is :do_end' do + let(:enforced_style) do + :do_end + end + + context 'when do_end style is used' do + it 'does not register an offense' do + expect_no_offenses(<<~RUBY) + let(:foo) do + 'bar' + end + RUBY + end + end + + context 'when braces style is used on `let`' do + it 'registers an offense' do + expect_offense(<<~RUBY) + let(:foo) { + ^ Use do_end style block delimiters. + 'bar' + } + RUBY + + expect_correction(<<~RUBY) + let(:foo) do + 'bar' + end + RUBY + end + end + + context 'when braces style is used on `subject`' do + it 'registers an offense' do + expect_offense(<<~RUBY) + subject(:foo) { + ^ Use do_end style block delimiters. + 'bar' + } + RUBY + + expect_correction(<<~RUBY) + subject(:foo) do + 'bar' + end + RUBY + end + end + + context 'when braces style is used in one-line style' do + it 'registers an offense' do + expect_offense(<<~RUBY) + let(:foo) { 'bar' } + ^^^^^^^^^ Use do_end style block delimiters. + RUBY + + expect_correction(<<~RUBY) + let(:foo) do + 'bar'#{' '} + end + RUBY + end + end + + context 'when braces style is used in without spaces' do + it 'registers an offense' do + expect_offense(<<~RUBY) + let(:foo){'bar'} + ^^^^^^^ Use do_end style block delimiters. + RUBY + + expect_correction(<<~RUBY) + let(:foo) do + 'bar' + end + RUBY + end + end + end +end diff --git a/spec/rubocop/cop/rspec/multiple_expectations_spec.rb b/spec/rubocop/cop/rspec/multiple_expectations_spec.rb index 9a3e2bd95..68780cbb0 100644 --- a/spec/rubocop/cop/rspec/multiple_expectations_spec.rb +++ b/spec/rubocop/cop/rspec/multiple_expectations_spec.rb @@ -2,7 +2,9 @@ RSpec.describe RuboCop::Cop::RSpec::MultipleExpectations do context 'without configuration' do - let(:cop_config) { {} } + let(:cop_config) do + {} + end it 'flags multiple expectations' do expect_offense(<<-RUBY) diff --git a/spec/rubocop/cop/rspec/multiple_memoized_helpers_spec.rb b/spec/rubocop/cop/rspec/multiple_memoized_helpers_spec.rb index 8d73edd9a..527c849f3 100644 --- a/spec/rubocop/cop/rspec/multiple_memoized_helpers_spec.rb +++ b/spec/rubocop/cop/rspec/multiple_memoized_helpers_spec.rb @@ -1,7 +1,9 @@ # frozen_string_literal: true RSpec.describe RuboCop::Cop::RSpec::MultipleMemoizedHelpers do - let(:cop_config) { { 'Max' => 1 } } + let(:cop_config) do + { 'Max' => 1 } + end it 'flags excessive `#let`' do expect_offense(<<~RUBY) @@ -100,7 +102,9 @@ end context 'when using AllowSubject configuration' do - let(:cop_config) { { 'Max' => 1, 'AllowSubject' => false } } + let(:cop_config) do + { 'Max' => 1, 'AllowSubject' => false } + end it 'flags `#subject` without name' do expect_offense(<<~RUBY) diff --git a/spec/rubocop/cop/rspec/named_subject_spec.rb b/spec/rubocop/cop/rspec/named_subject_spec.rb index 9dfd179af..2f9289de9 100644 --- a/spec/rubocop/cop/rspec/named_subject_spec.rb +++ b/spec/rubocop/cop/rspec/named_subject_spec.rb @@ -61,7 +61,9 @@ def foo end context 'when IgnoreSharedExamples is false' do - let(:cop_config) { { 'IgnoreSharedExamples' => false } } + let(:cop_config) do + { 'IgnoreSharedExamples' => false } + end it_behaves_like 'checking subject outside of shared examples' @@ -92,7 +94,9 @@ def foo end context 'when IgnoreSharedExamples is true' do - let(:cop_config) { { 'IgnoreSharedExamples' => true } } + let(:cop_config) do + { 'IgnoreSharedExamples' => true } + end it_behaves_like 'checking subject outside of shared examples' diff --git a/spec/rubocop/cop/rspec/nested_groups_spec.rb b/spec/rubocop/cop/rspec/nested_groups_spec.rb index 622574faa..07d93dfe7 100644 --- a/spec/rubocop/cop/rspec/nested_groups_spec.rb +++ b/spec/rubocop/cop/rspec/nested_groups_spec.rb @@ -98,7 +98,9 @@ module MyNamespace end context 'when Max is configured as 2' do - let(:cop_config) { { 'Max' => '2' } } + let(:cop_config) do + { 'Max' => '2' } + end it 'flags two levels of nesting' do expect_offense(<<-RUBY) @@ -117,7 +119,9 @@ module MyNamespace end context 'when configured with MaxNesting' do - let(:cop_config) { { 'MaxNesting' => '1' } } + let(:cop_config) do + { 'MaxNesting' => '1' } + end it 'emits a deprecation warning' do expect { inspect_source('describe(Foo) { }', 'foo_spec.rb') } @@ -145,7 +149,9 @@ module MyNamespace end context 'when AllowedGroups is configured as' do - let(:cop_config) { { 'AllowedGroups' => ['path'] } } + let(:cop_config) do + { 'AllowedGroups' => ['path'] } + end it 'accept nested example groups defined inside `describe`' \ 'path is not counted' do diff --git a/spec/rubocop/cop/rspec/no_expectation_example_spec.rb b/spec/rubocop/cop/rspec/no_expectation_example_spec.rb index 74a7a923e..4403c1943 100644 --- a/spec/rubocop/cop/rspec/no_expectation_example_spec.rb +++ b/spec/rubocop/cop/rspec/no_expectation_example_spec.rb @@ -155,7 +155,9 @@ end context 'when `AllowedPatterns: [^expect_]`' do - let(:cop_config) { { 'AllowedPatterns' => ['^expect_'] } } + let(:cop_config) do + { 'AllowedPatterns' => ['^expect_'] } + end context 'when only allowed pattern methods are used' do it 'registers no offenses' do diff --git a/spec/rubocop/cop/rspec/not_to_not_spec.rb b/spec/rubocop/cop/rspec/not_to_not_spec.rb index 8d8dc7be3..e75fb85a8 100644 --- a/spec/rubocop/cop/rspec/not_to_not_spec.rb +++ b/spec/rubocop/cop/rspec/not_to_not_spec.rb @@ -2,7 +2,9 @@ RSpec.describe RuboCop::Cop::RSpec::NotToNot do context 'when EnforcedStyle is `not_to`' do - let(:cop_config) { { 'EnforcedStyle' => 'not_to' } } + let(:cop_config) do + { 'EnforcedStyle' => 'not_to' } + end it 'detects the `to_not` offense' do expect_offense(<<-RUBY) @@ -38,7 +40,9 @@ end context 'when AcceptedMethod is `to_not`' do - let(:cop_config) { { 'EnforcedStyle' => 'to_not' } } + let(:cop_config) do + { 'EnforcedStyle' => 'to_not' } + end it 'detects the `not_to` offense' do expect_offense(<<-RUBY) diff --git a/spec/rubocop/cop/rspec/predicate_matcher_spec.rb b/spec/rubocop/cop/rspec/predicate_matcher_spec.rb index 9d179644a..e86bb940b 100644 --- a/spec/rubocop/cop/rspec/predicate_matcher_spec.rb +++ b/spec/rubocop/cop/rspec/predicate_matcher_spec.rb @@ -6,10 +6,14 @@ 'Strict' => strict, 'AllowedExplicitMatchers' => allowed_explicit_matchers } end - let(:allowed_explicit_matchers) { [] } + let(:allowed_explicit_matchers) do + [] + end context 'when enforced style is `inflected`' do - let(:enforced_style) { 'inflected' } + let(:enforced_style) do + 'inflected' + end shared_examples 'inflected common' do it 'registers an offense for a predicate method in actual' do @@ -164,7 +168,9 @@ end context 'when strict is true' do - let(:strict) { true } + let(:strict) do + true + end include_examples 'inflected common' @@ -180,7 +186,9 @@ end context 'when strict is false' do - let(:strict) { false } + let(:strict) do + false + end include_examples 'inflected common' @@ -221,7 +229,9 @@ end context 'when enforced style is `explicit`' do - let(:enforced_style) { 'explicit' } + let(:enforced_style) do + 'explicit' + end shared_examples 'explicit common' do it 'registers an offense for a predicate mather' do @@ -271,7 +281,9 @@ end context 'when custom matchers are allowed' do - let(:allowed_explicit_matchers) { ['have_http_status'] } + let(:allowed_explicit_matchers) do + ['have_http_status'] + end it 'accepts custom allowed explicit matchers' do expect_no_offenses(<<-RUBY) @@ -348,14 +360,18 @@ end context 'when strict is true' do - let(:strict) { true } + let(:strict) do + true + end include_examples 'explicit common' include_examples 'explicit autocorrect', 'be(true)', 'be(false)' end context 'when strict is false' do - let(:strict) { false } + let(:strict) do + false + end include_examples 'explicit common' include_examples 'explicit autocorrect', 'be_truthy', 'be_falsey' diff --git a/spec/rubocop/cop/rspec/rails/http_status_spec.rb b/spec/rubocop/cop/rspec/rails/http_status_spec.rb index e3523bd4b..5e69b443c 100644 --- a/spec/rubocop/cop/rspec/rails/http_status_spec.rb +++ b/spec/rubocop/cop/rspec/rails/http_status_spec.rb @@ -2,7 +2,9 @@ RSpec.describe RuboCop::Cop::RSpec::Rails::HttpStatus do context 'when EnforcedStyle is `symbolic`' do - let(:cop_config) { { 'EnforcedStyle' => 'symbolic' } } + let(:cop_config) do + { 'EnforcedStyle' => 'symbolic' } + end it 'registers an offense when using numeric value' do expect_offense(<<-RUBY) @@ -42,7 +44,9 @@ end context 'when EnforcedStyle is `numeric`' do - let(:cop_config) { { 'EnforcedStyle' => 'numeric' } } + let(:cop_config) do + { 'EnforcedStyle' => 'numeric' } + end it 'registers an offense when using symbolic value' do expect_offense(<<-RUBY) diff --git a/spec/rubocop/cop/rspec/return_from_stub_spec.rb b/spec/rubocop/cop/rspec/return_from_stub_spec.rb index 5af3ca1bd..9c65ea872 100644 --- a/spec/rubocop/cop/rspec/return_from_stub_spec.rb +++ b/spec/rubocop/cop/rspec/return_from_stub_spec.rb @@ -6,7 +6,9 @@ end context 'with EnforcedStyle `and_return`' do - let(:enforced_style) { 'and_return' } + let(:enforced_style) do + 'and_return' + end it 'finds static values returned from block' do expect_offense(<<-RUBY) @@ -194,7 +196,9 @@ def stub_foo end context 'with EnforcedStyle `block`' do - let(:enforced_style) { 'block' } + let(:enforced_style) do + 'block' + end it 'registers an offense for static values returned from method' do expect_offense(<<-RUBY) diff --git a/spec/rubocop/cop/rspec/variable_definition_spec.rb b/spec/rubocop/cop/rspec/variable_definition_spec.rb index dda1c810f..2488535a9 100644 --- a/spec/rubocop/cop/rspec/variable_definition_spec.rb +++ b/spec/rubocop/cop/rspec/variable_definition_spec.rb @@ -2,7 +2,9 @@ RSpec.describe RuboCop::Cop::RSpec::VariableDefinition do context 'when EnforcedStyle is `symbols`' do - let(:cop_config) { { 'EnforcedStyle' => 'symbols' } } + let(:cop_config) do + { 'EnforcedStyle' => 'symbols' } + end it 'registers an offense for string name' do expect_offense(<<~RUBY) @@ -36,7 +38,9 @@ end context 'when EnforcedStyle is `strings`' do - let(:cop_config) { { 'EnforcedStyle' => 'strings' } } + let(:cop_config) do + { 'EnforcedStyle' => 'strings' } + end it 'registers an offense for symbol name' do expect_offense(<<~RUBY) diff --git a/spec/rubocop/cop/rspec/variable_name_spec.rb b/spec/rubocop/cop/rspec/variable_name_spec.rb index 199094cc5..5512eb856 100644 --- a/spec/rubocop/cop/rspec/variable_name_spec.rb +++ b/spec/rubocop/cop/rspec/variable_name_spec.rb @@ -2,7 +2,9 @@ RSpec.describe RuboCop::Cop::RSpec::VariableName do context 'when configured for `snake_case`' do - let(:cop_config) { { 'EnforcedStyle' => 'snake_case' } } + let(:cop_config) do + { 'EnforcedStyle' => 'snake_case' } + end context 'when `let` with symbol names' do it 'registers an offense for camelCase' do @@ -126,7 +128,9 @@ end context 'when configured for `camelCase`' do - let(:cop_config) { { 'EnforcedStyle' => 'camelCase' } } + let(:cop_config) do + { 'EnforcedStyle' => 'camelCase' } + end context 'when `let`' do it 'registers an offense for snake_case' do diff --git a/spec/rubocop/cop/rspec/verified_doubles_spec.rb b/spec/rubocop/cop/rspec/verified_doubles_spec.rb index 13e2f679f..3e2e20733 100644 --- a/spec/rubocop/cop/rspec/verified_doubles_spec.rb +++ b/spec/rubocop/cop/rspec/verified_doubles_spec.rb @@ -11,7 +11,9 @@ end context 'when configuration does not specify IgnoreSymbolicNames' do - let(:cop_config) { {} } + let(:cop_config) do + {} + end it 'find doubles whose name is a symbol' do expect_offense(<<-RUBY) @@ -33,7 +35,9 @@ end context 'when configured to ignore symbolic names' do - let(:cop_config) { { 'IgnoreSymbolicNames' => true } } + let(:cop_config) do + { 'IgnoreSymbolicNames' => true } + end it 'ignores doubles whose name is a symbol' do expect_no_offenses(<<-RUBY) @@ -54,7 +58,9 @@ end context 'when configured not to ignore nameless doubles' do - let(:cop_config) { { 'IgnoreNameless' => false } } + let(:cop_config) do + { 'IgnoreNameless' => false } + end it 'flags doubles that have no name specified' do expect_offense(<<-RUBY) diff --git a/spec/rubocop/rspec/example_group_spec.rb b/spec/rubocop/rspec/example_group_spec.rb index 78869cb1f..f180abbc2 100644 --- a/spec/rubocop/rspec/example_group_spec.rb +++ b/spec/rubocop/rspec/example_group_spec.rb @@ -3,9 +3,13 @@ RSpec.describe RuboCop::RSpec::ExampleGroup, :config do include RuboCop::AST::Sexp - subject(:group) { described_class.new(parse_source(source).ast) } + subject(:group) do + described_class.new(parse_source(source).ast) + end - let(:cop_class) { RuboCop::Cop::RSpec::Base } + let(:cop_class) do + RuboCop::Cop::RSpec::Base + end let(:source) do <<-RUBY diff --git a/spec/rubocop/rspec/example_spec.rb b/spec/rubocop/rspec/example_spec.rb index 8020c7ab5..356c71a25 100644 --- a/spec/rubocop/rspec/example_spec.rb +++ b/spec/rubocop/rspec/example_spec.rb @@ -3,7 +3,9 @@ RSpec.describe RuboCop::RSpec::Example, :config do include RuboCop::AST::Sexp - let(:cop_class) { RuboCop::Cop::RSpec::Base } + let(:cop_class) do + RuboCop::Cop::RSpec::Base + end # Trigger setting of the `Language` in the case when this spec # runs before cops' specs that set it. diff --git a/spec/rubocop/rspec/hook_spec.rb b/spec/rubocop/rspec/hook_spec.rb index dffb7d63d..6b3b19899 100644 --- a/spec/rubocop/rspec/hook_spec.rb +++ b/spec/rubocop/rspec/hook_spec.rb @@ -3,7 +3,9 @@ RSpec.describe RuboCop::RSpec::Hook, :config do include RuboCop::AST::Sexp - let(:cop_class) { RuboCop::Cop::RSpec::Base } + let(:cop_class) do + RuboCop::Cop::RSpec::Base + end # Trigger setting of the `Language` in the case when this spec # runs before cops' specs that set it. diff --git a/spec/rubocop/rspec/wording_spec.rb b/spec/rubocop/rspec/wording_spec.rb index 80f7c8a43..3fb79921e 100644 --- a/spec/rubocop/rspec/wording_spec.rb +++ b/spec/rubocop/rspec/wording_spec.rb @@ -1,8 +1,12 @@ # frozen_string_literal: true RSpec.describe RuboCop::RSpec::Wording do - let(:replacements) { { 'have' => 'has' } } - let(:ignores) { %w[only really] } + let(:replacements) do + { 'have' => 'has' } + end + let(:ignores) do + %w[only really] + end expected_rewrites = { diff --git a/spec/shared/smoke_test_examples.rb b/spec/shared/smoke_test_examples.rb index 85591467c..98249a360 100644 --- a/spec/shared/smoke_test_examples.rb +++ b/spec/shared/smoke_test_examples.rb @@ -6,7 +6,9 @@ # (so it is referenced in the 'config' shared context) but do not define # all of the dependent configuration options until inside of a context # that is out of scope, causing a NameError. - let(:cop_config) { {} } + let(:cop_config) do + {} + end stress_tests = Pathname.glob('spec/smoke_tests/*.rb')