rubocop-rspec 1.25.1 → 1.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
[](https://gitter.im/rubocop-rspec/Lobby)
|
4
4
|
[](https://rubygems.org/gems/rubocop-rspec)
|
5
|
-
[](https://circleci.com/gh/rubocop-hq/rubocop-rspec)
|
6
|
+
[](https://codeclimate.com/github/rubocop-hq/rubocop-rspec/test_coverage)
|
7
|
+
[](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
|