Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f7fe011

Browse files
authored
Merge pull request rubocop#860 from lazycoder9/cop/repeated_describe
[Close rubocop#856] implement repeated example group description/body cops
2 parents 218d2a0 + 32d2e2f commit f7fe011

9 files changed

+793
-0
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
* Fix `RSpec/InstanceVariable` detection inside custom matchers. ([@pirj][])
66
* Fix `RSpec/ScatteredSetup` to distinguish hooks with different metadata. ([@pirj][])
77
* Add autocorrect support for `RSpec/ExpectActual` cop. ([@dduugg][], [@pirj][])
8+
* Add `RSpec/RepeatedExampleGroupBody` cop. ([@lazycoder9][])
9+
* Add `RSpec/RepeatedExampleGroupDescription` cop. ([@lazycoder9][])
810

911
## 1.37.1 (2019-12-16)
1012

@@ -477,3 +479,4 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
477479
[@jfragoulis]: https://github.com/jfragoulis
478480
[@ybiquitous]: https://github.com/ybiquitous
479481
[@dduugg]: https://github.com/dduugg
482+
[@lazycoder9]: https://github.com/lazycoder9

config/default.yml

+10
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,16 @@ RSpec/RepeatedExample:
394394
Description: Check for repeated examples within example groups.
395395
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExample
396396

397+
RSpec/RepeatedExampleGroupBody:
398+
Enabled: true
399+
Description: Check for repeated describe and context block body.
400+
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExampleGroupBody
401+
402+
RSpec/RepeatedExampleGroupDescription:
403+
Enabled: true
404+
Description: Check for repeated example group descriptions.
405+
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExampleGroupDescription
406+
397407
RSpec/ReturnFromStub:
398408
Enabled: true
399409
Description: Checks for consistent style of stub's return setting.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module RSpec
6+
# Check for repeated describe and context block body.
7+
#
8+
# @example
9+
#
10+
# # bad
11+
# describe 'cool feature x' do
12+
# it { cool_predicate }
13+
# end
14+
#
15+
# describe 'cool feature y' do
16+
# it { cool_predicate }
17+
# end
18+
#
19+
# # good
20+
# describe 'cool feature' do
21+
# it { cool_predicate }
22+
# end
23+
#
24+
# describe 'another cool feature' do
25+
# it { another_predicate }
26+
# end
27+
#
28+
# # good
29+
# context 'when case x', :tag do
30+
# it { cool_predicate }
31+
# end
32+
#
33+
# context 'when case y' do
34+
# it { cool_predicate }
35+
# end
36+
#
37+
class RepeatedExampleGroupBody < Cop
38+
MSG = 'Repeated %<group>s block body on line(s) %<loc>s'
39+
40+
def_node_matcher :several_example_groups?, <<-PATTERN
41+
(begin <#example_group_with_body? #example_group_with_body? ...>)
42+
PATTERN
43+
44+
def_node_matcher :metadata, '(block (send _ _ _ $...) ...)'
45+
def_node_matcher :body, '(block _ args $...)'
46+
47+
def_node_matcher :skip_or_pending?, <<-PATTERN
48+
(block <(send nil? {:skip :pending}) ...>)
49+
PATTERN
50+
51+
def on_begin(node)
52+
return unless several_example_groups?(node)
53+
54+
repeated_group_bodies(node).each do |group, repeats|
55+
add_offense(group, message: message(group, repeats))
56+
end
57+
end
58+
59+
private
60+
61+
def repeated_group_bodies(node)
62+
node
63+
.children
64+
.select { |child| example_group_with_body?(child) }
65+
.reject { |child| skip_or_pending?(child) }
66+
.group_by { |group| signature_keys(group) }
67+
.values
68+
.reject(&:one?)
69+
.flat_map { |groups| add_repeated_lines(groups) }
70+
end
71+
72+
def add_repeated_lines(groups)
73+
repeated_lines = groups.map(&:first_line)
74+
groups.map { |group| [group, repeated_lines - [group.first_line]] }
75+
end
76+
77+
def signature_keys(group)
78+
[metadata(group), body(group)]
79+
end
80+
81+
def message(group, repeats)
82+
format(MSG, group: group.method_name, loc: repeats)
83+
end
84+
end
85+
end
86+
end
87+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module RSpec
6+
# Check for repeated example group descriptions.
7+
#
8+
# @example
9+
#
10+
# # bad
11+
# describe 'cool feature' do
12+
# # example group
13+
# end
14+
#
15+
# describe 'cool feature' do
16+
# # example group
17+
# end
18+
#
19+
# # bad
20+
# context 'when case x' do
21+
# # example group
22+
# end
23+
#
24+
# describe 'when case x' do
25+
# # example group
26+
# end
27+
#
28+
# # good
29+
# describe 'cool feature' do
30+
# # example group
31+
# end
32+
#
33+
# describe 'another cool feature' do
34+
# # example group
35+
# end
36+
#
37+
# # good
38+
# context 'when case x' do
39+
# # example group
40+
# end
41+
#
42+
# context 'when another case' do
43+
# # example group
44+
# end
45+
#
46+
class RepeatedExampleGroupDescription < Cop
47+
MSG = 'Repeated %<group>s block description on line(s) %<loc>s'
48+
49+
def_node_matcher :several_example_groups?, <<-PATTERN
50+
(begin <#example_group? #example_group? ...>)
51+
PATTERN
52+
53+
def_node_matcher :doc_string_and_metadata, <<-PATTERN
54+
(block (send _ _ $_ $...) ...)
55+
PATTERN
56+
57+
def_node_matcher :skip_or_pending?, <<-PATTERN
58+
(block <(send nil? {:skip :pending}) ...>)
59+
PATTERN
60+
61+
def_node_matcher :empty_description?, '(block (send _ _) ...)'
62+
63+
def on_begin(node)
64+
return unless several_example_groups?(node)
65+
66+
repeated_group_descriptions(node).each do |group, repeats|
67+
add_offense(group, message: message(group, repeats))
68+
end
69+
end
70+
71+
private
72+
73+
def repeated_group_descriptions(node)
74+
node
75+
.children
76+
.select { |child| example_group?(child) }
77+
.reject { |child| skip_or_pending?(child) }
78+
.reject { |child| empty_description?(child) }
79+
.group_by { |group| doc_string_and_metadata(group) }
80+
.values
81+
.reject(&:one?)
82+
.flat_map { |groups| add_repeated_lines(groups) }
83+
end
84+
85+
def add_repeated_lines(groups)
86+
repeated_lines = groups.map(&:first_line)
87+
groups.map { |group| [group, repeated_lines - [group.first_line]] }
88+
end
89+
90+
def message(group, repeats)
91+
format(MSG, group: group.method_name, loc: repeats)
92+
end
93+
end
94+
end
95+
end
96+
end

lib/rubocop/cop/rspec_cops.rb

+2
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@
7474
require_relative 'rspec/receive_never'
7575
require_relative 'rspec/repeated_description'
7676
require_relative 'rspec/repeated_example'
77+
require_relative 'rspec/repeated_example_group_body'
78+
require_relative 'rspec/repeated_example_group_description'
7779
require_relative 'rspec/return_from_stub'
7880
require_relative 'rspec/scattered_let'
7981
require_relative 'rspec/scattered_setup'

manual/cops.md

+2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@
7373
* [RSpec/ReceiveNever](cops_rspec.md#rspecreceivenever)
7474
* [RSpec/RepeatedDescription](cops_rspec.md#rspecrepeateddescription)
7575
* [RSpec/RepeatedExample](cops_rspec.md#rspecrepeatedexample)
76+
* [RSpec/RepeatedExampleGroupBody](cops_rspec.md#rspecrepeatedexamplegroupbody)
77+
* [RSpec/RepeatedExampleGroupDescription](cops_rspec.md#rspecrepeatedexamplegroupdescription)
7678
* [RSpec/ReturnFromStub](cops_rspec.md#rspecreturnfromstub)
7779
* [RSpec/ScatteredLet](cops_rspec.md#rspecscatteredlet)
7880
* [RSpec/ScatteredSetup](cops_rspec.md#rspecscatteredsetup)

manual/cops_rspec.md

+95
Original file line numberDiff line numberDiff line change
@@ -2556,6 +2556,101 @@ end
25562556

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

2559+
## RSpec/RepeatedExampleGroupBody
2560+
2561+
Enabled by default | Supports autocorrection
2562+
--- | ---
2563+
Enabled | No
2564+
2565+
Check for repeated describe and context block body.
2566+
2567+
### Examples
2568+
2569+
```ruby
2570+
# bad
2571+
describe 'cool feature x' do
2572+
it { cool_predicate }
2573+
end
2574+
2575+
describe 'cool feature y' do
2576+
it { cool_predicate }
2577+
end
2578+
2579+
# good
2580+
describe 'cool feature' do
2581+
it { cool_predicate }
2582+
end
2583+
2584+
describe 'another cool feature' do
2585+
it { another_predicate }
2586+
end
2587+
2588+
# good
2589+
context 'when case x', :tag do
2590+
it { cool_predicate }
2591+
end
2592+
2593+
context 'when case y' do
2594+
it { cool_predicate }
2595+
end
2596+
```
2597+
2598+
### References
2599+
2600+
* [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExampleGroupBody](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExampleGroupBody)
2601+
2602+
## RSpec/RepeatedExampleGroupDescription
2603+
2604+
Enabled by default | Supports autocorrection
2605+
--- | ---
2606+
Enabled | No
2607+
2608+
Check for repeated example group descriptions.
2609+
2610+
### Examples
2611+
2612+
```ruby
2613+
# bad
2614+
describe 'cool feature' do
2615+
# example group
2616+
end
2617+
2618+
describe 'cool feature' do
2619+
# example group
2620+
end
2621+
2622+
# bad
2623+
context 'when case x' do
2624+
# example group
2625+
end
2626+
2627+
describe 'when case x' do
2628+
# example group
2629+
end
2630+
2631+
# good
2632+
describe 'cool feature' do
2633+
# example group
2634+
end
2635+
2636+
describe 'another cool feature' do
2637+
# example group
2638+
end
2639+
2640+
# good
2641+
context 'when case x' do
2642+
# example group
2643+
end
2644+
2645+
context 'when another case' do
2646+
# example group
2647+
end
2648+
```
2649+
2650+
### References
2651+
2652+
* [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExampleGroupDescription](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExampleGroupDescription)
2653+
25592654
## RSpec/ReturnFromStub
25602655

25612656
Enabled by default | Supports autocorrection

0 commit comments

Comments
 (0)