rubocop-rspec 1.25.1 → 1.26.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +43 -6
- data/config/default.yml +5 -0
- data/lib/rubocop/cop/rspec/align_left_let_brace.rb +2 -0
- data/lib/rubocop/cop/rspec/align_right_let_brace.rb +2 -0
- data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +14 -3
- data/lib/rubocop/cop/rspec/describe_class.rb +1 -3
- data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +25 -8
- data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +1 -1
- data/lib/rubocop/cop/rspec/instance_spy.rb +1 -3
- data/lib/rubocop/cop/rspec/leading_subject.rb +1 -1
- data/lib/rubocop/cop/rspec/let_before_examples.rb +2 -1
- data/lib/rubocop/cop/rspec/message_chain.rb +3 -1
- data/lib/rubocop/cop/rspec/named_subject.rb +1 -11
- data/lib/rubocop/cop/rspec/overwriting_setup.rb +12 -6
- data/lib/rubocop/cop/rspec/pending.rb +0 -3
- data/lib/rubocop/cop/rspec/receive_counts.rb +86 -0
- data/lib/rubocop/cop/rspec/scattered_let.rb +1 -1
- data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +1 -1
- data/lib/rubocop/cop/rspec_cops.rb +1 -0
- data/lib/rubocop/rspec/align_let_brace.rb +1 -1
- data/lib/rubocop/rspec/example_group.rb +5 -7
- data/lib/rubocop/rspec/language.rb +1 -5
- data/lib/rubocop/rspec/version.rb +1 -1
- data/rubocop-rspec.gemspec +2 -2
- data/spec/rubocop/cop/rspec/align_left_let_brace_spec.rb +4 -0
- data/spec/rubocop/cop/rspec/align_right_let_brace_spec.rb +4 -0
- data/spec/rubocop/cop/rspec/capybara/feature_methods_spec.rb +9 -0
- data/spec/rubocop/cop/rspec/empty_example_group_spec.rb +14 -0
- data/spec/rubocop/cop/rspec/empty_line_after_final_let_spec.rb +49 -0
- data/spec/rubocop/cop/rspec/empty_line_after_subject_spec.rb +21 -1
- data/spec/rubocop/cop/rspec/overwriting_setup_spec.rb +22 -0
- data/spec/rubocop/cop/rspec/receive_counts_spec.rb +88 -0
- data/spec/rubocop/rspec/language/selector_set_spec.rb +4 -2
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 25c78adb48d8a15bb6229d1d8140abcd3f2e455c846288276ad01cedba024b4a
|
4
|
+
data.tar.gz: 0e2fbf154570fca1aaf0a32cf7866902424bab10da2854038f5674c8664a2d11
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7cc9ec95f341241d6c02083673e09ebd861929d846baae085c927a185998da95f573a18f0e7f3b9e7cdaba7f62812d7b80338534742fb2bde205df2fe29e0f3b
|
7
|
+
data.tar.gz: 891e9714f685e8a3b1bee0ef80395cc11fe87eccb3226cd1bef201a90379e4424cbc5bd937cc632ba11efb7463f0654d756450e4ba44556e93dde7ef0838ab1a
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,13 @@
|
|
2
2
|
|
3
3
|
## Master (Unreleased)
|
4
4
|
|
5
|
+
## 1.26.0 (2018-06-06)
|
6
|
+
|
7
|
+
* Fix false positive in `RSpec/EmptyExampleGroup` cop when methods named like a RSpec method are used. ([@Darhazer][])
|
8
|
+
* Fix `Capybara/FeatureMethods` not working when there is require before the spec. ([@Darhazer][])
|
9
|
+
* Fix `RSpec/EmptyLineAfterFinalLet`: allow a comment to be placed after latest let, requiring empty line after the comment. ([@Darhazer][])
|
10
|
+
* Add `RSpec/ReceiveCounts` cop to enforce usage of :once and :twice matchers. ([@Darhazer][])
|
11
|
+
|
5
12
|
## 1.25.1 (2018-04-10)
|
6
13
|
|
7
14
|
* Fix false positive in `RSpec/Pending` cop when pending is used as a method name. ([@Darhazer][])
|
data/README.md
CHANGED
@@ -2,14 +2,13 @@
|
|
2
2
|
|
3
3
|
[![Join the chat at https://gitter.im/rubocop-rspec/Lobby](https://badges.gitter.im/rubocop-rspec/Lobby.svg)](https://gitter.im/rubocop-rspec/Lobby)
|
4
4
|
[![Gem Version](https://badge.fury.io/rb/rubocop-rspec.svg)](https://rubygems.org/gems/rubocop-rspec)
|
5
|
-
[![CircleCI](https://circleci.com/gh/rubocop-
|
6
|
-
[![Test Coverage](https://api.codeclimate.com/v1/badges/
|
7
|
-
[![Maintainability](https://api.codeclimate.com/v1/badges/
|
5
|
+
[![CircleCI](https://circleci.com/gh/rubocop-hq/rubocop-rspec.svg?style=svg)](https://circleci.com/gh/rubocop-hq/rubocop-rspec)
|
6
|
+
[![Test Coverage](https://api.codeclimate.com/v1/badges/8ffaabf633c968c22bdd/test_coverage)](https://codeclimate.com/github/rubocop-hq/rubocop-rspec/test_coverage)
|
7
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/8ffaabf633c968c22bdd/maintainability)](https://codeclimate.com/github/rubocop-hq/rubocop-rspec/maintainability)
|
8
8
|
|
9
9
|
RSpec-specific analysis for your projects, as an extension to
|
10
10
|
[RuboCop](https://github.com/bbatsov/rubocop).
|
11
11
|
|
12
|
-
|
13
12
|
## Installation
|
14
13
|
|
15
14
|
Just install the `rubocop-rspec` gem
|
@@ -24,7 +23,6 @@ or if you use bundler put this in your `Gemfile`
|
|
24
23
|
gem 'rubocop-rspec'
|
25
24
|
```
|
26
25
|
|
27
|
-
|
28
26
|
## Usage
|
29
27
|
|
30
28
|
You need to tell RuboCop to load the RSpec extension. There are three
|
@@ -98,10 +96,49 @@ RSpec/FilePath:
|
|
98
96
|
- spec/my_poorly_named_spec_file.rb
|
99
97
|
```
|
100
98
|
|
99
|
+
## Non-goals of RuboCop RSpec
|
100
|
+
|
101
|
+
### Enforcing `should` vs. `expect` syntax
|
102
|
+
|
103
|
+
Enforcing
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
expect(calculator.compute(line_item)).to eq(5)
|
107
|
+
```
|
108
|
+
|
109
|
+
over
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
calculator.compute(line_item).should == 5
|
113
|
+
```
|
114
|
+
|
115
|
+
is a feature of RSpec itself – you can read about it in the [RSpec Documentation](https://relishapp.com/rspec/rspec-expectations/docs/syntax-configuration#disable-should-syntax)
|
116
|
+
|
117
|
+
### Enforcing an explicit RSpec receiver for top-level methods (disabling monkey patching)
|
118
|
+
|
119
|
+
Enforcing
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
Rspec.describe MyClass do
|
123
|
+
...
|
124
|
+
end
|
125
|
+
```
|
126
|
+
|
127
|
+
over
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
describe MyClass do
|
131
|
+
...
|
132
|
+
end
|
133
|
+
```
|
134
|
+
|
135
|
+
can be achieved using RSpec's `disable_monkey_patching!` method, which you can read more about in the [RSpec Documentation](https://relishapp.com/rspec/rspec-core/v/3-7/docs/configuration/zero-monkey-patching-mode#monkey-patched-methods-are-undefined-with-%60disable-monkey-patching!%60). This will also prevent `should` from being defined on every object in your system.
|
136
|
+
|
137
|
+
Before disabling `should` you will need all your specs to use the `expect` syntax. You can use [Transpec](http://yujinakayama.me/transpec/), which will do the conversion for you.
|
101
138
|
|
102
139
|
## Contributing
|
103
140
|
|
104
|
-
Checkout the [contribution guidelines](.github/CONTRIBUTING.md)
|
141
|
+
Checkout the [contribution guidelines](.github/CONTRIBUTING.md).
|
105
142
|
|
106
143
|
## License
|
107
144
|
|
data/config/default.yml
CHANGED
@@ -299,6 +299,11 @@ RSpec/Pending:
|
|
299
299
|
Description: Checks for any pending or skipped examples.
|
300
300
|
StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Pending
|
301
301
|
|
302
|
+
RSpec/ReceiveCounts:
|
303
|
+
Enabled: true
|
304
|
+
Description: Check for `once` and `twice` receive counts matchers usage.
|
305
|
+
StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/ReceiveCounts
|
306
|
+
|
302
307
|
RSpec/RepeatedDescription:
|
303
308
|
Enabled: true
|
304
309
|
Description: Check for repeated description strings in example groups.
|
@@ -66,7 +66,7 @@ module RuboCop
|
|
66
66
|
PATTERN
|
67
67
|
|
68
68
|
def on_block(node)
|
69
|
-
return unless
|
69
|
+
return unless inside_spec?(node)
|
70
70
|
|
71
71
|
feature_method(node) do |send_node, match|
|
72
72
|
next if enabled?(match)
|
@@ -87,8 +87,19 @@ module RuboCop
|
|
87
87
|
|
88
88
|
private
|
89
89
|
|
90
|
-
def
|
91
|
-
|
90
|
+
def inside_spec?(node)
|
91
|
+
return spec?(node) if root_node?(node)
|
92
|
+
|
93
|
+
root = node.ancestors.find { |parent| root_node?(parent) }
|
94
|
+
spec?(root)
|
95
|
+
end
|
96
|
+
|
97
|
+
def root_node?(node)
|
98
|
+
node.parent.nil? || root_with_siblings?(node.parent)
|
99
|
+
end
|
100
|
+
|
101
|
+
def root_with_siblings?(node)
|
102
|
+
node.begin_type? && node.parent.nil?
|
92
103
|
end
|
93
104
|
|
94
105
|
def enabled?(method_name)
|
@@ -39,9 +39,7 @@ module RuboCop
|
|
39
39
|
(sym {:request :feature :system :routing :view}))
|
40
40
|
PATTERN
|
41
41
|
|
42
|
-
def_node_matcher :shared_group?,
|
43
|
-
(block (send {(const nil? :RSpec) nil?} #{SharedGroups::ALL.node_pattern_union} ...) ...)
|
44
|
-
PATTERN
|
42
|
+
def_node_matcher :shared_group?, SharedGroups::ALL.block_pattern
|
45
43
|
|
46
44
|
def on_top_level_describe(node, args)
|
47
45
|
return if shared_group?(root_node)
|
@@ -17,9 +17,11 @@ module RuboCop
|
|
17
17
|
#
|
18
18
|
# it { does_something }
|
19
19
|
class EmptyLineAfterFinalLet < Cop
|
20
|
+
include RangeHelp
|
21
|
+
|
20
22
|
MSG = 'Add an empty line after the last `let` block.'.freeze
|
21
23
|
|
22
|
-
def_node_matcher :let?,
|
24
|
+
def_node_matcher :let?, Helpers::ALL.block_pattern
|
23
25
|
|
24
26
|
def on_block(node)
|
25
27
|
return unless example_group_with_body?(node)
|
@@ -29,24 +31,28 @@ module RuboCop
|
|
29
31
|
return if latest_let.nil?
|
30
32
|
return if latest_let.equal?(node.body.children.last)
|
31
33
|
|
32
|
-
no_new_line_after(latest_let) do
|
33
|
-
add_offense(latest_let, location:
|
34
|
+
no_new_line_after(latest_let) do |location|
|
35
|
+
add_offense(latest_let, location: location)
|
34
36
|
end
|
35
37
|
end
|
36
38
|
|
37
39
|
def autocorrect(node)
|
38
|
-
|
39
|
-
|
40
|
+
lambda do |corrector|
|
41
|
+
no_new_line_after(node) do |location|
|
42
|
+
corrector.insert_after(location.end, "\n")
|
43
|
+
end
|
44
|
+
end
|
40
45
|
end
|
41
46
|
|
42
47
|
private
|
43
48
|
|
44
49
|
def no_new_line_after(node)
|
45
50
|
loc = last_node_loc(node)
|
51
|
+
line = loc.line
|
52
|
+
line += 1 while comment_line?(processed_source[line])
|
46
53
|
|
47
|
-
|
48
|
-
|
49
|
-
yield unless next_line.blank?
|
54
|
+
return if processed_source[line].blank?
|
55
|
+
yield offending_loc(node, line)
|
50
56
|
end
|
51
57
|
|
52
58
|
def last_node_loc(node)
|
@@ -62,6 +68,17 @@ module RuboCop
|
|
62
68
|
|
63
69
|
node.each_child_node { |child| heredoc_line(child, &block) }
|
64
70
|
end
|
71
|
+
|
72
|
+
def offending_loc(node, last_line)
|
73
|
+
offending_line = processed_source[last_line - 1]
|
74
|
+
if comment_line?(offending_line)
|
75
|
+
start = offending_line.index('#')
|
76
|
+
length = offending_line.length - start
|
77
|
+
source_range(processed_source.buffer, last_line, start, length)
|
78
|
+
else
|
79
|
+
node.loc.expression
|
80
|
+
end
|
81
|
+
end
|
65
82
|
end
|
66
83
|
end
|
67
84
|
end
|
@@ -17,7 +17,7 @@ module RuboCop
|
|
17
17
|
class EmptyLineAfterSubject < Cop
|
18
18
|
MSG = 'Add empty line after `subject`.'.freeze
|
19
19
|
|
20
|
-
def_node_matcher :subject?,
|
20
|
+
def_node_matcher :subject?, Subject::ALL.block_pattern
|
21
21
|
|
22
22
|
def on_block(node)
|
23
23
|
return unless subject?(node) && !in_spec_block?(node)
|
@@ -22,9 +22,7 @@ module RuboCop
|
|
22
22
|
MSG = 'Use `instance_spy` when you check your double '\
|
23
23
|
'with `have_received`.'.freeze
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
def_node_matcher :example?, "(block $(send nil? #{EXAMPLES}) ...)"
|
25
|
+
def_node_matcher :example?, Examples::ALL.block_pattern
|
28
26
|
|
29
27
|
def_node_search :null_double, <<-PATTERN
|
30
28
|
(lvasgn $_
|
@@ -31,7 +31,7 @@ module RuboCop
|
|
31
31
|
|
32
32
|
MSG = 'Declare `subject` above any other `let` declarations.'.freeze
|
33
33
|
|
34
|
-
def_node_matcher :subject?,
|
34
|
+
def_node_matcher :subject?, Subject::ALL.block_pattern
|
35
35
|
|
36
36
|
def on_block(node)
|
37
37
|
return unless subject?(node) && !in_spec_block?(node)
|
@@ -35,7 +35,8 @@ module RuboCop
|
|
35
35
|
|
36
36
|
MSG = 'Move `let` before the examples in the group.'.freeze
|
37
37
|
|
38
|
-
def_node_matcher :let?,
|
38
|
+
def_node_matcher :let?, Helpers::ALL.block_pattern
|
39
|
+
|
39
40
|
def_node_matcher :example_or_group?, <<-PATTERN
|
40
41
|
{
|
41
42
|
#{(Examples::ALL + ExampleGroups::ALL).block_pattern}
|
@@ -14,7 +14,9 @@ module RuboCop
|
|
14
14
|
class MessageChain < Cop
|
15
15
|
MSG = 'Avoid stubbing using `%<method>s`.'.freeze
|
16
16
|
|
17
|
-
def_node_matcher :message_chain,
|
17
|
+
def_node_matcher :message_chain, <<-PATTERN
|
18
|
+
(send _ {:receive_message_chain :stub_chain} ...)
|
19
|
+
PATTERN
|
18
20
|
|
19
21
|
def on_send(node)
|
20
22
|
message_chain(node) { add_offense(node, location: :selector) }
|
@@ -48,7 +48,7 @@ module RuboCop
|
|
48
48
|
}
|
49
49
|
PATTERN
|
50
50
|
|
51
|
-
|
51
|
+
def_node_search :subject_usage, '$(send nil? :subject)'
|
52
52
|
|
53
53
|
def on_block(node)
|
54
54
|
return unless rspec_block?(node)
|
@@ -57,16 +57,6 @@ module RuboCop
|
|
57
57
|
add_offense(subject_node, location: :selector)
|
58
58
|
end
|
59
59
|
end
|
60
|
-
|
61
|
-
private
|
62
|
-
|
63
|
-
def subject_usage(node, &block)
|
64
|
-
unnamed_subject(node, &block)
|
65
|
-
|
66
|
-
node.each_child_node do |child|
|
67
|
-
subject_usage(child, &block)
|
68
|
-
end
|
69
|
-
end
|
70
60
|
end
|
71
61
|
end
|
72
62
|
end
|
@@ -22,11 +22,11 @@ module RuboCop
|
|
22
22
|
# let(:baz) { baz }
|
23
23
|
# let!(:other) { other }
|
24
24
|
class OverwritingSetup < Cop
|
25
|
+
include RuboCop::RSpec::Util
|
26
|
+
|
25
27
|
MSG = '`%<name>s` is already defined.'.freeze
|
26
28
|
|
27
|
-
def_node_matcher :setup?,
|
28
|
-
(block (send nil? {:let :let! :subject} (sym $_)) ...)
|
29
|
-
PATTERN
|
29
|
+
def_node_matcher :setup?, (Helpers::ALL + Subject::ALL).block_pattern
|
30
30
|
|
31
31
|
def on_block(node)
|
32
32
|
return unless example_group_with_body?(node)
|
@@ -45,9 +45,15 @@ module RuboCop
|
|
45
45
|
def find_duplicates(node)
|
46
46
|
setup_expressions = Set.new
|
47
47
|
node.each_child_node do |child|
|
48
|
-
setup?(child)
|
49
|
-
|
50
|
-
|
48
|
+
next unless setup?(child)
|
49
|
+
|
50
|
+
name = if child.send_node.arguments?
|
51
|
+
child.send_node.first_argument.value
|
52
|
+
else
|
53
|
+
:subject
|
54
|
+
end
|
55
|
+
|
56
|
+
yield child, name unless setup_expressions.add?(name)
|
51
57
|
end
|
52
58
|
end
|
53
59
|
end
|
@@ -43,11 +43,8 @@ module RuboCop
|
|
43
43
|
|
44
44
|
def_node_matcher :pending_block?, PENDING_EXAMPLES.send_pattern
|
45
45
|
|
46
|
-
def_node_matcher :rspec?, '(send {(const nil? :RSpec) nil?} ...)'
|
47
|
-
|
48
46
|
def on_send(node)
|
49
47
|
return unless pending_block?(node) || skipped_from_metadata?(node)
|
50
|
-
return unless rspec?(node)
|
51
48
|
|
52
49
|
add_offense(node, location: :expression)
|
53
50
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module RuboCop
|
2
|
+
module Cop
|
3
|
+
module RSpec
|
4
|
+
# Check for `once` and `twice` receive counts matchers usage.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
#
|
8
|
+
# # bad
|
9
|
+
# expect(foo).to receive(:bar).exactly(1).times
|
10
|
+
# expect(foo).to receive(:bar).exactly(2).times
|
11
|
+
# expect(foo).to receive(:bar).at_least(1).times
|
12
|
+
# expect(foo).to receive(:bar).at_least(2).times
|
13
|
+
# expect(foo).to receive(:bar).at_most(1).times
|
14
|
+
# expect(foo).to receive(:bar).at_most(2).times
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# expect(foo).to receive(:bar).once
|
18
|
+
# expect(foo).to receive(:bar).twice
|
19
|
+
# expect(foo).to receive(:bar).at_least(:once)
|
20
|
+
# expect(foo).to receive(:bar).at_least(:twice)
|
21
|
+
# expect(foo).to receive(:bar).at_most(:once)
|
22
|
+
# expect(foo).to receive(:bar).at_most(:twice).times
|
23
|
+
#
|
24
|
+
class ReceiveCounts < Cop
|
25
|
+
include RangeHelp
|
26
|
+
|
27
|
+
MSG = 'Use `%<alternative>s` instead of `%<original>s`.'.freeze
|
28
|
+
|
29
|
+
def_node_matcher :receive_counts, <<-PATTERN
|
30
|
+
(send $(send _ {:exactly :at_least :at_most} (int {1 2})) :times)
|
31
|
+
PATTERN
|
32
|
+
|
33
|
+
def on_send(node)
|
34
|
+
receive_counts(node) do |offending_node|
|
35
|
+
offending_range = range(node, offending_node)
|
36
|
+
|
37
|
+
add_offense(
|
38
|
+
offending_node,
|
39
|
+
message: message_for(offending_node, offending_range.source),
|
40
|
+
location: offending_range
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def autocorrect(node)
|
46
|
+
lambda do |corrector|
|
47
|
+
replacement = matcher_for(
|
48
|
+
node.method_name,
|
49
|
+
node.first_argument.source.to_i
|
50
|
+
)
|
51
|
+
corrector.replace(
|
52
|
+
range(node.parent, node),
|
53
|
+
replacement
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def message_for(node, source)
|
61
|
+
alternative = matcher_for(
|
62
|
+
node.method_name,
|
63
|
+
node.first_argument.source.to_i
|
64
|
+
)
|
65
|
+
format(MSG, alternative: alternative, original: source)
|
66
|
+
end
|
67
|
+
|
68
|
+
def matcher_for(method, count)
|
69
|
+
matcher = count == 1 ? 'once' : 'twice'
|
70
|
+
if method == :exactly
|
71
|
+
".#{matcher}"
|
72
|
+
else
|
73
|
+
".#{method}(:#{matcher})"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def range(node, offending_node)
|
78
|
+
range_between(
|
79
|
+
offending_node.loc.selector.begin_pos - 1, # match the dot as well
|
80
|
+
node.loc.expression.end_pos
|
81
|
+
)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -29,7 +29,7 @@ module RuboCop
|
|
29
29
|
class ScatteredLet < Cop
|
30
30
|
MSG = 'Group all let/let! blocks in the example group together.'.freeze
|
31
31
|
|
32
|
-
def_node_matcher :let?,
|
32
|
+
def_node_matcher :let?, Helpers::ALL.block_pattern
|
33
33
|
|
34
34
|
def on_block(node)
|
35
35
|
return unless example_group_with_body?(node)
|
@@ -19,7 +19,7 @@ module RuboCop
|
|
19
19
|
'`%<called>s` with a single argument.'.freeze
|
20
20
|
|
21
21
|
def_node_matcher :message_chain, <<-PATTERN
|
22
|
-
(send _
|
22
|
+
(send _ {:receive_message_chain :stub_chain} $_)
|
23
23
|
PATTERN
|
24
24
|
|
25
25
|
def_node_matcher :single_key_hash?, '(hash pair)'
|
@@ -57,6 +57,7 @@ require_relative 'rspec/not_to_not'
|
|
57
57
|
require_relative 'rspec/overwriting_setup'
|
58
58
|
require_relative 'rspec/pending'
|
59
59
|
require_relative 'rspec/predicate_matcher'
|
60
|
+
require_relative 'rspec/receive_counts'
|
60
61
|
require_relative 'rspec/repeated_description'
|
61
62
|
require_relative 'rspec/repeated_example'
|
62
63
|
require_relative 'rspec/return_from_stub'
|
@@ -14,14 +14,12 @@ module RuboCop
|
|
14
14
|
ExampleGroups::ALL + SharedGroups::ALL + Includes::ALL
|
15
15
|
).block_pattern
|
16
16
|
|
17
|
-
# @!method hook(node)
|
17
|
+
# @!method hook?(node)
|
18
18
|
#
|
19
19
|
# Detect if node is `before`, `after`, `around`
|
20
|
-
def_node_matcher :hook
|
21
|
-
(block {$(send nil? #{Hooks::ALL.node_pattern_union} ...)} ...)
|
22
|
-
PATTERN
|
20
|
+
def_node_matcher :hook?, Hooks::ALL.block_pattern
|
23
21
|
|
24
|
-
def_node_matcher :subject
|
22
|
+
def_node_matcher :subject?, Subject::ALL.block_pattern
|
25
23
|
|
26
24
|
def subjects
|
27
25
|
subjects_in_scope(node)
|
@@ -46,7 +44,7 @@ module RuboCop
|
|
46
44
|
def find_subjects(node)
|
47
45
|
return [] if scope_change?(node)
|
48
46
|
|
49
|
-
if subject(node)
|
47
|
+
if subject?(node)
|
50
48
|
[node]
|
51
49
|
else
|
52
50
|
subjects_in_scope(node)
|
@@ -62,7 +60,7 @@ module RuboCop
|
|
62
60
|
def find_hooks(node)
|
63
61
|
return [] if scope_change?(node) || example?(node)
|
64
62
|
|
65
|
-
if hook(node)
|
63
|
+
if hook?(node)
|
66
64
|
[node]
|
67
65
|
else
|
68
66
|
hooks_in_scope(node)
|
@@ -27,7 +27,7 @@ module RuboCop
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def send_pattern
|
30
|
-
"(send
|
30
|
+
"(send {(const nil? :RSpec) nil?} #{node_pattern_union} ...)"
|
31
31
|
end
|
32
32
|
|
33
33
|
def node_pattern_union
|
@@ -43,10 +43,6 @@ module RuboCop
|
|
43
43
|
attr_reader :selectors
|
44
44
|
end
|
45
45
|
|
46
|
-
module Matchers
|
47
|
-
MESSAGE_CHAIN = SelectorSet.new(%i[receive_message_chain stub_chain])
|
48
|
-
end
|
49
|
-
|
50
46
|
module ExampleGroups
|
51
47
|
GROUPS = SelectorSet.new(%i[describe context feature example_group])
|
52
48
|
SKIPPED = SelectorSet.new(%i[xdescribe xcontext xfeature])
|
data/rubocop-rspec.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
|
|
8
8
|
Code style checking for RSpec files.
|
9
9
|
A plugin for the RuboCop code style enforcing & linting tool.
|
10
10
|
DESCRIPTION
|
11
|
-
spec.homepage = 'https://github.com/rubocop-
|
11
|
+
spec.homepage = 'https://github.com/rubocop-hq/rubocop-rspec'
|
12
12
|
spec.authors = ['John Backus', 'Ian MacLeod', 'Nils Gemeinhardt']
|
13
13
|
spec.email = [
|
14
14
|
'johncbackus@gmail.com',
|
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
|
|
33
33
|
spec.extra_rdoc_files = ['MIT-LICENSE.md', 'README.md']
|
34
34
|
|
35
35
|
spec.metadata = {
|
36
|
-
'changelog_uri' => 'https://github.com/rubocop-
|
36
|
+
'changelog_uri' => 'https://github.com/rubocop-hq/rubocop-rspec/blob/master/CHANGELOG.md',
|
37
37
|
'documentation_uri' => 'https://rubocop-rspec.readthedocs.io/'
|
38
38
|
}
|
39
39
|
|
@@ -76,6 +76,15 @@ RSpec.describe RuboCop::Cop::RSpec::Capybara::FeatureMethods, :config do
|
|
76
76
|
RUBY
|
77
77
|
end
|
78
78
|
|
79
|
+
it 'allows includes before the spec' do
|
80
|
+
expect_offense(<<-RUBY)
|
81
|
+
require 'rails_helper'
|
82
|
+
|
83
|
+
RSpec.feature 'Foo' do; end
|
84
|
+
^^^^^^^ Use `describe` instead of `feature`.
|
85
|
+
RUBY
|
86
|
+
end
|
87
|
+
|
79
88
|
context 'with configured `EnabledMethods`' do
|
80
89
|
let(:cop_config) { { 'EnabledMethods' => %w[feature] } }
|
81
90
|
|
@@ -49,6 +49,20 @@ RSpec.describe RuboCop::Cop::RSpec::EmptyExampleGroup, :config do
|
|
49
49
|
RUBY
|
50
50
|
end
|
51
51
|
|
52
|
+
it 'does not flag methods matching example group names' do
|
53
|
+
expect_no_offenses(<<-RUBY)
|
54
|
+
describe Foo do
|
55
|
+
it 'yields a block when given' do
|
56
|
+
value = nil
|
57
|
+
|
58
|
+
helper.feature('whatevs') { value = 5 }
|
59
|
+
|
60
|
+
expect(value).to be 5
|
61
|
+
end
|
62
|
+
end
|
63
|
+
RUBY
|
64
|
+
end
|
65
|
+
|
52
66
|
it 'does not recognize custom include methods by default' do
|
53
67
|
expect_offense(<<-RUBY)
|
54
68
|
describe Foo do
|
@@ -38,6 +38,30 @@ RSpec.describe RuboCop::Cop::RSpec::EmptyLineAfterFinalLet do
|
|
38
38
|
RUBY
|
39
39
|
end
|
40
40
|
|
41
|
+
it 'allows comment followed by an empty line after let' do
|
42
|
+
expect_no_offenses(<<-RUBY)
|
43
|
+
RSpec.describe User do
|
44
|
+
let(:a) { a }
|
45
|
+
let(:b) { b }
|
46
|
+
# end of setup
|
47
|
+
|
48
|
+
it { expect(a).to eq(b) }
|
49
|
+
end
|
50
|
+
RUBY
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'flags missing empty line after the comment that comes after last let' do
|
54
|
+
expect_offense(<<-RUBY)
|
55
|
+
RSpec.describe User do
|
56
|
+
let(:a) { a }
|
57
|
+
let(:b) { b }
|
58
|
+
# end of setup
|
59
|
+
^^^^^^^^^^^^^^ Add an empty line after the last `let` block.
|
60
|
+
it { expect(a).to eq(b) }
|
61
|
+
end
|
62
|
+
RUBY
|
63
|
+
end
|
64
|
+
|
41
65
|
it 'ignores empty lines between the lets' do
|
42
66
|
expect_offense(<<-RUBY)
|
43
67
|
RSpec.describe User do
|
@@ -136,6 +160,31 @@ RSpec.describe RuboCop::Cop::RSpec::EmptyLineAfterFinalLet do
|
|
136
160
|
end
|
137
161
|
RUBY
|
138
162
|
|
163
|
+
include_examples 'autocorrect',
|
164
|
+
bad_example,
|
165
|
+
good_example
|
166
|
+
|
167
|
+
bad_example = <<-RUBY
|
168
|
+
RSpec.describe User do
|
169
|
+
let(:params) { foo }
|
170
|
+
# a multiline comment marking
|
171
|
+
# the end of setup
|
172
|
+
it 'has a new line' do
|
173
|
+
end
|
174
|
+
end
|
175
|
+
RUBY
|
176
|
+
|
177
|
+
good_example = <<-RUBY
|
178
|
+
RSpec.describe User do
|
179
|
+
let(:params) { foo }
|
180
|
+
# a multiline comment marking
|
181
|
+
# the end of setup
|
182
|
+
|
183
|
+
it 'has a new line' do
|
184
|
+
end
|
185
|
+
end
|
186
|
+
RUBY
|
187
|
+
|
139
188
|
include_examples 'autocorrect',
|
140
189
|
bad_example,
|
141
190
|
good_example
|
@@ -13,6 +13,16 @@ RSpec.describe RuboCop::Cop::RSpec::EmptyLineAfterSubject do
|
|
13
13
|
RUBY
|
14
14
|
end
|
15
15
|
|
16
|
+
it 'checks for empty line after subject!' do
|
17
|
+
expect_offense(<<-RUBY)
|
18
|
+
RSpec.describe User do
|
19
|
+
subject! { described_class.new }
|
20
|
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Add empty line after `subject`.
|
21
|
+
let(:params) { foo }
|
22
|
+
end
|
23
|
+
RUBY
|
24
|
+
end
|
25
|
+
|
16
26
|
it 'approves empty line after subject' do
|
17
27
|
expect_no_offenses(<<-RUBY)
|
18
28
|
RSpec.describe User do
|
@@ -23,6 +33,16 @@ RSpec.describe RuboCop::Cop::RSpec::EmptyLineAfterSubject do
|
|
23
33
|
RUBY
|
24
34
|
end
|
25
35
|
|
36
|
+
it 'approves empty line after subject!' do
|
37
|
+
expect_no_offenses(<<-RUBY)
|
38
|
+
RSpec.describe User do
|
39
|
+
subject! { described_class.new }
|
40
|
+
|
41
|
+
let(:params) { foo }
|
42
|
+
end
|
43
|
+
RUBY
|
44
|
+
end
|
45
|
+
|
26
46
|
it 'handles subjects in tests' do
|
27
47
|
expect_no_offenses(<<-RUBY)
|
28
48
|
RSpec.describe User do
|
@@ -48,7 +68,7 @@ RSpec.describe RuboCop::Cop::RSpec::EmptyLineAfterSubject do
|
|
48
68
|
RUBY
|
49
69
|
end
|
50
70
|
|
51
|
-
it 'handles
|
71
|
+
it 'handles subject being the latest node' do
|
52
72
|
expect_no_offenses(<<-RUBY)
|
53
73
|
RSpec.describe User do
|
54
74
|
subject { described_user }
|
@@ -22,6 +22,17 @@ RSpec.describe RuboCop::Cop::RSpec::OverwritingSetup do
|
|
22
22
|
RUBY
|
23
23
|
end
|
24
24
|
|
25
|
+
it 'works with `subject!` and `let!`' do
|
26
|
+
expect_offense(<<-RUBY)
|
27
|
+
RSpec.describe User do
|
28
|
+
subject!(:a) { a }
|
29
|
+
|
30
|
+
let!(:a) { b }
|
31
|
+
^^^^^^^^^^^^^^ `a` is already defined.
|
32
|
+
end
|
33
|
+
RUBY
|
34
|
+
end
|
35
|
+
|
25
36
|
it 'finds `let!` overwriting `let`' do
|
26
37
|
expect_offense(<<-RUBY)
|
27
38
|
RSpec.describe User do
|
@@ -44,6 +55,17 @@ RSpec.describe RuboCop::Cop::RSpec::OverwritingSetup do
|
|
44
55
|
RUBY
|
45
56
|
end
|
46
57
|
|
58
|
+
it 'handles unnamed subjects' do
|
59
|
+
expect_offense(<<-RUBY)
|
60
|
+
RSpec.describe User do
|
61
|
+
subject { a }
|
62
|
+
|
63
|
+
let(:subject) { b }
|
64
|
+
^^^^^^^^^^^^^^^^^^^ `subject` is already defined.
|
65
|
+
end
|
66
|
+
RUBY
|
67
|
+
end
|
68
|
+
|
47
69
|
it 'does not encounter an error when handling an empty describe' do
|
48
70
|
expect { inspect_source('RSpec.describe(User) do end', 'a_spec.rb') }
|
49
71
|
.not_to raise_error
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe RuboCop::Cop::RSpec::ReceiveCounts do
|
4
|
+
subject(:cop) { described_class.new }
|
5
|
+
|
6
|
+
it 'flags usage of `exactly(1).times`' do
|
7
|
+
expect_offense(<<-RUBY)
|
8
|
+
expect(foo).to receive(:bar).exactly(1).times
|
9
|
+
^^^^^^^^^^^^^^^^^ Use `.once` instead of `.exactly(1).times`.
|
10
|
+
RUBY
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'flags usage of `exactly(2).times`' do
|
14
|
+
expect_offense(<<-RUBY)
|
15
|
+
expect(foo).to receive(:bar).exactly(2).times
|
16
|
+
^^^^^^^^^^^^^^^^^ Use `.twice` instead of `.exactly(2).times`.
|
17
|
+
RUBY
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'allows `exactly(3).times`' do
|
21
|
+
expect_no_offenses(<<-RUBY)
|
22
|
+
expect(foo).to receive(:bar).exactly(3).times
|
23
|
+
RUBY
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'allows `exactly(n).times`' do
|
27
|
+
expect_no_offenses(<<-RUBY)
|
28
|
+
expect(foo).to receive(:bar).exactly(n).times
|
29
|
+
RUBY
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'flags usage of `exactly(1).times` after `with`' do
|
33
|
+
expect_offense(<<-RUBY)
|
34
|
+
expect(foo).to receive(:bar).with(baz).exactly(1).times
|
35
|
+
^^^^^^^^^^^^^^^^^ Use `.once` instead of `.exactly(1).times`.
|
36
|
+
RUBY
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'flags usage of `exactly(1).times` with return value' do
|
40
|
+
expect_offense(<<-RUBY)
|
41
|
+
expect(foo).to receive(:bar).exactly(1).times.and_return(true)
|
42
|
+
^^^^^^^^^^^^^^^^^ Use `.once` instead of `.exactly(1).times`.
|
43
|
+
RUBY
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'flags usage of `exactly(1).times` with a block' do
|
47
|
+
expect_offense(<<-RUBY)
|
48
|
+
expect(foo).to receive(:bar).exactly(1).times { true }
|
49
|
+
^^^^^^^^^^^^^^^^^ Use `.once` instead of `.exactly(1).times`.
|
50
|
+
RUBY
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'flags usage of `at_least(1).times`' do
|
54
|
+
expect_offense(<<-RUBY)
|
55
|
+
expect(foo).to receive(:bar).at_least(1).times
|
56
|
+
^^^^^^^^^^^^^^^^^^ Use `.at_least(:once)` instead of `.at_least(1).times`.
|
57
|
+
RUBY
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'flags usage of `at_least(2).times`' do
|
61
|
+
expect_offense(<<-RUBY)
|
62
|
+
expect(foo).to receive(:bar).at_least(2).times
|
63
|
+
^^^^^^^^^^^^^^^^^^ Use `.at_least(:twice)` instead of `.at_least(2).times`.
|
64
|
+
RUBY
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'flags usage of `at_most(1).times`' do
|
68
|
+
expect_offense(<<-RUBY)
|
69
|
+
expect(foo).to receive(:bar).at_most(1).times
|
70
|
+
^^^^^^^^^^^^^^^^^ Use `.at_most(:once)` instead of `.at_most(1).times`.
|
71
|
+
RUBY
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'flags usage of `at_most(2).times`' do
|
75
|
+
expect_offense(<<-RUBY)
|
76
|
+
expect(foo).to receive(:bar).at_most(2).times
|
77
|
+
^^^^^^^^^^^^^^^^^ Use `.at_most(:twice)` instead of `.at_most(2).times`.
|
78
|
+
RUBY
|
79
|
+
end
|
80
|
+
|
81
|
+
include_examples 'autocorrect',
|
82
|
+
'expect(foo).to receive(:bar).exactly(1).times { true }',
|
83
|
+
'expect(foo).to receive(:bar).once { true }'
|
84
|
+
|
85
|
+
include_examples 'autocorrect',
|
86
|
+
'expect(foo).to receive(:bar).at_least(2).times { true }',
|
87
|
+
'expect(foo).to receive(:bar).at_least(:twice) { true }'
|
88
|
+
end
|
@@ -35,14 +35,16 @@ RSpec.describe RuboCop::RSpec::Language::SelectorSet do
|
|
35
35
|
|
36
36
|
describe '#send_pattern' do
|
37
37
|
it 'builds a send matching pattern' do
|
38
|
-
expect(selector_set.send_pattern).to eql(
|
38
|
+
expect(selector_set.send_pattern).to eql(
|
39
|
+
'(send {(const nil? :RSpec) nil?} {:foo :bar} ...)'
|
40
|
+
)
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
42
44
|
describe '#block_pattern' do
|
43
45
|
it 'builds a block matching pattern' do
|
44
46
|
expect(selector_set.block_pattern).to eql(
|
45
|
-
'(block (send
|
47
|
+
'(block (send {(const nil? :RSpec) nil?} {:foo :bar} ...) ...)'
|
46
48
|
)
|
47
49
|
end
|
48
50
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop-rspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.26.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Backus
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2018-
|
13
|
+
date: 2018-06-06 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rubocop
|
@@ -169,6 +169,7 @@ files:
|
|
169
169
|
- lib/rubocop/cop/rspec/pending.rb
|
170
170
|
- lib/rubocop/cop/rspec/predicate_matcher.rb
|
171
171
|
- lib/rubocop/cop/rspec/rails/http_status.rb
|
172
|
+
- lib/rubocop/cop/rspec/receive_counts.rb
|
172
173
|
- lib/rubocop/cop/rspec/repeated_description.rb
|
173
174
|
- lib/rubocop/cop/rspec/repeated_example.rb
|
174
175
|
- lib/rubocop/cop/rspec/return_from_stub.rb
|
@@ -256,6 +257,7 @@ files:
|
|
256
257
|
- spec/rubocop/cop/rspec/pending_spec.rb
|
257
258
|
- spec/rubocop/cop/rspec/predicate_matcher_spec.rb
|
258
259
|
- spec/rubocop/cop/rspec/rails/http_status_spec.rb
|
260
|
+
- spec/rubocop/cop/rspec/receive_counts_spec.rb
|
259
261
|
- spec/rubocop/cop/rspec/repeated_description_spec.rb
|
260
262
|
- spec/rubocop/cop/rspec/repeated_example_spec.rb
|
261
263
|
- spec/rubocop/cop/rspec/return_from_stub_spec.rb
|
@@ -279,11 +281,11 @@ files:
|
|
279
281
|
- spec/shared/detects_style_behavior.rb
|
280
282
|
- spec/spec_helper.rb
|
281
283
|
- spec/support/expect_offense.rb
|
282
|
-
homepage: https://github.com/rubocop-
|
284
|
+
homepage: https://github.com/rubocop-hq/rubocop-rspec
|
283
285
|
licenses:
|
284
286
|
- MIT
|
285
287
|
metadata:
|
286
|
-
changelog_uri: https://github.com/rubocop-
|
288
|
+
changelog_uri: https://github.com/rubocop-hq/rubocop-rspec/blob/master/CHANGELOG.md
|
287
289
|
documentation_uri: https://rubocop-rspec.readthedocs.io/
|
288
290
|
post_install_message:
|
289
291
|
rdoc_options: []
|
@@ -362,6 +364,7 @@ test_files:
|
|
362
364
|
- spec/rubocop/cop/rspec/pending_spec.rb
|
363
365
|
- spec/rubocop/cop/rspec/predicate_matcher_spec.rb
|
364
366
|
- spec/rubocop/cop/rspec/rails/http_status_spec.rb
|
367
|
+
- spec/rubocop/cop/rspec/receive_counts_spec.rb
|
365
368
|
- spec/rubocop/cop/rspec/repeated_description_spec.rb
|
366
369
|
- spec/rubocop/cop/rspec/repeated_example_spec.rb
|
367
370
|
- spec/rubocop/cop/rspec/return_from_stub_spec.rb
|