Skip to content

Commit eb3e0ff

Browse files
committed
Add RSpec/EmptyLineAfterExample cop
Fixes #822
1 parent 6d4a3c4 commit eb3e0ff

File tree

7 files changed

+275
-0
lines changed

7 files changed

+275
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* Fix `RSpec/ExampleWording` autocorrect of multi-line docstrings. ([@pirj][])
77
* Add `RSpec/ContextMethod` cop, to detect method names in `context`. ([@geniou][])
88
* Update RuboCop dependency to 0.68.1 with support for children matching node pattern syntax. ([@pirj][])
9+
* Add `RSpec/EmptyLineAfterExample` cop to check that there is an empty line after example blocks. ([@pirj][])
910

1011
## 1.35.0 (2019-08-02)
1112

config/default.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,12 @@ RSpec/EmptyExampleGroup:
100100
CustomIncludeMethods: []
101101
StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyExampleGroup
102102

103+
RSpec/EmptyLineAfterExample:
104+
Description: Checks if there is an empty line after example blocks.
105+
Enabled: true
106+
AllowConsecutiveOneLiners: true
107+
StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyLineAfterExample
108+
103109
RSpec/EmptyLineAfterExampleGroup:
104110
Description: Checks if there is an empty line after example group blocks.
105111
Enabled: true
Lines changed: 87 additions & 0 deletions
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+
# Checks if there is an empty line after example blocks.
7+
#
8+
# @example
9+
# # bad
10+
# RSpec.describe Foo do
11+
# it 'does this' do
12+
# end
13+
# it 'does that' do
14+
# end
15+
# end
16+
#
17+
# # good
18+
# RSpec.describe Foo do
19+
# it 'does this' do
20+
# end
21+
#
22+
# it 'does that' do
23+
# end
24+
# end
25+
#
26+
# # fair - it's ok to have non-separated one-liners
27+
# RSpec.describe Foo do
28+
# it { one }
29+
# it { two }
30+
# end
31+
#
32+
# @example with AllowConsecutiveOneLiners configuration
33+
#
34+
# # rubocop.yml
35+
# # RSpec/EmptyLineAfterExample:
36+
# # AllowConsecutiveOneLiners: false
37+
#
38+
# # bad
39+
# RSpec.describe Foo do
40+
# it { one }
41+
# it { two }
42+
# end
43+
#
44+
class EmptyLineAfterExample < Cop
45+
include RuboCop::RSpec::BlankLineSeparation
46+
47+
MSG = 'Add an empty line after `%<example>s`.'
48+
49+
def on_block(node)
50+
return unless example?(node)
51+
return if last_child?(node)
52+
return if allowed_one_liner?(node)
53+
54+
missing_separating_line(node) do |location|
55+
add_offense(node,
56+
location: location,
57+
message: format(MSG, example: node.method_name))
58+
end
59+
end
60+
61+
def allowed_one_liner?(node)
62+
consecutive_one_liner?(node) && allow_consecutive_one_liners?
63+
end
64+
65+
def allow_consecutive_one_liners?
66+
cop_config['AllowConsecutiveOneLiners']
67+
end
68+
69+
def consecutive_one_liner?(node)
70+
node.line_count == 1 && next_one_line_example?(node)
71+
end
72+
73+
def next_one_line_example?(node)
74+
next_sibling = next_sibling(node)
75+
return unless next_sibling
76+
return unless example?(next_sibling)
77+
78+
next_sibling.line_count == 1
79+
end
80+
81+
def next_sibling(node)
82+
node.parent.children[node.sibling_index + 1]
83+
end
84+
end
85+
end
86+
end
87+
end

lib/rubocop/cop/rspec_cops.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
require_relative 'rspec/described_class'
2828
require_relative 'rspec/dialect'
2929
require_relative 'rspec/empty_example_group'
30+
require_relative 'rspec/empty_line_after_example'
3031
require_relative 'rspec/empty_line_after_example_group'
3132
require_relative 'rspec/empty_line_after_final_let'
3233
require_relative 'rspec/empty_line_after_hook'

manual/cops.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* [RSpec/DescribedClass](cops_rspec.md#rspecdescribedclass)
2727
* [RSpec/Dialect](cops_rspec.md#rspecdialect)
2828
* [RSpec/EmptyExampleGroup](cops_rspec.md#rspecemptyexamplegroup)
29+
* [RSpec/EmptyLineAfterExample](cops_rspec.md#rspecemptylineafterexample)
2930
* [RSpec/EmptyLineAfterExampleGroup](cops_rspec.md#rspecemptylineafterexamplegroup)
3031
* [RSpec/EmptyLineAfterFinalLet](cops_rspec.md#rspecemptylineafterfinallet)
3132
* [RSpec/EmptyLineAfterHook](cops_rspec.md#rspecemptylineafterhook)

manual/cops_rspec.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,64 @@ CustomIncludeMethods | `[]` | Array
602602

603603
* [http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyExampleGroup](http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyExampleGroup)
604604

605+
## RSpec/EmptyLineAfterExample
606+
607+
Enabled by default | Supports autocorrection
608+
--- | ---
609+
Enabled | Yes
610+
611+
Checks if there is an empty line after example blocks.
612+
613+
### Examples
614+
615+
```ruby
616+
# bad
617+
RSpec.describe Foo do
618+
it 'does this' do
619+
end
620+
it 'does that' do
621+
end
622+
end
623+
624+
# good
625+
RSpec.describe Foo do
626+
it 'does this' do
627+
end
628+
629+
it 'does that' do
630+
end
631+
end
632+
633+
# fair - it's ok to have non-separated one-liners
634+
RSpec.describe Foo do
635+
it { one }
636+
it { two }
637+
end
638+
```
639+
#### with AllowConsecutiveOneLiners configuration
640+
641+
```ruby
642+
# rubocop.yml
643+
# RSpec/EmptyLineAfterExample:
644+
# AllowConsecutiveOneLiners: false
645+
646+
# bad
647+
RSpec.describe Foo do
648+
it { one }
649+
it { two }
650+
end
651+
```
652+
653+
### Configurable attributes
654+
655+
Name | Default value | Configurable values
656+
--- | --- | ---
657+
AllowConsecutiveOneLiners | `true` | Boolean
658+
659+
### References
660+
661+
* [http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyLineAfterExample](http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/EmptyLineAfterExample)
662+
605663
## RSpec/EmptyLineAfterExampleGroup
606664

607665
Enabled by default | Supports autocorrection
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::RSpec::EmptyLineAfterExample, :config do
4+
subject(:cop) { described_class.new(config) }
5+
6+
let(:cop_config) { { 'AllowConsecutiveOneLiners' => true } }
7+
8+
it 'flags a missing empty line after `it`' do
9+
expect_offense(<<-RUBY)
10+
RSpec.describe Foo do
11+
it 'does this' do
12+
end
13+
^^^ Add an empty line after `it`.
14+
it 'does that' do
15+
end
16+
end
17+
RUBY
18+
19+
expect_correction(<<-RUBY)
20+
RSpec.describe Foo do
21+
it 'does this' do
22+
end
23+
24+
it 'does that' do
25+
end
26+
end
27+
RUBY
28+
end
29+
30+
it 'flags one-line examples' do
31+
expect_offense(<<-RUBY)
32+
RSpec.describe Foo do
33+
it { }
34+
^^^^^^ Add an empty line after `it`.
35+
it 'does that' do
36+
end
37+
end
38+
RUBY
39+
40+
expect_correction(<<-RUBY)
41+
RSpec.describe Foo do
42+
it { }
43+
44+
it 'does that' do
45+
end
46+
end
47+
RUBY
48+
end
49+
50+
it 'flags a missing empty line after `specify`' do
51+
expect_offense(<<-RUBY)
52+
RSpec.context 'foo' do
53+
specify do
54+
end
55+
^^^ Add an empty line after `specify`.
56+
specify 'something gets done' do
57+
end
58+
end
59+
RUBY
60+
61+
expect_correction(<<-RUBY)
62+
RSpec.context 'foo' do
63+
specify do
64+
end
65+
66+
specify 'something gets done' do
67+
end
68+
end
69+
RUBY
70+
end
71+
72+
it 'ignores when an empty line is present' do
73+
expect_no_offenses(<<-RUBY)
74+
RSpec.describe Foo do
75+
it 'does this' do
76+
end
77+
78+
it 'does that' do
79+
end
80+
end
81+
RUBY
82+
end
83+
84+
it 'ignores consecutive one-liners' do
85+
expect_no_offenses(<<-RUBY)
86+
RSpec.describe Foo do
87+
it { one }
88+
it { two }
89+
end
90+
RUBY
91+
end
92+
93+
it 'flags mixed one-line and multi-line examples' do
94+
expect_offense(<<-RUBY)
95+
RSpec.context 'foo' do
96+
it { }
97+
it { }
98+
^^^^^^ Add an empty line after `it`.
99+
it 'does this' do
100+
end
101+
^^^ Add an empty line after `it`.
102+
it { }
103+
it { }
104+
end
105+
RUBY
106+
end
107+
108+
context 'when AllowConsecutiveOneLiners is false' do
109+
let(:cop_config) { { 'AllowConsecutiveOneLiners' => false } }
110+
111+
it 'ignores consecutive one-liners' do
112+
expect_offense(<<-RUBY)
113+
RSpec.describe Foo do
114+
it { one }
115+
^^^^^^^^^^ Add an empty line after `it`.
116+
it { two }
117+
end
118+
RUBY
119+
end
120+
end
121+
end

0 commit comments

Comments
 (0)