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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +43 -6
  4. data/config/default.yml +5 -0
  5. data/lib/rubocop/cop/rspec/align_left_let_brace.rb +2 -0
  6. data/lib/rubocop/cop/rspec/align_right_let_brace.rb +2 -0
  7. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +14 -3
  8. data/lib/rubocop/cop/rspec/describe_class.rb +1 -3
  9. data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +25 -8
  10. data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +1 -1
  11. data/lib/rubocop/cop/rspec/instance_spy.rb +1 -3
  12. data/lib/rubocop/cop/rspec/leading_subject.rb +1 -1
  13. data/lib/rubocop/cop/rspec/let_before_examples.rb +2 -1
  14. data/lib/rubocop/cop/rspec/message_chain.rb +3 -1
  15. data/lib/rubocop/cop/rspec/named_subject.rb +1 -11
  16. data/lib/rubocop/cop/rspec/overwriting_setup.rb +12 -6
  17. data/lib/rubocop/cop/rspec/pending.rb +0 -3
  18. data/lib/rubocop/cop/rspec/receive_counts.rb +86 -0
  19. data/lib/rubocop/cop/rspec/scattered_let.rb +1 -1
  20. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +1 -1
  21. data/lib/rubocop/cop/rspec_cops.rb +1 -0
  22. data/lib/rubocop/rspec/align_let_brace.rb +1 -1
  23. data/lib/rubocop/rspec/example_group.rb +5 -7
  24. data/lib/rubocop/rspec/language.rb +1 -5
  25. data/lib/rubocop/rspec/version.rb +1 -1
  26. data/rubocop-rspec.gemspec +2 -2
  27. data/spec/rubocop/cop/rspec/align_left_let_brace_spec.rb +4 -0
  28. data/spec/rubocop/cop/rspec/align_right_let_brace_spec.rb +4 -0
  29. data/spec/rubocop/cop/rspec/capybara/feature_methods_spec.rb +9 -0
  30. data/spec/rubocop/cop/rspec/empty_example_group_spec.rb +14 -0
  31. data/spec/rubocop/cop/rspec/empty_line_after_final_let_spec.rb +49 -0
  32. data/spec/rubocop/cop/rspec/empty_line_after_subject_spec.rb +21 -1
  33. data/spec/rubocop/cop/rspec/overwriting_setup_spec.rb +22 -0
  34. data/spec/rubocop/cop/rspec/receive_counts_spec.rb +88 -0
  35. data/spec/rubocop/rspec/language/selector_set_spec.rb +4 -2
  36. metadata +7 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3aa1fc65d7a5b5430ef4c5a88fa0daa56e09d330dd243225f3ef9b6332b076b3
4
- data.tar.gz: 2e56f9dfd5b0b4f11ba040b032bd75ea366a10f15ac7255144971ddfd56027f3
3
+ metadata.gz: 25c78adb48d8a15bb6229d1d8140abcd3f2e455c846288276ad01cedba024b4a
4
+ data.tar.gz: 0e2fbf154570fca1aaf0a32cf7866902424bab10da2854038f5674c8664a2d11
5
5
  SHA512:
6
- metadata.gz: a6066bc29073c219f495824ddce3c3b7d2237d1d06c28605ce536c46b2ec11d0240f05438d7ff8d4a03fde39d484816eb479122617cf411dcf91caba0b2d2cad
7
- data.tar.gz: f1d974b0e62efee91acc48eef293fee4237ebb7ec3eb25eb210808e10432be9a010867818410e99b9986e518d24d237d77709c8acea41a35b3dab225412168d8
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-rspec/rubocop-rspec.svg?style=svg)](https://circleci.com/gh/rubocop-rspec/rubocop-rspec)
6
- [![Test Coverage](https://api.codeclimate.com/v1/badges/f6254deb61671e357f30/test_coverage)](https://codeclimate.com/github/rubocop-rspec/rubocop-rspec/test_coverage)
7
- [![Maintainability](https://api.codeclimate.com/v1/badges/f6254deb61671e357f30/maintainability)](https://codeclimate.com/github/rubocop-rspec/rubocop-rspec/maintainability)
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.
@@ -25,6 +25,8 @@ module RuboCop
25
25
  end
26
26
 
27
27
  def investigate(_processed_source)
28
+ return if processed_source.blank?
29
+
28
30
  token_aligner.offending_tokens.each do |let|
29
31
  add_offense(let, location: :begin)
30
32
  end
@@ -25,6 +25,8 @@ module RuboCop
25
25
  end
26
26
 
27
27
  def investigate(_processed_source)
28
+ return if processed_source.blank?
29
+
28
30
  token_aligner.offending_tokens.each do |let|
29
31
  add_offense(let, location: :end)
30
32
  end
@@ -66,7 +66,7 @@ module RuboCop
66
66
  PATTERN
67
67
 
68
68
  def on_block(node)
69
- return unless spec?(root_node)
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 root_node
91
- processed_source.ast
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?, <<-PATTERN
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?, '(block $(send nil? {:let :let!} ...) args ...)'
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: :expression)
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
- loc = last_node_loc(node)
39
- ->(corrector) { corrector.insert_after(loc.end, "\n") }
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
- next_line = processed_source[loc.line]
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?, '(block $(send nil? :subject ...) args ...)'
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
- EXAMPLES = Examples::ALL.node_pattern_union.freeze
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?, '(block $(send nil? :subject ...) args ...)'
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?, '(block (send nil? {:let :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, Matchers::MESSAGE_CHAIN.send_pattern
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
- def_node_matcher :unnamed_subject, '$(send nil? :subject)'
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?, <<-PATTERN
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) do |name|
49
- yield child, name unless setup_expressions.add?(name)
50
- end
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?, '(block (send nil? {:let :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 _ #{Matchers::MESSAGE_CHAIN.node_pattern_union} $_)
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'
@@ -6,7 +6,7 @@ module RuboCop
6
6
  class AlignLetBrace
7
7
  extend NodePattern::Macros
8
8
 
9
- def_node_matcher :let?, Language::Helpers::ALL.block_pattern.freeze
9
+ def_node_matcher :let?, Language::Helpers::ALL.block_pattern
10
10
 
11
11
  def initialize(root, token)
12
12
  @root = root
@@ -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, <<-PATTERN
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, Subject::ALL.block_pattern
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 _ #{node_pattern_union} ...)"
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])
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module RSpec
5
5
  # Version information for the RSpec RuboCop plugin.
6
6
  module Version
7
- STRING = '1.25.1'.freeze
7
+ STRING = '1.26.0'.freeze
8
8
  end
9
9
  end
10
10
  end
@@ -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-rspec/rubocop-rspec'
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-rspec/rubocop-rspec/blob/master/CHANGELOG.md',
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
 
@@ -24,6 +24,10 @@ RSpec.describe RuboCop::Cop::RSpec::AlignLeftLetBrace do
24
24
  RUBY
25
25
  end
26
26
 
27
+ it 'works with empty file' do
28
+ expect_no_offenses('')
29
+ end
30
+
27
31
  offensive_source = <<-RUBY
28
32
  let(:foo) { bar }
29
33
  let(:hi) { baz }
@@ -24,6 +24,10 @@ RSpec.describe RuboCop::Cop::RSpec::AlignRightLetBrace do
24
24
  RUBY
25
25
  end
26
26
 
27
+ it 'works with empty file' do
28
+ expect_no_offenses('')
29
+ end
30
+
27
31
  offensive_source = <<-RUBY
28
32
  let(:foo) { a }
29
33
  let(:hi) { ab }
@@ -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 let being the latest node' do
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('(send _ {:foo :bar} ...)')
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 _ {:foo :bar} ...) ...)'
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.25.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-04-10 00:00:00.000000000 Z
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-rspec/rubocop-rspec
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-rspec/rubocop-rspec/blob/master/CHANGELOG.md
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