diff --git a/CHANGELOG.md b/CHANGELOG.md index a8d0a0438..e86e30df1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Add autocorrect support for `RSpec/ExpectActual` cop. ([@dduugg][], [@pirj][]) * Add `RSpec/RepeatedExampleGroupBody` cop. ([@lazycoder9][]) * Add `RSpec/RepeatedExampleGroupDescription` cop. ([@lazycoder9][]) +* Add block name and other lines to `RSpec/ScatteredSetup` message. ([@elebow][]) ## 1.37.1 (2019-12-16) @@ -480,3 +481,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features. [@ybiquitous]: https://github.com/ybiquitous [@dduugg]: https://github.com/dduugg [@lazycoder9]: https://github.com/lazycoder9 +[@elebow]: https://github.com/elebow diff --git a/lib/rubocop/cop/rspec/scattered_setup.rb b/lib/rubocop/cop/rspec/scattered_setup.rb index f247ca2ee..f54a0a482 100644 --- a/lib/rubocop/cop/rspec/scattered_setup.rb +++ b/lib/rubocop/cop/rspec/scattered_setup.rb @@ -23,25 +23,43 @@ module RSpec # end # class ScatteredSetup < Cop - MSG = 'Do not define multiple hooks in the same example group.' + MSG = 'Do not define multiple `%s` hooks in the same '\ + 'example group (also defined on %s).' def on_block(node) return unless example_group?(node) - analyzable_hooks(node).each do |repeated_hook| - add_offense(repeated_hook) + repeated_hooks(node).each do |occurrences| + lines = occurrences.map(&:first_line) + + occurrences.each do |occurrence| + lines_except_current = lines - [occurrence.first_line] + message = format(MSG, hook_name: occurrences.first.method_name, + lines: lines_msg(lines_except_current)) + add_offense(occurrence, message: message) + end end end - def analyzable_hooks(node) - RuboCop::RSpec::ExampleGroup.new(node) + def repeated_hooks(node) + hooks = RuboCop::RSpec::ExampleGroup.new(node) .hooks .select(&:knowable_scope?) .group_by { |hook| [hook.name, hook.scope, hook.metadata] } .values .reject(&:one?) - .flatten - .map(&:to_node) + + hooks.map do |hook| + hook.map(&:to_node) + end + end + + def lines_msg(numbers) + if numbers.size == 1 + "line #{numbers.first}" + else + "lines #{numbers.join(', ')}" + end end end end diff --git a/spec/rubocop/cop/rspec/scattered_setup_spec.rb b/spec/rubocop/cop/rspec/scattered_setup_spec.rb index c6e1bf865..d0ccce9ac 100644 --- a/spec/rubocop/cop/rspec/scattered_setup_spec.rb +++ b/spec/rubocop/cop/rspec/scattered_setup_spec.rb @@ -7,9 +7,9 @@ expect_offense(<<-RUBY) describe Foo do before { bar } - ^^^^^^^^^^^^^^ Do not define multiple hooks in the same example group. + ^^^^^^^^^^^^^^ Do not define multiple `before` hooks in the same example group (also defined on line 3). before { baz } - ^^^^^^^^^^^^^^ Do not define multiple hooks in the same example group. + ^^^^^^^^^^^^^^ Do not define multiple `before` hooks in the same example group (also defined on line 2). end RUBY end @@ -17,12 +17,12 @@ it 'flags multiple hooks of the same scope with different symbols' do expect_offense(<<-RUBY) describe Foo do - before { bar } - ^^^^^^^^^^^^^^ Do not define multiple hooks in the same example group. - before(:each) { baz } - ^^^^^^^^^^^^^^^^^^^^^ Do not define multiple hooks in the same example group. - before(:example) { baz } - ^^^^^^^^^^^^^^^^^^^^^^^^ Do not define multiple hooks in the same example group. + after { bar } + ^^^^^^^^^^^^^ Do not define multiple `after` hooks in the same example group (also defined on lines 3, 4). + after(:each) { baz } + ^^^^^^^^^^^^^^^^^^^^ Do not define multiple `after` hooks in the same example group (also defined on lines 2, 4). + after(:example) { baz } + ^^^^^^^^^^^^^^^^^^^^^^^ Do not define multiple `after` hooks in the same example group (also defined on lines 2, 3). end RUBY end @@ -31,9 +31,9 @@ expect_offense(<<-RUBY) describe Foo do before(:all) { bar } - ^^^^^^^^^^^^^^^^^^^^ Do not define multiple hooks in the same example group. + ^^^^^^^^^^^^^^^^^^^^ Do not define multiple `before` hooks in the same example group (also defined on line 3). before(:all) { baz } - ^^^^^^^^^^^^^^^^^^^^ Do not define multiple hooks in the same example group. + ^^^^^^^^^^^^^^^^^^^^ Do not define multiple `before` hooks in the same example group (also defined on line 2). end RUBY end @@ -109,13 +109,13 @@ expect_offense(<<-RUBY) describe Foo do before(:each, :special_case) { foo } - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not define multiple hooks in the same example group. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not define multiple `before` hooks in the same example group (also defined on lines 3, 4, 5). before(:example, :special_case) { bar } - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not define multiple hooks in the same example group. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not define multiple `before` hooks in the same example group (also defined on lines 2, 4, 5). before(:example, special_case: true) { bar } - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not define multiple hooks in the same example group. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not define multiple `before` hooks in the same example group (also defined on lines 2, 3, 5). before(special_case: true) { bar } - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not define multiple hooks in the same example group. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not define multiple `before` hooks in the same example group (also defined on lines 2, 3, 4). before(:example, special_case: false) { bar } end RUBY