Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* Fix `RSpec/InstanceVariable` detection inside custom matchers. ([@pirj][])
* Fix `RSpec/ScatteredSetup` to distinguish hooks with different metadata. ([@pirj][])
* Add autocorrect support for `RSpec/ExpectActual` cop. ([@dduugg][], [@pirj][])
* Add `RSpec/RepeatedExampleGroupBody` cop. ([@lazycoder9][])
* Add `RSpec/RepeatedExampleGroupDescription` cop. ([@lazycoder9][])

## 1.37.1 (2019-12-16)

Expand Down Expand Up @@ -477,3 +479,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
[@jfragoulis]: https://github.com/jfragoulis
[@ybiquitous]: https://github.com/ybiquitous
[@dduugg]: https://github.com/dduugg
[@lazycoder9]: https://github.com/lazycoder9
10 changes: 10 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,16 @@ RSpec/RepeatedExample:
Description: Check for repeated examples within example groups.
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExample

RSpec/RepeatedExampleGroupBody:
Enabled: true
Description: Check for repeated describe and context block body.
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExampleGroupBody

RSpec/RepeatedExampleGroupDescription:
Enabled: true
Description: Check for repeated example group descriptions.
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExampleGroupDescription

RSpec/ReturnFromStub:
Enabled: true
Description: Checks for consistent style of stub's return setting.
Expand Down
87 changes: 87 additions & 0 deletions lib/rubocop/cop/rspec/repeated_example_group_body.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# frozen_string_literal: true

module RuboCop
module Cop
module RSpec
# Check for repeated describe and context block body.
#
# @example
#
# # bad
# describe 'cool feature x' do
# it { cool_predicate }
# end
#
# describe 'cool feature y' do
# it { cool_predicate }
# end
#
# # good
# describe 'cool feature' do
# it { cool_predicate }
# end
#
# describe 'another cool feature' do
# it { another_predicate }
# end
#
# # good
# context 'when case x', :tag do
# it { cool_predicate }
# end
#
# context 'when case y' do
# it { cool_predicate }
# end
#
class RepeatedExampleGroupBody < Cop
MSG = 'Repeated %<group>s block body on line(s) %<loc>s'

def_node_matcher :several_example_groups?, <<-PATTERN
(begin <#example_group_with_body? #example_group_with_body? ...>)
PATTERN

def_node_matcher :metadata, '(block (send _ _ _ $...) ...)'
def_node_matcher :body, '(block _ args $...)'

def_node_matcher :skip_or_pending?, <<-PATTERN
(block <(send nil? {:skip :pending}) ...>)
PATTERN

def on_begin(node)
return unless several_example_groups?(node)

repeated_group_bodies(node).each do |group, repeats|
add_offense(group, message: message(group, repeats))
end
end

private

def repeated_group_bodies(node)
node
.children
.select { |child| example_group_with_body?(child) }
.reject { |child| skip_or_pending?(child) }
.group_by { |group| signature_keys(group) }
.values
.reject(&:one?)
.flat_map { |groups| add_repeated_lines(groups) }
end

def add_repeated_lines(groups)
repeated_lines = groups.map(&:first_line)
groups.map { |group| [group, repeated_lines - [group.first_line]] }
end

def signature_keys(group)
[metadata(group), body(group)]
end

def message(group, repeats)
format(MSG, group: group.method_name, loc: repeats)
end
end
end
end
end
96 changes: 96 additions & 0 deletions lib/rubocop/cop/rspec/repeated_example_group_description.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# frozen_string_literal: true

module RuboCop
module Cop
module RSpec
# Check for repeated example group descriptions.
#
# @example
#
# # bad
# describe 'cool feature' do
# # example group
# end
#
# describe 'cool feature' do
# # example group
# end
#
# # bad
# context 'when case x' do
# # example group
# end
#
# describe 'when case x' do
# # example group
# end
#
# # good
# describe 'cool feature' do
# # example group
# end
#
# describe 'another cool feature' do
# # example group
# end
#
# # good
# context 'when case x' do
# # example group
# end
#
# context 'when another case' do
# # example group
# end
#
class RepeatedExampleGroupDescription < Cop
MSG = 'Repeated %<group>s block description on line(s) %<loc>s'

def_node_matcher :several_example_groups?, <<-PATTERN
(begin <#example_group? #example_group? ...>)
PATTERN

def_node_matcher :doc_string_and_metadata, <<-PATTERN
(block (send _ _ $_ $...) ...)
PATTERN

def_node_matcher :skip_or_pending?, <<-PATTERN
(block <(send nil? {:skip :pending}) ...>)
PATTERN

def_node_matcher :empty_description?, '(block (send _ _) ...)'

def on_begin(node)
return unless several_example_groups?(node)

repeated_group_descriptions(node).each do |group, repeats|
add_offense(group, message: message(group, repeats))
end
end

private

def repeated_group_descriptions(node)
node
.children
.select { |child| example_group?(child) }
.reject { |child| skip_or_pending?(child) }
.reject { |child| empty_description?(child) }
.group_by { |group| doc_string_and_metadata(group) }
.values
.reject(&:one?)
.flat_map { |groups| add_repeated_lines(groups) }
end

def add_repeated_lines(groups)
repeated_lines = groups.map(&:first_line)
groups.map { |group| [group, repeated_lines - [group.first_line]] }
end

def message(group, repeats)
format(MSG, group: group.method_name, loc: repeats)
end
end
end
end
end
2 changes: 2 additions & 0 deletions lib/rubocop/cop/rspec_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@
require_relative 'rspec/receive_never'
require_relative 'rspec/repeated_description'
require_relative 'rspec/repeated_example'
require_relative 'rspec/repeated_example_group_body'
require_relative 'rspec/repeated_example_group_description'
require_relative 'rspec/return_from_stub'
require_relative 'rspec/scattered_let'
require_relative 'rspec/scattered_setup'
Expand Down
2 changes: 2 additions & 0 deletions manual/cops.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
* [RSpec/ReceiveNever](cops_rspec.md#rspecreceivenever)
* [RSpec/RepeatedDescription](cops_rspec.md#rspecrepeateddescription)
* [RSpec/RepeatedExample](cops_rspec.md#rspecrepeatedexample)
* [RSpec/RepeatedExampleGroupBody](cops_rspec.md#rspecrepeatedexamplegroupbody)
* [RSpec/RepeatedExampleGroupDescription](cops_rspec.md#rspecrepeatedexamplegroupdescription)
* [RSpec/ReturnFromStub](cops_rspec.md#rspecreturnfromstub)
* [RSpec/ScatteredLet](cops_rspec.md#rspecscatteredlet)
* [RSpec/ScatteredSetup](cops_rspec.md#rspecscatteredsetup)
Expand Down
95 changes: 95 additions & 0 deletions manual/cops_rspec.md
Original file line number Diff line number Diff line change
Expand Up @@ -2552,6 +2552,101 @@ end

* [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExample](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExample)

## RSpec/RepeatedExampleGroupBody

Enabled by default | Supports autocorrection
--- | ---
Enabled | No

Check for repeated describe and context block body.

### Examples

```ruby
# bad
describe 'cool feature x' do
it { cool_predicate }
end

describe 'cool feature y' do
it { cool_predicate }
end

# good
describe 'cool feature' do
it { cool_predicate }
end

describe 'another cool feature' do
it { another_predicate }
end

# good
context 'when case x', :tag do
it { cool_predicate }
end

context 'when case y' do
it { cool_predicate }
end
```

### References

* [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExampleGroupBody](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExampleGroupBody)

## RSpec/RepeatedExampleGroupDescription

Enabled by default | Supports autocorrection
--- | ---
Enabled | No

Check for repeated example group descriptions.

### Examples

```ruby
# bad
describe 'cool feature' do
# example group
end

describe 'cool feature' do
# example group
end

# bad
context 'when case x' do
# example group
end

describe 'when case x' do
# example group
end

# good
describe 'cool feature' do
# example group
end

describe 'another cool feature' do
# example group
end

# good
context 'when case x' do
# example group
end

context 'when another case' do
# example group
end
```

### References

* [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExampleGroupDescription](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExampleGroupDescription)

## RSpec/ReturnFromStub

Enabled by default | Supports autocorrection
Expand Down
Loading