Skip to content

Commit b15d623

Browse files
committed
implement repeated block description/body cops
1 parent e76f7d4 commit b15d623

File tree

9 files changed

+376
-0
lines changed

9 files changed

+376
-0
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
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/RepeatedBlockBody` copy. ([@lazycoder9][])
9+
* Add `RSpec/RepeatedBlockDescription` copy. ([@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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,16 @@ RSpec/ReceiveNever:
384384
Description: Prefer `not_to receive(...)` over `receive(...).never`.
385385
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ReceiveNever
386386

387+
RSpec/RepeatedBlockBody:
388+
Enabled: true
389+
Description: Check for repeated describe and context block body.
390+
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedBlockBody
391+
392+
RSpec/RepeatedBlockDescription:
393+
Enabled: true
394+
Description: Check for repeated describe and context block descriptions.
395+
StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedBlockDescription
396+
387397
RSpec/RepeatedDescription:
388398
Enabled: true
389399
Description: Check for repeated description strings in example groups.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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+
# # bad
29+
# context 'when case x' do
30+
# it { cool_predicate }
31+
# end
32+
#
33+
# context 'when case y' do
34+
# it { cool_predicate }
35+
# end
36+
#
37+
# # good
38+
# context 'when case x' do
39+
# it { cool_predicate }
40+
# end
41+
#
42+
# context 'when case y' do
43+
# it { another_predicate }
44+
# end
45+
#
46+
class RepeatedBlockBody < Cop
47+
MSG = "Don't repeat block body"
48+
49+
def_node_matcher :several_blocks?, <<-PATTERN
50+
(begin (block (send nil? {:context :describe} ...) ...)+ )
51+
PATTERN
52+
53+
def_node_matcher :extract_metadata, '(block (send _ _ _ $...) ...)'
54+
def_node_matcher :extract_body, '(block send args $...)'
55+
56+
def on_begin(node)
57+
return unless several_blocks?(node)
58+
59+
repeated_block_bodies(node).each { |child| add_offense(child) }
60+
end
61+
62+
private
63+
64+
def repeated_block_bodies(node)
65+
grouped_blocks = node
66+
.children
67+
.group_by do |child|
68+
[child.method_name, extract_metadata(child), extract_body(child)]
69+
end
70+
71+
grouped_blocks
72+
.select { |_, group| group.size > 1 }
73+
.values
74+
.flatten
75+
end
76+
end
77+
end
78+
end
79+
end
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module RSpec
6+
# Check for repeated describe and context block 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+
# describe 'when another case' do
43+
# # example group
44+
# end
45+
#
46+
class RepeatedBlockDescription < Cop
47+
MSG = "Don't repeat block descriptions"
48+
49+
def_node_matcher :several_blocks?, <<-PATTERN
50+
(begin (block (send nil? {:context :describe} ...) ...)+ )
51+
PATTERN
52+
53+
def_node_matcher :extract_doc_string, '(block (send _ _ $str ...) ...)'
54+
55+
def on_begin(node)
56+
return unless several_blocks?(node)
57+
58+
repeated_block_descriptions(node).each { |child| add_offense(child) }
59+
end
60+
61+
private
62+
63+
def repeated_block_descriptions(node)
64+
node
65+
.children
66+
.group_by { |child| [child.method_name, extract_doc_string(child)] }
67+
.select { |block, group| block && group.size > 1 }
68+
.values
69+
.flatten
70+
end
71+
end
72+
end
73+
end
74+
end

lib/rubocop/cop/rspec_cops.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@
7272
require_relative 'rspec/predicate_matcher'
7373
require_relative 'rspec/receive_counts'
7474
require_relative 'rspec/receive_never'
75+
require_relative 'rspec/repeated_block_body'
76+
require_relative 'rspec/repeated_block_description'
7577
require_relative 'rspec/repeated_description'
7678
require_relative 'rspec/repeated_example'
7779
require_relative 'rspec/return_from_stub'

manual/cops.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@
7171
* [RSpec/PredicateMatcher](cops_rspec.md#rspecpredicatematcher)
7272
* [RSpec/ReceiveCounts](cops_rspec.md#rspecreceivecounts)
7373
* [RSpec/ReceiveNever](cops_rspec.md#rspecreceivenever)
74+
* [RSpec/RepeatedBlockBody](cops_rspec.md#rspecrepeatedblockbody)
75+
* [RSpec/RepeatedBlockDescription](cops_rspec.md#rspecrepeatedblockdescription)
7476
* [RSpec/RepeatedDescription](cops_rspec.md#rspecrepeateddescription)
7577
* [RSpec/RepeatedExample](cops_rspec.md#rspecrepeatedexample)
7678
* [RSpec/ReturnFromStub](cops_rspec.md#rspecreturnfromstub)

manual/cops_rspec.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2490,6 +2490,110 @@ expect(foo).not_to receive(:bar)
24902490

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

2493+
## RSpec/RepeatedBlockBody
2494+
2495+
Enabled by default | Supports autocorrection
2496+
--- | ---
2497+
Enabled | No
2498+
2499+
Check for repeated describe and context block body.
2500+
2501+
### Examples
2502+
2503+
```ruby
2504+
# bad
2505+
describe 'cool feature x' do
2506+
it { cool_predicate }
2507+
end
2508+
2509+
describe 'cool feature y' do
2510+
it { cool_predicate }
2511+
end
2512+
2513+
# bad
2514+
context 'when case x' do
2515+
it { cool_predicate }
2516+
end
2517+
2518+
describe 'when case y' do
2519+
it { cool_predicate }
2520+
end
2521+
2522+
# good
2523+
describe 'cool feature' do
2524+
it { cool_predicate }
2525+
end
2526+
2527+
describe 'another cool feature' do
2528+
it { another_predicate }
2529+
end
2530+
2531+
# good
2532+
context 'when case x' do
2533+
it { cool_predicate }
2534+
end
2535+
2536+
describe 'when another case' do
2537+
it { another_predicate }
2538+
end
2539+
```
2540+
2541+
### References
2542+
2543+
* [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedBlockBody](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedBlockBody)
2544+
2545+
## RSpec/RepeatedBlockDescription
2546+
2547+
Enabled by default | Supports autocorrection
2548+
--- | ---
2549+
Enabled | No
2550+
2551+
Check for repeated describe and context block descriptions.
2552+
2553+
### Examples
2554+
2555+
```ruby
2556+
# bad
2557+
describe 'cool feature' do
2558+
# example group
2559+
end
2560+
2561+
describe 'cool feature' do
2562+
# example group
2563+
end
2564+
2565+
# bad
2566+
context 'when case x' do
2567+
# example group
2568+
end
2569+
2570+
describe 'when case x' do
2571+
# example group
2572+
end
2573+
2574+
# good
2575+
describe 'cool feature' do
2576+
# example group
2577+
end
2578+
2579+
describe 'another cool feature' do
2580+
# example group
2581+
end
2582+
2583+
# good
2584+
context 'when case x' do
2585+
# example group
2586+
end
2587+
2588+
describe 'when another case' do
2589+
# example group
2590+
end
2591+
```
2592+
2593+
### References
2594+
2595+
* [https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedBlockDescription](https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedBlockDescription)
2596+
24932597
## RSpec/RepeatedDescription
24942598

24952599
Enabled by default | Supports autocorrection
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::RSpec::RepeatedBlockBody do
4+
subject(:cop) { described_class.new }
5+
6+
it 'registers an offense for repeated describe body' do
7+
expect_offense(<<-RUBY)
8+
describe 'doing x' do
9+
^^^^^^^^^^^^^^^^^^^^^ Don't repeat block body
10+
it { cool_predicate_method }
11+
end
12+
13+
describe 'doing y' do
14+
^^^^^^^^^^^^^^^^^^^^^ Don't repeat block body
15+
it { cool_predicate_method }
16+
end
17+
RUBY
18+
end
19+
20+
it 'registers an offense for repeated context body' do
21+
expect_offense(<<-RUBY)
22+
context 'when awesome case' do
23+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't repeat block body
24+
it { cool_predicate_method }
25+
end
26+
27+
context 'when another awesome case' do
28+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Don't repeat block body
29+
it { cool_predicate_method }
30+
end
31+
RUBY
32+
end
33+
34+
it 'does not register offense for different block body implementation' do
35+
expect_no_offenses(<<-RUBY)
36+
context 'when awesome case' do
37+
it { cool_predicate_method }
38+
end
39+
40+
context 'when another awesome case' do
41+
it { another_predicate_method }
42+
end
43+
RUBY
44+
end
45+
46+
it 'does not register offense if rspec tag magic is involved' do
47+
expect_no_offenses(<<-RUBY)
48+
describe 'doing x' do
49+
it { cool_predicate_method }
50+
end
51+
52+
describe 'doing x', :request do
53+
it { cool_predicate_method }
54+
end
55+
RUBY
56+
end
57+
end

0 commit comments

Comments
 (0)